乐趣区

关于springboot:Ribbon工作流程细节

起因是 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 申请,并获取执行后果:

退出移动版