先看示例代碼
@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
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/424881.html
標籤:Java
