• 从服务间的一次调用分析整个springcloud的调用过程(二)


    先看示例代码

     

    复制代码
    @RestController
    @RequestMapping("/students")
    public class StudentController {
    
        @Autowired
        private TeacherFeignClient teacherFeignClient;
    
        @GetMapping("/{id}")
        public ResponseEntity<Teacher> getTeacher(@PathVariable("id") int id) {
            return ResponseEntity.ok(teacherFeignClient.findTeacher(5));
        }
    }
    复制代码
    复制代码
    @FeignClient(name = "teacher-service",path = "/teachers")
    public interface TeacherFeignClient {
    
        @GetMapping("/{id}")
        Teacher findTeacher(@PathVariable("id") int id);
    
    }
    复制代码

      1.  首先我们肯定知道spring会基于TeacherFeignClient生成代理类Proxy,代理类的代码如下

    复制代码
        public final Teacher findTeacher(int var1) throws  {
            try {
                return (Teacher)super.h.invoke(this, m3, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    复制代码

          代理类中的invoke方法指向的是feign.ReflectiveFeign.FeignInvocationHandler.invoke方法,之后会走向调用SynchronousMethodHandler invoke方法,其中SynchronousMethodHandler中的target属性就是包装了feignclient的相关属性,比如service,url等; 在这个类里面还有一些很重要的属性,比如List<RequestInterceptor> requestInterceptors,这个就是spring调用的拦截器,比如我们可以自定义拦截器,添加一些请求头;还有比如errorDecoder,自定义对feign请求异常结果的封装

     

     

    然后会调用executeAndDecode方法,该方法第一步就会根据target构造Request对象,这个方法最终会调用Cilent接口的实现类LoadBalancerFeignClient。

            

    @Override
        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);
            }
        }
    View Code

      2.  LoadBalancerFeignClient execute方法先构造RibbonRequest,然后会去获取IClientConfig,再去调用SpringClientFactory的getInstance方法,走到NamedContextFactory getInstance方法。

        @Override
        public <C> C getInstance(String name, Class<C> type) {
            C instance = super.getInstance(name, type);
            if (instance != null) {
                return instance;
            }
            IClientConfig config = getInstance(name, IClientConfig.class);
            return instantiateWithConfig(getContext(name), type, config);
        }
    View Code

      3. 最终会通过AnnotationConfigApplicationContext getBean方法经过一些列流程走到 RibbonClientConfiguration 装载IClientConfig bean方法,最后就获得了当前service ribbon的相关配置

        @Bean
        @ConditionalOnMissingBean
        public IClientConfig ribbonClientConfig() {
            DefaultClientConfigImpl config = new DefaultClientConfigImpl();
            config.loadProperties(this.name);
            config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
            config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
            config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
            return config;
        }
    View Code

     

      4. 回到LoadBalancerFeignClient execute方法64行获取到IClientConfig后,此时有个核心步骤就是根据当前clientName获取到一个FeignLoadBalancer的实现,可以看到其中有一个cache属性,如果cache有的话就从cache返回,这也就feign第一次调用会慢的原因之一,因为首次需要去加载这个FeignLoadBalancer;首次加载的时候因为这里我们没有配置retryFactory,所以会返回一个 FeignLoadBalancer

        public FeignLoadBalancer create(String clientName) {
            FeignLoadBalancer client = this.cache.get(clientName);
            if(client != null) {
                return client;
            }
            IClientConfig config = this.factory.getClientConfig(clientName);
            ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
            ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);
            client = loadBalancedRetryFactory != null ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
                loadBalancedRetryFactory) : new FeignLoadBalancer(lb, config, serverIntrospector);
            this.cache.put(clientName, client);
            return client;
        }
    View Code

     

     5. 获取到FeignLoadBalancer会执行它父类AbstractLoadBalancerAwareClient中的executeWithLoadBalancer(该方法中牵扯到负载均衡的逻辑,会在下一篇中说到),该方法最终还是会执行FeignLoadBalancer的execute方法,方法中会获取request的client,该client默认是feign.Client.Default,实现就是通过构造HttpURLConnection发起http请求

        @Override
        public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
                throws IOException {
            Request.Options options;
            if (configOverride != null) {
                RibbonProperties override = RibbonProperties.from(configOverride);
                options = new Request.Options(
                        override.connectTimeout(this.connectTimeout),
                        override.readTimeout(this.readTimeout));
            }
            else {
                options = new Request.Options(this.connectTimeout, this.readTimeout);
            }
            Response response = request.client().execute(request.toRequest(), options);
            return new RibbonResponse(request.getUri(), response);
        }
    View Code

     

  • 相关阅读:
    如何快速构建研发效能度量的指标体系?
    经典的设计模式6——桥接模式
    uniapp开发微信小程序-用户授权登录和获取手机号码
    上位机与MES对接的常见方式
    什么是贪财型人格,如何改变贪财型性格
    计算机毕业设计springboot+vue基本微信小程序的水库巡检系统
    代码随想录训练营第60天|84.柱状图中最大的矩形
    【Leetcode】旋转系列(数组、矩阵、链表、函数、字符串)
    华为路由器MPLS VPN综合实验
    贪心算法之过河问题
  • 原文地址:https://www.cnblogs.com/minjay/p/15897845.html