使用Feign客户端调用微服务时,就是通过接口的代理对象调用微服务,逻辑都是在SynchronousMethodHandler#invoke()方法调用。

public Object invoke(Object[] argv) throws Throwable {

RequestTemplate template = buildTemplateFromArgs.create(argv);

Retryer retryer = this.retryer.clone();

while (true) {

try {

return executeAndDecode(template);

} catch (RetryableException e) {

retryer.continueOrPropagate(e);

if (logLevel != Logger.Level.NONE) {

logger.logRetry(metadata.configKey(), logLevel);

}

continue;

}

}

}

首先创建请求模板RequestTemplate,先获取解析过的参数索引和参数名的映射关系,比如0->str,然后根据索引获取真实的参数值,即传给方法的参数值,这样就能获取映射的参数值,比如str->hello,然后在RequestTemplate#expand()方法完成变量的替换,这样就能知道真正的请求路径,然后在请求路径前拼接以http开头的微服务名

public RequestTemplate create(Object[] argv) {

RequestTemplate mutable = new RequestTemplate(metadata.template());

if (metadata.urlIndex() != null) {

int urlIndex = metadata.urlIndex();

checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);

mutable.insert(0, String.valueOf(argv[urlIndex]));

}

Map varBuilder = new LinkedHashMap();

for (Entry> entry : metadata.indexToName().entrySet()) {

int i = entry.getKey();

Object value = argv[entry.getKey()];

if (value != null) { // Null values are skipped.

if (indexToExpander.containsKey(i)) {

value = expandElements(indexToExpander.get(i), value);

}

for (String name : entry.getValue()) {

varBuilder.put(name, value);

}

}

}

RequestTemplate template = resolve(argv, mutable, varBuilder);

if (metadata.queryMapIndex() != null) {

// add query map parameters after initial resolve so that they take

// precedence over any predefined values

template = addQueryMapQueryParameters((Map) argv[metadata.queryMapIndex()], template);

}

if (metadata.headerMapIndex() != null) {

template = addHeaderMapHeaders((Map) argv[metadata.headerMapIndex()], template);

}

return template;

}

然后是请求的执行了,在LoadBalancerFeignClient#getClientConfig()通过SpringClientFactory为调用的微服务创建上下文容器,再从容器中获取IClientConfig(LoadBalancerFeignClient$FeignOptionsClientConfig),再继续获取负载均衡器ILoadBalancer(ZoneAwareLoadBalancer)。

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);

}

}

最后就是请求的执行了,在LoadBalancerCommand#submit()实现,这里会通过selectServer()找到负载均衡器,再使用负载均衡器ILoadBalancer#chooseServer()获取要调用的微服务;然后在LoadBalancerCommand#submit()的参数lamdba表达式通过LoadBalancerContext#reconstructURIWithServer()获取ip地址,再替换调用的url的微服务名,往下就是Client.Default#execute()发送请求了,本质还是HttpURLConnection。

public Response execute(Request request, Options options) throws IOException {

HttpURLConnection connection = convertAndSend(request, options);

return convertResponse(connection).toBuilder().request(request).build();

}

总结下,使用feign作为客户端调用微服务时,首先创建请求模板,即封装请求的路径,参数等信息的RequestTemplate对象,再为微服务创建应用上下文,从应用上下文中获取负载均衡器,通过负载均衡器找到要调用的微服务,完成url中ip地址的替换,使用HttpURLConnection进行http接口的调用。有不对的地方请大神指出,欢迎大家一起讨论交流,共同进步。

好文推荐

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: