gpt4 book ai didi

feign 如何获取请求真实目的ip地址

转载 作者:qq735679552 更新时间:2022-09-29 22:32:09 25 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章feign 如何获取请求真实目的ip地址由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

需求

最近小编的项目中出现了很多feign 调用出现 Read Time out 的异常,但因为没有集成链路追踪的第三方框架,查不到原因.

所以想到打印请求的ip地址,判断是指定的服务器出现的问题还是所有服务器都有这个问题,但是feign 打印异常日志不会显示目的端地址,这就很难受了没办法只能自己改装下 。

大致想法

需要改装肯定需要知道feign 具体请求调用的源码,大致需要知道下面几个问题 。

  • feign 集成了ribbon 如何在负载均衡之后获取真实的ip地址
  • feign 实际请求 http 源码在哪
  • 能否替换 feign http 请求的组件

源码解析

之前小编有两篇文章分析过 feign相关的源码 。

自定义 feign 调用实现 hystrix 超时、异常熔断 。

Feign 集成 Hystrix实现不同的调用接口不同的设置 。

这其中有个关键的源码位置在于 InvocationHandler 的 invoke 方法,在feign 组件中大致有两个类实现了此接口 。

?
1
2
FeignInvocationHandler
HystrixInvocationHandler

如果 项目中使用了 Hystrix 那么会用到HystrixInvocationHandler那个,否则一般是FeignInvocationHandler(自定义组件的除外) 。

那么此时只需要在invoke 方法中打个断点就行 。

feign 如何获取请求真实目的ip地址

此时跟踪到 。

?
1
feign.SynchronousMethodHandler#executeAndDecode
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 是 。

?
1
2
LoadBalancerFeignClient
org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#execute
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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);
         }
     }
?
1
com.netflix.client.AbstractLoadBalancerAwareClient#executeWithLoadBalancer(....)
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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);
             }
         }       
     }
?
1
org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer#execute
?
1
2
3
4
5
6
7
8
9
10
11
@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);
  }
?
1
feign.Client.Default#execute
?
1
2
3
4
5
@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资源路径了 。

现在屡屡逻辑 。

?
1
org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient和feign.Client.Default

都实现了 feign.Client 接口,但是 LoadBalancerFeignClient 实际上调用的还是 feign.Client.Default,无非做了自己处理(负载),有些类似于静态代理 。

那么上面的问题就只剩下能否替换的问题了 。

?
1
2
3
4
5
6
7
8
9
10
@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 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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;
         }
     }
}

然后将这个类替换 。

?
1
2
3
4
5
6
7
8
9
10
11
12
@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);
     }   
}

原文链接:https://blog.csdn.net/weixin_39660224/article/details/115397061 。

最后此篇关于feign 如何获取请求真实目的ip地址的文章就讲到这里了,如果你想了解更多关于feign 如何获取请求真实目的ip地址的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com