接上一篇,上次學習到了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的原理。