(1)feign使用,是main上的@EnableFeignClients 和 api的@FeignClient(value = "serviceXXX") 搭配使用。
(2)@FeignClient 是個自定義註解沒啥東西只是個feign定義,
(3)@EnableFeignClients核心是@Import(FeignClientsRegistrar.class),在這裏完成對@FeignClient的掃描和beanDefination的定義。spring
(1)FeignClientsRegistrar的核心方法 registerBeanDefinitions(),包含
registerDefaultConfiguration(metadata, registry); 拿到@EnableFeignClients的屬性冰註冊配置bean,
registerFeignClients (metadata, registry); 掃@feignClient並註冊,是核心api
(2)registerFeignClients細節,
a 獲得scanner:ClassPathScanningCandidateComponentProvider
b 根據getBasePackages()獲得包,默認是SpringbootApplication同路徑的包。
c 聲明FeignClient.class的AnnotationTypeFilter,加入到scanner
d 遍歷packages,findCandidateComponents(),經過判斷isCandidateComponent拿到class,判斷依據是是否知足註解filter(已經加入FeignClient條件)
e registerFeignClient 註冊獲得的feign的bd,是用factorybean,FeignClientFactoryBean,擴展了不少feign屬性。beanname是feign類的全類名。最終register到容器。緩存
(1)經過getObject()返回bean,調用getTarget()架構
(2)首先FeignContext context = applicationContext.getBean(FeignContext.class); 拿到feign環境,bean定義是在org.springframework.cloud.openfeign.FeignAutoConfiguration, 核心this.configurations.put(client.getName(), client) 是爲每一個feign維護了一個環境,放在map中。每一個feign實例都能從feignContext裏拿到獨立的spring容器。T instance = context.getInstance(this.name, type);這種,name就是feignclient註解的 「servicexxxx」。mvc
(3)Feign.Builder builder = get(context, Feign.Builder.class),結合環境生成一個builder,app
這個builder也是從spring容器拿的,FeignClientsConfiguration中有兩個,一個hystrix的,一個retryer(默認)的。從feignContext裏拿到servicename對應的容器,再根據類型拿到Encoder、Decoder、Contract設置builder。這些bean都在FeignClientsConfiguraiton中定義:SpringEncoder、ResponseEntityDecoder、SpringMvcContract負載均衡
configureFeign(),把application.yml的屬性配置進builder中,ide
(4)核心,處理url後,手動寫一個HardCoudeTarget,包含服務名稱、接口類名、url,調用loadBalance(target),在這裏面經過容器拿到一個Client,client定義在DefaultFeignLoadBalancedConfiguration,實際類型是LoadBalancerFeignClient。微服務
(5)targeter.target(this, builder, context, target) 實現代理。ui
Feign.Builder和HardCodedTarget,來最終基於feign的動態代理。
(1) build().newInstance(target);
build()融合了FeignBuilder全部內容建立了RefrectiveFeign,先建立SynchronousMethodHandler.Factory,包含LoadBalancerFeignClient、Retryer(負責請求重試)、請求攔截器、日誌打印等,用來建立methodHandler。
(2)Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
根據contract,解析target裏面全部方法,包含屬性和各類springmvc註解解析,造成SynchronousMethodHandler
(3)Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
遍歷target的method,造成method和SynchronousMethodHandler的對應map。
(4)InvocationHandler handler = factory.create(target, methodToHandler)-》ReflectiveFeign.FeignInvocationHandler(target, dispatch) 造成代理。最終是SynchronousMethodHandler的invoke方法,實現最終Proxy.newProxyInstance代理過程。
(5)invoke方法: dispatch.get(method).invoke(args) 根據方法自己,拿到MethodHandler,調用MethodHandler的invoke,包含了ribbon的RequestTemplate,負載均衡,融合重試機制。
(1)核心在FeignInvocationHandler的invoke,dispatch.get(method).invoke(args);實際是SynchronousMethodHandler的invoke,進而executeAndDecode(template);
(2) Request request = targetRequest(template); 獲得request,其實就是target的feignService信息(service名稱,類型等)和請求的springmvc方法,結合出
「GET http://service2/serviceInfo HTTP/1.1」這種請求信息。
(3)response = client.execute(request, options);是LoadBalanceFeignClient,先建立一個RibbonRequest,在拿到ribbon的IClientConfig。
(4)lbClient(clientName),從CachingSpringLoadBalancerFactory中拿到FeignLoadBalancer,FeignLoadBalancer裏經過spring容器拿到並封裝了ribbon的ILoadBalancer(ribbon用的ZoneAwareLoadBalancer)。CachingSpringLoadBalancerFactory採用了緩存。
(5)lbClient(clientName).executeWithLoadBalancer(ribbonRequest,requestConfig).toResponse() 的第二步
executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
調用路線:submit()->selectServer()->ServerOption.call() 發送http請求。selectServer使用了ribbon的select過程。
HttpMessageConverters 默認使用jackson2方式進行序列化和反序列化。可是若是是微服務間頻頻通訊,使用jackson2序列化和反序列化會佔用很多系統資源,而且效率較差。能夠手動擴展編解碼方式,繼承AbstractHttpMessageConverter<Object> ,使用@FeignClient(value = "service",path ="/nafosRemoteCall/test" , configuration = ProtoFeignConfiguration.class) 進行配置。