在閱讀這篇博客以前,但願你對SpringCloud套件熟悉和理解,更但願關注下微服務開發平臺java
在使用springcloud ribbon客戶端負載均衡的時候,能夠給RestTemplate bean 加一個@LoadBalanced註解,就能讓這個RestTemplate在請求時擁有客戶端負載均衡的能力,先前有細嚼過可是沒有作過筆記,恰好處理此類問題記錄下git
/** * 註釋將RestTemplate bean標記爲配置爲使用LoadBalancerClient。 */ @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Qualifier public @interface LoadBalanced { }
經過源碼能夠發現這是一個LoadBalanced
標記註解而且標記了@Qualifier
(基於Spring Boot的自動配置機制),咱們能夠溯源到LoadBalancerAutoConfiguration
spring
/** * 功能區的自動配置(客戶端負載平衡) */ @Configuration @ConditionalOnClass(RestTemplate.class) @ConditionalOnBean(LoadBalancerClient.class) @EnableConfigurationProperties(LoadBalancerRetryProperties.class) public class LoadBalancerAutoConfiguration { @LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); //這裏持有@LoadBalanced標記的RestTemplate實例 @Autowired(required = false) private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList(); @Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated( final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) { return () -> restTemplateCustomizers.ifAvailable(customizers -> { for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { for (RestTemplateCustomizer customizer : customizers) { //爲restTemplate添加定製 customizer.customize(restTemplate); } } }); } // ... /** * 如下針對classpath存在RetryTemplate.class的狀況配置,先忽略 */ @Configuration @ConditionalOnClass(RetryTemplate.class) public static class RetryAutoConfiguration { @Bean @ConditionalOnMissingBean public LoadBalancedRetryFactory loadBalancedRetryFactory() { return new LoadBalancedRetryFactory() { }; } } // ... }
@LoadBalanced
和@Autowried
結合使用,意思就是這裏注入的RestTempate
Bean是全部加有@LoadBalanced
註解標記的(持有@LoadBalanced
標記的RestTemplate實例)api
這段自動裝配的代碼的含義不難理解,就是利用了RestTempllate的攔截器,使用RestTemplateCustomizer對全部標註了@LoadBalanced的RestTemplate Bean添加了一個LoadBalancerInterceptor攔截器,而這個攔截器的做用就是對請求的URI進行轉換獲取到具體應該請求哪一個服務實例ServiceInstance。app
關鍵問下本身:爲何?負載均衡
繼續扒看源碼>
上面能夠看出,會LoadBalancerAutoConfiguration類
對咱們加上@LoadBalanced
註解的bean 添加loadBalancerInterceptor
攔截器ide
/** * 功能區的自動配置(客戶端負載平衡)。 */ public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor { private LoadBalancerClient loadBalancer; private LoadBalancerRequestFactory requestFactory; public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) { this.loadBalancer = loadBalancer; this.requestFactory = requestFactory; } public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) { // for backwards compatibility this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer)); } @Override public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException { final URI originalUri = request.getURI(); String serviceName = originalUri.getHost(); Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri); return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution)); } }
重點看intercept方法 當咱們restTemplate執行請求操做時,就會被攔截器攔截進入intercept方法,而loadBalancer是LoadBalancerClient的具體實現微服務
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException { ILoadBalancer loadBalancer = getLoadBalancer(serviceId); Server server = getServer(loadBalancer, hint); if (server == null) { throw new IllegalStateException("No instances available for " + serviceId); } RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server)); return execute(serviceId, ribbonServer, request); }
看到這裏相信都遇到過相似的錯誤,恍然大悟ui
No instances available for xxxxx
這裏this
注意: @LoadBalanced 標記註解獲取到最後經過負載均衡規則獲取具體的具體的server來發起請求
/** * 服務註冊中心配置 * * @author <a href="mailto:shangzhi.ibyte@gmail.com">iByte</a> * @since 1.0.1 */ @Configuration @EnableConfigurationProperties(ModuleMappingHelper.class) public class DiscoveryConfig { @Autowired Environment environment; /** * DiscoveryHeaderHelper默認bean * @return */ @Bean public DiscoveryHeaderHelper discoveryHeaderHelper() { DiscoveryHeaderHelper discoveryHeaderHelper = new DiscoveryHeaderHelper(environment); DiscoveryHeaderHelper.INSTANCE = discoveryHeaderHelper; return discoveryHeaderHelper; } /** * resttemplate構建 */ @Resource private RestTemplateBuilder restTemplateBuilder; /** * resttemplate請求bean,更改系統自己的builder * @return */ @Bean @LoadBalanced public RestTemplate restTemplate() { RestTemplate restTemplate = restTemplateBuilder.configure(new RestTemplate()); //RestTemplate interceptors 遠程調用請求增長頭部信息處理 restTemplate.getInterceptors().add(new RestApiHeaderInterceptor()); //RestTemplate Set the error handler 錯誤處理 restTemplate.setErrorHandler(new RestResponseErrorHandler()); return restTemplate; } @Bean public DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs() { DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs = new DiscoveryClient.DiscoveryClientOptionalArgs(); discoveryClientOptionalArgs.setAdditionalFilters(Collections.singletonList(new DiscoveryHeaderClientFilter())); discoveryClientOptionalArgs.setEventListeners(Collections.singleton(new EurekaClientEventListener())); return discoveryClientOptionalArgs; } }
源碼地址 > DiscoveryConfig