我们已经知道了feign 接口是通过FeignClientFactoryBean 来生成代理类的,那么一个真正的http 请求是如果进行构建的呢?
我们知道 代理类的最终生成 是在Feign 中的build() 方法:
public <T> T target(Target<T> target) {
return this.build().newInstance(target);
}
public Feign build() {
super.enrich();
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(this.client, this.retryer, this.requestInterceptors, this.responseInterceptor, this.logger, this.logLevel, this.dismiss404, this.closeAfterDecode, this.propagationPolicy, this.forceDecoding);
ReflectiveFeign.ParseHandlersByName handlersByName = new ReflectiveFeign.ParseHandlersByName(this.contract, this.options, this.encoder, this.decoder, this.queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, this.invocationHandlerFactory, this.queryMapEncoder);
}
SynchronousMethodHandler 是对http 请求方法的处理,ReflectiveFeign 会调到其内部的 invoke 方法中,最终又调到 SynchronousMethodHandler 的 invoke 方法中
;
ReflectiveFeign invoke :
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!"equals".equals(method.getName())) {
if ("hashCode".equals(method.getName())) {
return this.hashCode();
} else {
return "toString".equals(method.getName()) ? this.toString() : ((InvocationHandlerFactory.MethodHandler)this.dispatch.get(method)).invoke(args);
}
} else {
try {
Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return this.equals(otherHandler);
} catch (IllegalArgumentException var5) {
return false;
}
}
}
方法本身不是 equals/hashCode/toString 则会进入到SynchronousMethodHandler 的 invoke
方法中;
public Object invoke(Object[] argv) throws Throwable {
// 构建http request 请求的模版, buildTemplateFromArgs 中对要执行的方法的参数 进行解析
RequestTemplate template = this.buildTemplateFromArgs.create(argv);
// request 的配置项获取,包括连接超时和读取超时的设置
Request.Options options = this.findOptions(argv);
// request 重试机制
Retryer retryer = this.retryer.clone();
while(true) {
try {
// 构建请求,完成发送,和对返回数据进行解析
return this.executeAndDecode(template, options);
} catch (RetryableException var9) {
RetryableException e = var9;
try {
// 重试机制判断,达到一定次数 则直接抛出异常
retryer.continueOrPropagate(e);
} catch (RetryableException var8) {
Throwable cause = var8.getCause();
if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
throw cause;
}
throw var8;
}
if (this.logLevel != Level.NONE) {
this.logger.logRetry(this.metadata.configKey(), this.logLevel);
}
}
}
}
this.buildTemplateFromArgs.create(argv) 进入到ReflectiveFeign 对 RequestTemplate 模版的创建:
public RequestTemplate create(Object[] argv) {
// this.metadata.template() 对方法的原数据进行解析后 创建 一个新的 RequestTemplate 并返回
RequestTemplate mutable = RequestTemplate.from(this.metadata.template());
mutable.feignTarget(this.target) ;
if (this.metadata.urlIndex() != null) {
int urlIndex = this.metadata.urlIndex();
Util.checkArgument(argv[urlIndex] != null, "URI parameter %s was null", new Object[]{urlIndex});
mutable.target(String.valueOf(argv[urlIndex]));
}
// 遍历对 参数进行解析 ,
Map<String, Object> varBuilder = new LinkedHashMap();
Iterator var4 = this.metadata.indexToName().entrySet().iterator();
while(true) {
Map.Entry entry;
int i;
Object value;
do {
if (!var4.hasNext()) {
RequestTemplate template = this.resolve(argv, mutable, varBuilder);
Object value;
Map headerMap;
if (this.metadata.queryMapIndex() != null) {
value = argv[this.metadata.queryMapIndex()];
headerMap = this.toQueryMap(value);
// 添加请求参数
template = this.addQueryMapQueryParameters(headerMap, template);
}
if (this.metadata.headerMapIndex() != null) {
value = argv[this.metadata.headerMapIndex()];
headerMap = this.toQueryMap(value);
// 添加请求header 头
template = this.addHeaderMapHeaders(headerMap, template);
}
return template;
}
entry = (Map.Entry)var4.next();
i = (Integer)entry.getKey();
value = argv[(Integer)entry.getKey()];
} while(value == null);
if (this.indexToExpander.containsKey(i)) {
value = this.expandElements((Param.Expander)this.indexToExpander.get(i), value);
}
Iterator var8 = ((Collection)entry.getValue()).iterator();
while(var8.hasNext()) {
String name = (String)var8.next();
varBuilder.put(name, value);
}
}
}
executeAndDecode 方法中对其请求的构建:
Object executeAndDecode(RequestTemplate template, Request.Options options) throws Throwable {
// 构建 request http 请求
Request request = this.targetRequest(template);
if (this.logLevel != Level.NONE) {
// feign log 日志
this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
}
long start = System.nanoTime();
Response response;
try {
// 发送http 请求 并获取到结果
response = this.client.execute(request, options);
response = response.toBuilder().request(request).requestTemplate(template).build();
} catch (IOException var13) {
if (this.logLevel != Level.NONE) {
this.logger.logIOException(this.metadata.configKey(), this.logLevel, var13, this.elapsedTime(start));
}
throw FeignException.errorExecuting(request, var13);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
// 请求结果进行解析
if (this.decoder != null) {
return this.responseInterceptor.aroundDecode(new InvocationContext(this.decoder, this.metadata.returnType(), response));
} else {
CompletableFuture<Object> resultFuture = new CompletableFuture();
this.asyncResponseHandler.handleResponse(resultFuture, this.metadata.configKey(), response, this.metadata.returnType(), elapsedTime);
try {
if (!resultFuture.isDone()) {
throw new IllegalStateException("Response handling not done");
} else {
return resultFuture.join();
}
} catch (CompletionException var12) {
Throwable cause = var12.getCause();
if (cause != null) {
throw cause;
} else {
throw var12;
}
}
}
}
this.targetRequest(template) 请求构建:
Request targetRequest(RequestTemplate template) {
// 拦截器调用
Iterator var2 = this.requestInterceptors.iterator();
while(var2.hasNext()) {
RequestInterceptor interceptor = (RequestInterceptor)var2.next();
interceptor.apply(template);
}
// 构建 Request http 请求
return this.target.apply(template);
}
target.apply(template) 根据 RequestTemplate 组装http 请求:
public Request request() {
if (!this.resolved) {
throw new IllegalStateException("template has not been resolved.");
} else {
return Request.create(this.method, this.url(), this.headers(), this.body, this);
}
}
最终通过 Request 构造方法进行属性填充:
public static Request create(HttpMethod httpMethod, String url, Map<String, Collection<String>> headers, Body body, RequestTemplate requestTemplate) {
return new Request(httpMethod, url, headers, body, requestTemplate);
}
Request(HttpMethod method, String url, Map<String, Collection<String>> headers, Body body, RequestTemplate requestTemplate) {
this.httpMethod = (HttpMethod)Util.checkNotNull(method, "httpMethod of %s", new Object[]{method.name()});
this.url = (String)Util.checkNotNull(url, "url", new Object[0]);
this.headers = (Map)Util.checkNotNull(headers, "headers of %s %s", new Object[]{method, url});
this.body = body;
this.requestTemplate = requestTemplate;
this.protocolVersion = Request.ProtocolVersion.HTTP_1_1;
}
在构建http 的 RequestTemplate 模版后在executeAndDecode 方法中通过 this.client.execute(request, options)
进行请求的发送;
public Response execute(Request request, Request.Options options) throws IOException {
// 创建http 请求并发送
HttpURLConnection connection = this.convertAndSend(request, options);
// 获取http 请求结果并返回
return this.convertResponse(connection, request);
}
this.convertAndSend(request, options) 请求发送:
HttpURLConnection convertAndSend(Request request, Request.Options options) throws IOException {
// 创建 URL 对象
URL url = new URL(request.url());
// 打开连接:
HttpURLConnection connection = this.getConnection(url);
if (connection instanceof HttpsURLConnection) {
// https 处理
HttpsURLConnection sslCon = (HttpsURLConnection)connection;
if (this.sslContextFactory != null) {
sslCon.setSSLSocketFactory(this.sslContextFactory);
}
if (this.hostnameVerifier != null) {
sslCon.setHostnameVerifier(this.hostnameVerifier);
}
}
// 连接和读取超时
connection.setConnectTimeout(options.connectTimeoutMillis());
connection.setReadTimeout(options.readTimeoutMillis());
connection.setAllowUserInteraction(false);
connection.setInstanceFollowRedirects(options.isFollowRedirects());
// 设置请求方法(默认为 GET)
connection.setRequestMethod(request.httpMethod().name());
Collection<String> contentEncodingValues = (Collection)request.headers().get("Content-Encoding");
boolean gzipEncodedRequest = this.isGzip(contentEncodingValues);
boolean deflateEncodedRequest = this.isDeflate(contentEncodingValues);
boolean hasAcceptHeader = false;
Integer contentLength = null;
Iterator var10 = request.headers().keySet().iterator();
while(var10.hasNext()) {
String field = (String)var10.next();
if (field.equalsIgnoreCase("Accept")) {
hasAcceptHeader = true;
}
Iterator var12 = ((Collection)request.headers().get(field)).iterator();
while(var12.hasNext()) {
String value = (String)var12.next();
if (field.equals("Content-Length")) {
if (!gzipEncodedRequest && !deflateEncodedRequest) {
contentLength = Integer.valueOf(value);
connection.addRequestProperty(field, value);
}
} else {
connection.addRequestProperty(field, value);
}
}
}
if (!hasAcceptHeader) {
// 设置请求首部
connection.addRequestProperty("Accept", "*/*");
}
// 对于 POST 或 PUT 请求,向 OutputStream 写入数据
if (request.body() != null) {
if (this.disableRequestBuffering) {
if (contentLength != null) {
connection.setFixedLengthStreamingMode(contentLength);
} else {
connection.setChunkedStreamingMode(8196);
}
}
connection.setDoOutput(true);
OutputStream out = connection.getOutputStream();
if (gzipEncodedRequest) {
out = new GZIPOutputStream((OutputStream)out);
} else if (deflateEncodedRequest) {
out = new DeflaterOutputStream((OutputStream)out);
}
try {
((OutputStream)out).write(request.body());
} finally {
try {
((OutputStream)out).close();
} catch (IOException var19) {
}
}
}
return connection;
}
convertResponse(connection, request)
去获取请求的响应信息;
Response convertResponse(HttpURLConnection connection, Request request) throws IOException {
// 响应代码
int status = connection.getResponseCode();
// 响应消息
String reason = connection.getResponseMessage();
if (status < 0) {
throw new IOException(String.format("Invalid status(%s) executing %s %s", status, connection.getRequestMethod(), connection.getURL()));
} else {
Map<String, Collection<String>> headers = new TreeMap(String.CASE_INSENSITIVE_ORDER);
Iterator var6 = connection.getHeaderFields().entrySet().iterator();
while(var6.hasNext()) {
Map.Entry<String, List<String>> field = (Map.Entry)var6.next();
if (field.getKey() != null) {
headers.put(field.getKey(), field.getValue());
}
}
Integer length = connection.getContentLength();
if (length == -1) {
length = null;
}
// 读取响应数据
Object stream;
if (status >= 400) {
stream = connection.getErrorStream();
} else if (this.isGzip((Collection)headers.get("Content-Encoding"))) {
stream = new GZIPInputStream(connection.getInputStream());
} else if (this.isDeflate((Collection)headers.get("Content-Encoding"))) {
stream = new InflaterInputStream(connection.getInputStream());
} else {
stream = connection.getInputStream();
}
// 返回相应的 Response对象
return Response.builder().status(status).reason(reason).headers(headers).request(request).body((InputStream)stream, length).build();
}
}
启用okHttp :
feign:
okhttp:
enabled: true
public Response execute(feign.Request input, feign.Request.Options options) throws IOException {
// 超时时间配置项
okhttp3.OkHttpClient requestScoped = this.getClient(options);
// 构建http 请求
Request request = toOkHttpRequest(input);
// 获取请求结果并封装 Response 对象返回
okhttp3.Response response = requestScoped.newCall(request).execute();
return toFeignResponse(response, input).toBuilder().request(input).build();
}
this.getClient(options) 配置项:
private okhttp3.OkHttpClient getClient(feign.Request.Options options) {
okhttp3.OkHttpClient requestScoped;
// 如果OkHttpClient 配置的请求连接,读取超时时间以及重定向策略和 我们对客户端配置的相同则直接使用OkHttpClient
// 否则使用客户端的配置
if (this.delegate.connectTimeoutMillis() == options.connectTimeoutMillis() && this.delegate.readTimeoutMillis() == options.readTimeoutMillis() && this.delegate.followRedirects() == options.isFollowRedirects()) {
requestScoped = this.delegate;
} else {
requestScoped = this.delegate.newBuilder().connectTimeout((long)options.connectTimeoutMillis(), TimeUnit.MILLISECONDS).readTimeout((long)options.readTimeoutMillis(), TimeUnit.MILLISECONDS).followRedirects(options.isFollowRedirects()).build();
}
return requestScoped;
}
如果okhttp 和客户端都进行了配置,则以客户端配置项为准;
toOkHttpRequest(input) 构建request 请求:
static Request toOkHttpRequest(feign.Request input) {
Request.Builder requestBuilder = new Request.Builder();
// 构建 url
requestBuilder.url(input.url());
MediaType mediaType = null;
boolean hasAcceptHeader = false;
Iterator var4 = input.headers().keySet().iterator();
while(var4.hasNext()) {
String field = (String)var4.next();
if (field.equalsIgnoreCase("Accept")) {
hasAcceptHeader = true;
}
Iterator var6 = ((Collection)input.headers().get(field)).iterator();
while(var6.hasNext()) {
String value = (String)var6.next();
requestBuilder.addHeader(field, value);
if (field.equalsIgnoreCase("Content-Type")) {
mediaType = MediaType.parse(value);
if (input.charset() != null) {
mediaType.charset(input.charset());
}
}
}
}
if (!hasAcceptHeader) {
requestBuilder.addHeader("Accept", "*/*");
}
// 放入请求参数
byte[] inputBody = input.body();
boolean isMethodWithBody = HttpMethod.POST == input.httpMethod() || HttpMethod.PUT == input.httpMethod() || HttpMethod.PATCH == input.httpMethod();
if (isMethodWithBody) {
requestBuilder.removeHeader("Content-Type");
if (inputBody == null) {
inputBody = new byte[0];
}
}
RequestBody body = inputBody != null ? RequestBody.create(mediaType, inputBody) : null;
// 放入请求方式
requestBuilder.method(input.httpMethod().name(), body);
return requestBuilder.build();
}
okhttp3.Response response = requestScoped.newCall(request).execute();
return toFeignResponse(response, input).toBuilder().request(input).build();
toFeignResponse 对请求结果封装
private static Response toFeignResponse(okhttp3.Response response, feign.Request request) throws IOException {
return Response.builder().protocolVersion((feign.Request.ProtocolVersion)Util.enumForName(feign.Request.ProtocolVersion.class, response.protocol())).status(response.code()).reason(response.message()).request(request).headers(toMap(response.headers())).body(toBody(response.body())).build();
}
feign 请求返回的结果 默认使用 SpringDecoder
进行解析,SpringDecoder 基于 Spring 的 HttpMessageConverter
抽象进行实现,它集成了 Spring 的消息转换功能,允许你将HTTP的响应体转换成Java对象,对于SpringDecoder 的解释可见 四、 扩展 章节 。
Object executeAndDecode(RequestTemplate template, Request.Options options) throws Throwable {
Request request = this.targetRequest(template);
if (this.logLevel != Level.NONE) {
this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
}
long start = System.nanoTime();
Response response;
try {
response = this.client.execute(request, options);
response = response.toBuilder().request(request).requestTemplate(template).build();
} catch (IOException var13) {
if (this.logLevel != Level.NONE) {
this.logger.logIOException(this.metadata.configKey(), this.logLevel, var13, this.elapsedTime(start));
}
throw FeignException.errorExecuting(request, var13);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
if (this.decoder != null) {
// 如果设置了使用自己的 解码器
return this.responseInterceptor.aroundDecode(new InvocationContext(this.decoder, this.metadata.returnType(), response));
} else {
// 使用springmvc SpringDecoder 进行返回内容解析
CompletableFuture<Object> resultFuture = new CompletableFuture();
this.asyncResponseHandler.handleResponse(resultFuture, this.metadata.configKey(), response, this.metadata.returnType(), elapsedTime);
try {
if (!resultFuture.isDone()) {
throw new IllegalStateException("Response handling not done");
} else {
// 返回SpringDecoder 解析后的内容
return resultFuture.join();
}
} catch (CompletionException var12) {
Throwable cause = var12.getCause();
if (cause != null) {
throw cause;
} else {
throw var12;
}
}
}
}
handleResponse 方法对返回结果进行解析:
void handleResponse(CompletableFuture<Object> resultFuture, String configKey, Response response, Type returnType, long elapsedTime) {
boolean shouldClose = true;
try {
// 如果feign 日志级别 不是NONE 则进行日志输出
if (this.logLevel != Level.NONE) {
response = this.logger.logAndRebufferResponse(configKey, this.logLevel, response, elapsedTime);
}
// 返回内容解析
if (Response.class == returnType) {
if (response.body() == null) {
resultFuture.complete(response);
} else if (response.body().length() != null && (long)response.body().length() <= 8192L) {
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
resultFuture.complete(response.toBuilder().body(bodyData).build());
} else {
shouldClose = false;
resultFuture.complete(response);
}
} else {
Object result;
if (response.status() >= 200 && response.status() < 300) {
if (this.isVoidType(returnType)) {
resultFuture.complete((Object)null);
} else {
// 使用 SpringDecoder 进行解析
result = this.decode(response, returnType);
shouldClose = this.closeAfterDecode;
// 放入解析的结果
resultFuture.complete(result);
}
} else if (this.dismiss404 && response.status() == 404 && !this.isVoidType(returnType)) {
result = this.decode(response, returnType);
shouldClose = this.closeAfterDecode;
resultFuture.complete(result);
} else {
resultFuture.completeExceptionally(this.errorDecoder.decode(configKey, response));
}
}
} catch (IOException var13) {
if (this.logLevel != Level.NONE) {
this.logger.logIOException(configKey, this.logLevel, var13, elapsedTime);
}
resultFuture.completeExceptionally(FeignException.errorReading(response.request(), response, var13));
} catch (Exception var14) {
resultFuture.completeExceptionally(var14);
} finally {
if (shouldClose) {
Util.ensureClosed(response.body());
}
}
}
Object decode(Response response, Type type) throws IOException {
return this.responseInterceptor.aroundDecode(new InvocationContext(this.decoder, type, response));
}
在 Spring Cloud OpenFeign 中,SpringDecoder
是一个特定的解码器,用来处理从对Feign客户端发出的请求返回的响应。SpringDecoder
基于 Spring 的 HttpMessageConverter
抽象进行实现,它集成了 Spring 的消息转换功能,允许你将HTTP的响应体转换成Java对象。
Feign
是一个声明式的Web服务客户端,使得编写Web服务客户端变得更加简单。Feign中内置了对响应的解码(即反序列化)的支持。默认情况下,它使用一个名为Decoder
的组件来对返回的响应体进行解码处理。
SpringDecoder
实现了Feign中的Decoder
接口。在与Spring Cloud集成时,Feign可利用Spring MVC框架的强大功能,包括使用HttpMessageConverters
来对响应体进行解码。这意味着你可以使用如Jackson JSON或Gson这样的库来自动将返回的JSON映射到你的对象中。
当使用Spring Cloud OpenFeign时,SpringDecoder
通常会自动配置并使用。你可以自定义HttpMessageConverter
列表,以便在Feign客户端中处理特定的响应类型。
例如,假设你有一个OpenFeign接口来与REST API通信,并且你想要映射返回的JSON到你的Java对象中。Spring Cloud会使用SpringDecoder
来自动处理解码工作:
@FeignClient(name = "myclient", url = "http://my.api.com")
public interface MyClient {
@GetMapping("/some-endpoint")
MyResource getMyResource();
}
在上面的例子中,当你调用getMyResource
方法时,SpringDecoder
会使用合适的HttpMessageConverter
来将/some-endpoint
端点返回的JSON转换成MyResource
对象。如果你有自定义的需求,比如需要使用特殊的序列化配置或提供其他格式的转换支持,可以自定义Spring的HttpMessageConverter
配置,并且它将反映在Feign客户端的处理中。
OpenFeign是一个声明式的HTTP客户端框架,用于简化调用远程服务。底层的HTTP客户端可以由用户选择,常见的有Java原生的HttpURLConnection
(默认)和OkHttpClient
。每个客户端都有自己的优缺点。使用OkHttpClient而非HttpURLConnection作为Feign的客户端通信工具可能有以下几个优点:
1.) 性能提升:
OkHttpClient
支持连接池,减少握手时间,对于频繁的请求可以提高性能。)更丰富的特性:
OkHttpClient
支持各种拦截器,便于添加诸如重试、日志记录、头部处理等额外的功能。)更好的错误处理:
OkHttpClient
提供更详细的错误信息,可能还包括网络情况的统计等。)现代API设计:
OkHttpClient
使用Fluent API,链式调用,易于理解和使用。5.) API一致性和可维护性:
OkHttp
维护活跃,并且遵守语义化版本控制,确保API的一致性和向后兼容性。)更好的社区支持:
OkHttp
拥有活跃的社区支持和定期的更新,可以确保利用最新的技术和修复最近的问题或安全漏洞。)内置功能:
SpringCloud-OpenFeign 通过 SynchronousMethodHandler 类中的invoke 方法,对http 请求进行组装并进行发送,默认使用HttpURLConnection 当通过feign.okhttp.enabled : true 可以使用okhttp 客户端发送请求,最终使用springmvc 的HttpMessageConverter 对请求结果进行解析并返回;