從服務間的一次調用分析整個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.invoke方法,之後會走向調用SynchronousMethodHandler invoke方法,其中SynchronousMethodHandler中的target屬性就是包裝了feignclient的相關屬性,比如service,url等,然後會調用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