起因是ribbon集成spring boot、openfeign实现负载平衡近程调用,初始阶段没有增加上面配置,发现第一次进行近程调用,ribbon报错 【read time out】。而后增加如下配置,解决问题~
# 设置ribbon 我的项目启动时加载配置项,防止feign第一次调用【read time out】ribbon: eager-load: enabled: true clients: api-service
下面的配置,从字面意思看进去,让Ribbon及时加载,那么问题来了?利用启动时,ribbon是怎么起作用的呢?ok,持续往下看!
@ConfigurationProperties(prefix = "ribbon.eager-load")public class RibbonEagerLoadProperties { private boolean enabled = false; private List<String> clients;}
看进去了吧,是org.springframework.cloud.netflix.ribbon.RibbonEagerLoadProperties 起作用了。那么这个类是如何被唤起了呢?
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class })@ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled", havingValue = "true", matchIfMissing = true)public class RibbonAutoConfiguration { @Autowired(required = false) private List<RibbonClientSpecification> configurations = new ArrayList<>(); @Autowired private RibbonEagerLoadProperties ribbonEagerLoadProperties; @Bean @ConditionalOnProperty("ribbon.eager-load.enabled") public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() { return new RibbonApplicationContextInitializer(springClientFactory(), ribbonEagerLoadProperties.getClients()); }}
这个类在spring-cloud-netflix包中,ribbonEagerLoadProperties被注入进来,而后在
ribbonApplicationContextInitializer()中申明RibbonApplicationContextInitializer类,它继承了ApplicationListener,在实现办法onApplicationEvent()中调用initialize(),从而把咱们的配置的指标clients(也就是"api-service")加载到org.springframework.cloud.context.named.NamedContextFactory的contexts中。上面是具体的调用链:
图1
public void onApplicationEvent(ApplicationReadyEvent event) { if (clientNames != null) { for (String clientName : clientNames) { this.springClientFactory.getContext(clientName); } } }protected AnnotationConfigApplicationContext getContext(String name) { if (!this.contexts.containsKey(name)) { synchronized (this.contexts) { if (!this.contexts.containsKey(name)) { //看这里看这里 this.contexts.put(name, createContext(name)); } } } return this.contexts.get(name); }
上面的图是NamedContextFactory中存储的AnnotationConfigApplicationContext实例:
图2
上面咱们调用近程服务的checkToken(),上面的图很好得反馈了具体调用栈信息:
图3
到此为止,Robbin的工作形式曾经大略理解,后续会持续更新Robbin的更多细节,欢送留言沟通~
上面的局部,能够持续摸索feign执行细节
org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.execute()。
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); //这里依据clientName执行负载平衡逻辑,并依据url发送http 申请 return lbClient(clientName) .executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse(); } catch (ClientException e) { IOException io = findIOException(e); if (io != null) { throw io; } throw new RuntimeException(e); } }
feign的调用链如下:
其中,Client.execute()细节:
public Response execute(Request request, Options options) throws IOException { HttpURLConnection connection = convertAndSend(request, options); return convertResponse(connection, request); }public HttpURLConnection getConnection(final URL url) throws IOException { //还有这里^ _ ^ return (HttpURLConnection) url.openConnection(); }HttpURLConnection convertAndSend(Request request, Options options) throws IOException { final URL url = new URL(request.url()); //重点在这里^ - ^ final HttpURLConnection connection = this.getConnection(url); if (connection instanceof HttpsURLConnection) { HttpsURLConnection sslCon = (HttpsURLConnection) connection; if (sslContextFactory != null) { sslCon.setSSLSocketFactory(sslContextFactory); } if (hostnameVerifier != null) { sslCon.setHostnameVerifier(hostnameVerifier); } }}
看到了没有,最终用jdk的HttpURLConnection 执行http申请,并获取执行后果: