这篇文章主要介绍“feign怎么获取请求真实目的ip地址”,在日常操作中,相信很多人在feign怎么获取请求真实目的ip地址问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”feign怎么获取请求真实目的ip地址”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
最近小编的项目中出现了很多feign 调用出现 Read Time out 的异常,但因为没有集成链路追踪的第三方框架,查不到原因。
所以想到打印请求的ip地址,判断是指定的服务器出现的问题还是所有服务器都有这个问题,但是feign 打印异常日志不会显示目的端地址,这就很难受了没办法只能自己改装下
需要改装肯定需要知道feign 具体请求调用的源码,大致需要知道下面几个问题
feign 集成了ribbon 如何在负载均衡之后获取真实的ip地址
feign 实际请求 http 源码在哪
能否替换 feign http 请求的组件
之前小编有两篇文章分析过 feign相关的源码
自定义 feign 调用实现 hystrix 超时、异常熔断
Feign 集成 Hystrix实现不同的调用接口不同的设置
这其中有个关键的源码位置在于 InvocationHandler 的 invoke 方法,在feign 组件中大致有两个类实现了此接口
FeignInvocationHandler HystrixInvocationHandler
如果 项目中使用了 Hystrix 那么会用到HystrixInvocationHandler那个,否则一般是FeignInvocationHandler(自定义组件的除外)
那么此时只需要在invoke 方法中打个断点就行
此时跟踪到
feign.SynchronousMethodHandler#executeAndDecode
Object executeAndDecode(RequestTemplate template) throws Throwable { Request request = targetRequest(template); ....... Response response; long start = System.nanoTime(); try { // 真正执行请求 response = client.execute(request, options); response.toBuilder().request(request).build(); } catch (IOException e) { .... throw errorExecuting(request, e); } ..... }
通过debug就知道这个 client 是
LoadBalancerFeignClient org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#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); // 封装 ribbon 请求组件 FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest( this.delegate, request, uriWithoutHost); IClientConfig requestConfig = getClientConfig(options, clientName); // 这行是关键 return // 获取 FeignLoadBalancer lbClient(clientName) // 负载之后请求真实的url // com.netflix.client.AbstractLoadBalancerAwareClient#executeWithLoadBalancer(....) .executeWithLoadBalancer(ribbonRequest,requestConfig) .toResponse(); } catch (ClientException e) { .... throw new RuntimeException(e); } }
com.netflix.client.AbstractLoadBalancerAwareClient#executeWithLoadBalancer(....)
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException { LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig); try { // 在com.netflix.loadbalancer.reactive.LoadBalancerCommand#submit 中会根据 负载均衡算法之后获取到真实的ip地址 return command.submit( new ServerOperation<T>() { @Override // 传入的server 就是真实的ip public Observable<T> call(Server server) { URI finalUri = reconstructURIWithServer(server, request.getUri()); // 路径替换把原本 http://client-name/xxxx 地址改为 http://127.0.0.1:9090/xxxx S requestForServer = (S) request.replaceUri(finalUri); try { // 请求父类中的 execute 方法,也就是 上面 lbClient(clientName) 返回的 FeignLoadBalancer return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig)); } catch (Exception e) { return Observable.error(e); } } }) .toBlocking() .single(); } catch (Exception e) { Throwable t = e.getCause(); if (t instanceof ClientException) { throw (ClientException) t; } else { throw new ClientException(e); } } }
org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer#execute
@Override public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride) throws IOException { Request.Options options; ..... // 这里的 request 就是 `org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#execute` // 封装的FeignLoadBalancer.RibbonRequest // request.client() 返回就是 feign.Client.Default Response response = request.client().execute(request.toRequest(), options); return new RibbonResponse(request.getUri(), response); }
feign.Client.Default#execute
@Override public Response execute(Request request, Options options) throws IOException { HttpURLConnection connection = convertAndSend(request, options); return convertResponse(connection).toBuilder().request(request).build(); }
这里的request 中 url 就是真实的url资源路径了
现在屡屡逻辑
org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient和feign.Client.Default
都实现了 feign.Client 接口,但是 LoadBalancerFeignClient 实际上调用的还是 feign.Client.Default,无非做了自己处理(负载),有些类似于静态代理
那么上面的问题就只剩下能否替换的问题了
@Configuration class DefaultFeignLoadBalancedConfiguration { @Bean @ConditionalOnMissingBean public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) { return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory, clientFactory); } }
这就不需要我来过多解释了,我们只需要自定义一个 LoadBalancerFeignClient 或者 实现Client的类就行 然后注入就行
我选择的是 自定义实现一个 Client,去继承 feign.Client.Default
@Slf4j public class InFeignClient extends Client.Default { /** */ public InFeignClient(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier) { super(sslContextFactory, hostnameVerifier); } @Override public Response execute(Request request, Request.Options options) throws IOException { try { return super.execute(request, options); } catch (IOException e) { log.warn(" 请求 {} 异常 ======> {}", request.url(), e.getMessage()); throw e; } } }
然后将这个类替换
@Component public class RestConfig { public CachingSpringLoadBalancerFactory cachingLBClientFactory( SpringClientFactory factory) { return new CachingSpringLoadBalancerFactory(factory); } @Bean public Client feignClient(SpringClientFactory clientFactory) { CachingSpringLoadBalancerFactory bean = cachingLBClientFactory(clientFactory); return new LoadBalancerFeignClient(new InFeignClient(null, null), bean, clientFactory); } }
到此,关于“feign怎么获取请求真实目的ip地址”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。