接上一篇,上次学习到了feign到底是如何构造request和发送请求的,最后的时候想看一下异常情况的时候到底是怎么调用fallback的,但是一直没进断点,今天又重新试了一个,原来是会进入断点的。
继续学习。在HardCodeTarget中创建request的时候,只会设置url,所以会有一个问题,多服务间调用的时候,一些上下文信息怎么传递,比如用户信息。之前我们是把用户信息存储到zookeeper中,每次服务调用都会在request header中带有一个唯一token,根据token去zookeeper中获取用户信息。如果springCloud服务间调用不能传递header的话,这种情况该怎么处理?跟了整个构造request的源码和请求的源码,发现确实是只构造了url和基本的静态header(利用spring的@RequestMapping的header属性),例如charset等。这里做一个遗留问题,后续学习完完整的springCloud之后再看看有没有解决办法。
看一个构造request的代码:
/* no authentication or other special activity. just insert the url. */
@Override
public Request apply(RequestTemplate input) {
if (input.url().indexOf("http") != 0) {
input.insert(0, url());
}
return input.request();
}
/* roughly analogous to {@code javax.ws.rs.client.Target.request()}. */
public Request request() {
Map<String, Collection<String>> safeCopy = new LinkedHashMap<String, Collection<String>>();
safeCopy.putAll(headers);
return Request.create(
method, url + queryLine(),
Collections.unmodifiableMap(safeCopy),
body, charset
);
}
可以看到,确实是值构造了url。
接下来就是真正的去发送请求了,去发送请求返回response对象的是Client对象,该对象的一个默认实现内部类Default
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection).toBuilder().request(request).build();
}
当需要使用ribbon做负载均衡的时候,通过feign的配置类,FeignRibbonClientAutoConfiguration可以看到,注入的是LoadBalanceFeignClient。
看一下LoadBalanceFeignClient的execute方法:
public Response execute(Request request, Request.Options options) throws IOException {
try {
URI asUri = URI.create(request.url());
String clientName = asUri.getHost();
URI uriWithoutHost = cleanUrl(request.url(), clientName);
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
this.delegate, request, uriWithoutHost);
IClientConfig requestConfig = getClientConfig(options, clientName);
return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
requestConfig).toResponse();
}
catch (ClientException e) {
IOException io = findIOException(e);
if (io != null) {
throw io;
}
throw new RuntimeException(e);
}
}
我们可以看到调用的还是负载均衡的请求
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
RequestSpecificRetryHandler handler = this.getRequestSpecificRetryHandler(request, requestConfig);
LoadBalancerCommand command = LoadBalancerCommand.builder().withLoadBalancerContext(this).withRetryHandler(handler).withLoadBalancerURI(request.getUri()).build();
try {
return (IResponse)command.submit(new ServerOperation<T>() {
public Observable<T> call(Server server) {
URI finalUri = AbstractLoadBalancerAwareClient.this.reconstructURIWithServer(server, request.getUri());
ClientRequest requestForServer = request.replaceUri(finalUri);
try {
return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
} catch (Exception var5) {
return Observable.error(var5);
}
}
}).toBlocking().single();
} catch (Exception var7) {
Throwable t = var7.getCause();
if (t instanceof ClientException) {
throw (ClientException)t;
} else {
throw new ClientException(var7);
}
}
}
看到服务是在submit方法中调用的,
Observable<T> o = (this.server == null ? this.selectServer() : Observable.just(this.server)).concatMap(new Func1<Server, Observable<T>>() {
public Observable<T> call(Server server) {
context.setServer(server);
这里只截了一部分代码,可以看到最主要的就是这个selectServer()方法了,这方法就是进行负载均衡的,它会交给ribbon去处理,后面会学习到。
知道了feign是如何把虚拟的ip转换成实际的ip之后,我们继续回到之前的请求中,请求完之后,获取response对象。
由于我这里要看一下fallback,所以故意让请求的远程服务抛出一个异常,看到在上一篇讲到的SynchronousMethodHandler中的executeAndDecode方法中,处理response的逻辑为:
if (Response.class == metadata.returnType()) {
if (response.body() == null) {
return response;
}
if (response.body().length() == null ||
response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
shouldClose = false;
return response;
}
// Ensure the response body is disconnected
byte[] bodyData = Util.toByteArray(response.body().asInputStream());
return response.toBuilder().body(bodyData).build();
}
if (response.status() >= 200 && response.status() < 300) {
if (void.class == metadata.returnType()) {
return null;
} else {
return decode(response);
}
} else if (decode404 && response.status() == 404) {
return decode(response);
} else {
throw errorDecoder.decode(metadata.configKey(), response);
}
我们可以看到有各种情况的执行逻辑,请求正确的,404NOT FOUND的,我们这里执行的是errorDecoder.decode,它的作用就是根据response构造一个FeignException异常,抛出来。继续执行错误的处理。本以为后续的处理会有进入fallback的逻辑,但是通过调试,发现在这之前就已经执行了fallback。再返回去找一下。。。。好像是有一个类似于观察者的模式和一个多线程监听队列状态的地方,具体还没有看明白,明天继续学习这一块,彻底弄明白feign的原理。