图解+源码讲解 Feign 请求的流程


骐骥一跃,不能十步;驽马十驾,功在不舍 -- 荀子


相关文章
​图解+源码讲解 Feign 如何将客户端注入到容器中图解+源码讲解动态代理获取 FeignClient 代理对象图解+源码讲解代理对象 ReflectiveFeign 分析图解+源码讲解 Feign 如何选取指定服务

回顾上文

    执行的是 FeignLoadBalancer 父类 AbstractLoadBalancerAwareClient 的 executeWithLoadBalancer的方法,这个方法里面创建了一个 LoadBalancerCommand 指令,进行任务提交操作,后续的请求应该就是command.submit()方法里面了

public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig)
// 创建负载均衡指令
LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
// 提交一个任务操作
return command.submit(
new ServerOperation<T>() {
@Override
public Observable<T> call(Server server) {
// 解析地址
URI finalUri = reconstructURIWithServer(server, request.getUri());
S requestForServer = (S) request.replaceUri(finalUri);
// 执行请求
return Observable.just(
AbstractLoadBalancerAwareClient.
this.execute(requestForServer, requestConfig));

}
}).toBlocking().single();
}

command.submit()

    我们慢慢来看这个 submit 方法,这个方法好长我们分解来看,就看看里面的核心方法

public Observable<T> submit(final ServerOperation<T> operation) {
// Use the load balancer
Observable<T> o =
// 这里面的 selectServer() 方法是进行服务选择
(server == null ? selectServer() : Observable.just(server))
}

服务选择 selectServer()

private Observable<Server> selectServer() {
return Observable.create(new OnSubscribe<Server>() {
@Override
public void call(Subscriber<? super Server> next) {
// 获取服务 192.168.60.1:9200
Server server =
loadBalancerContext.
getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);
next.onNext(server);
next.onCompleted();
}
});
}

    选择的服务结果,其实这个结果是通过ribbon的负载均衡器中获取的,通过轮询算法
图解+源码讲解 Feign 请求的流程_ide

.concatMap(new Func1<Server, Observable<T>>() {
@Override
// Called for each server being selected
public Observable<T> call(Server server) {
// 设置所要访问的服务
context.setServer(server);
// 省略一些代码
}

    到这里已经设置完我们所要访问的服务了,后面是该发起服务请求了

发起请求

    this.execute(requestForServer, requestConfig),这个代码是进行服务网络请求,FeignLoadBalancerexecute 请求,显示设置连接超时和读取超时参数

@Override
public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
throws IOException {
Request.Options options;
if (configOverride != null) {
RibbonProperties override = RibbonProperties.from(configOverride);
options = new Request.Options(override.connectTimeout(this.connectTimeout),
override.readTimeout(this.readTimeout));
}
else {
options = new Request.Options(this.connectTimeout, this.readTimeout);
}
// 真正的执行请求,并获取响应结果
Response response = request.client().execute(request.toRequest(), options);
return new RibbonResponse(request.getUri(), response);
}

    这个 request.client() 是 Default类,它实现了 Client 接口所以是执行的 Default类的 execute 方法

@Override
public Response execute(Request request, Options options) throws IOException {
// 转换并发送请求
HttpURLConnection connection = convertAndSend(request, options);
// 转换响应结果
return convertResponse(connection, request);
}

转换并发送请求 convertAndSend

    转化并且发送请求的方法,通过 HttpURLConnection 进行方法请求

HttpURLConnection convertAndSend(Request request, Options options) throws IOException {
// sun.net.www.protocol.http.HttpURLConnection:http://192.168.60.1:9200/good/getGoods
final HttpURLConnection connection =
(HttpURLConnection) new URL(request.url()).openConnection();
// 设置连接超时
connection.setConnectTimeout(options.connectTimeoutMillis());
// 设置读取超时
connection.setReadTimeout(options.readTimeoutMillis());
connection.setAllowUserInteraction(false);
connection.setInstanceFollowRedirects(options.isFollowRedirects());
// 设置请求方法
connection.setRequestMethod(request.httpMethod().name());
// 下面也都是进行请求参数的设置
Collection<String> contentEncodingValues = request.headers().get(CONTENT_ENCODING);
boolean gzipEncodedRequest =
contentEncodingValues != null && contentEncodingValues.contains(ENCODING_GZIP);
boolean deflateEncodedRequest =2000
contentEncodingValues != null && contentEncodingValues.contains(ENCODING_DEFLATE);

boolean hasAcceptHeader = false;
Integer contentLength = null;
for (String field : request.headers().keySet()) {
if (field.equalsIgnoreCase("Accept")) {
hasAcceptHeader = true;
}
for (String value : request.headers().get(field)) {
if (field.equals(CONTENT_LENGTH)) {
if (!gzipEncodedRequest && !deflateEncodedRequest) {
contentLength = Integer.valueOf(value);
connection.addRequestProperty(field, value);
}
} else {
connection.addRequestProperty(field, value);
}
}
}
// Some servers choke on the default accept string.
if (!hasAcceptHeader) {
connection.addRequestProperty("Accept", "*/*");
}
......
return connection;
}

转换响应结果 convertResponse

    转换结果,获取响应码和响应信息,进行响应结果封装并返回

Response convertResponse(HttpURLConnection connection, Request request)
int status = connection.getResponseCode();
// 获取响应信息
String reason = connection.getResponseMessage();

if (status < 0) {
throw new IOException(format("Invalid status(%s) executing %s %s", status,
connection.getRequestMethod(), connection.getURL()));
}

Map<String, Collection<String>> headers =
new LinkedHashMap<String, Collection<String>>();
for (Map.Entry<String, List<String>> field : connection.getHeaderFields().entrySet()) {
// response message
if (field.getKey() != null) {
headers.put(field.getKey(), field.getValue());
}
}

Integer length = connection.getContentLength();
if (length == -1) {
length = null;
}
InputStream stream;
if (status >= 400) {
stream = connection.getErrorStream();
} else {
stream = connection.getInputStream();
}
// 构建响应结果
return Response.builder()
.status(status)
.reason(reason)
.headers(headers)
.request(request)
.body(stream, length)
.build();
}

封装响应结果 RibbonResponse

protected RibbonResponse(URI uri, Response response) {
this.uri = uri;
this.response = response;
}

输出结果

图解+源码讲解 Feign 请求的流程_封装_02

小结

  1. 进行指令构建,进行任务提交
  2. 进行服务选择
  3. 通过 HttpURLConnection 进行网络请求
  4. 进行响应结果封装
  5. 结果输出