1.Ribbon源码
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/add")
public String add() {
System.out.println("下单成功!");
String result = restTemplate.getForObject("http://stock-service/stock/deduct", String.class);
return "下单成功!" + result;
}
}
1.1 RibbonAutoConfiguration
spring-cloud-netflix-ribbon-2.2.9.RELEASE.jar,spring.factories中的自动配置类:
RibbonAutoConfiguration,这个在LoadBalancerAutoConfiguration前执行,引入的核心类:
- SpringClientFactory是个ApplicationContextAware,包含每个RPC服务端对应的Spring容器,并且会找出RibbonClientConfiguration
- RibbonLoadBalancerClient,其clientFactory是SpringClientFactory
1.2 LoadBalancerAutoConfiguration
spring-cloud-commons-2.2.9.RELEASE.jar,spring.factories中的自动配置类:LoadBalancerAutoConfiguration引入几个核心类:
- LoadBalancerInterceptor,负载均衡拦截器,入口逻辑在这
- RestTemplateCustomizer,将上面的拦截器加入到RestTemplate的interceptors中
- SmartInitializingSingleton,对所有RestTemplate调用RestTemplateCustomizer,也即将所有RestTemplate的拦截器加上LoadBalancerInterceptor
- LoadBalancerRequestFactory,包含属性loadBalancer:LoadBalancerClient(这个就是spring-cloud-netflix-ribbon-2.2.9.RELEASE.jar引入的RibbonLoadBalancerClient)
1.3 SpringClientFactory创建各RPC服务自己的Spring容器
容器启动过程中:
- SpringApplication#run
- 最后listeners.running(context);发布ApplicationReadyEvent事件
- RibbonApplicationContextInitializer#onApplicationEvent处理该事件
- RibbonApplicationContextInitializer#initialize
- 调用this.springClientFactory.getContext(clientName);这里clientName就是RPC远程调用的服务名stock-service
- SpringClientFactory#getContext
- this.contexts.put(name, createContext(name));
- NamedContextFactory#createContext,在这里会创建一个AnnotationConfigApplicationContext容器,并且进行刷新,然后放到contexts的hashmap中,也即一个远程服务对应一个容器
- context.register(RibbonClientConfiguration.class)
- 在RibbonClientConfiguration就定义了ZoneAwareLoadBalancer
- 并且默认情况HttpClient用的是HttpClientRibbonConfiguration,在这里面定义的是RibbonLoadBalancingHttpClient
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
// Order is important here, last should be the default, first should be optional
// see
// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({ HttpClientConfiguration.class, OkHttpRibbonConfiguration.class,
RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class })
public class RibbonClientConfiguration {
1.4 负载均衡器执行流程
执行流程:
- RestTemplate.getForObject()
- InterceptingClientHttpRequest.InterceptingRequestExecution#execute这里会先遍历执行Interceptor#intercept,然后再执行RPC调用
- LoadBalancerInterceptor#intercept
- RibbonLoadBalancerClient#execute
- 1)SpringClientFactory#getLoadBalancer获取到的是ZoneAwareLoadBalancer
- 2)ZoneAwareLoadBalancer#chooseServer
父类:BaseLoadBalancer#chooseServer;
rule.choose(key);默认是ZoneAvoidanceRule;
这里首先会获取ZoneAwareLoadBalancer的allServerList(Nacos整合关键点);
然后调用轮询算法从中获取一个。 - 3)execute()
LoadBalancerRequestFactory#createRequest中的request执行;
回到InterceptingClientHttpRequest.InterceptingRequestExecution#execute;
HttpComponentsClientHttpRequestFactory#createRequest;
HttpComponentsClientHttpRequest#execute;
AbstractClientHttpRequest#execute;
this.httpClient.execute;
RibbonLoadBalancerClient#execute()
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);
}
1.5 Ribbon与Nacos client的整合
ZoneAwareLoadBalancer构造方法:
- DynamicServerListLoadBalancer#restOfInit
- enableAndInitLearnNewServersFeature();定时执行updateListOfServers(); 30s执行一次。
- updateListOfServers();
- NacosServerList#getUpdatedListOfServers
- NacosNamingService#selectInstances()
clientProxy.subscribe()
clientProxy.queryInstancesOfService() - DynamicServerListLoadBalancer#updateAllServerList
- 设置到负载均衡器的allServerList里面
2.OpenFeign源码剖析
2.1 注解@EnableFeignClients
@EnableFeignClients
- @Import(FeignClientsRegistrar.class)
- FeignClientsRegistrar#registerBeanDefinitions
- FeignClientsRegistrar#registerFeignClients
找出所有@FeignClient注解的类;
beanDefinition的InstanceSupplier里面设置为FeignClientFactoryBean#getObject。
2.2 FeignClientFactoryBean创建动态代理
重点看FeignClientFactoryBean#getObject
- FeignClientFactoryBean#getTarget
- 从容器获取FeignContext context =
beanFactory.getBean(FeignContext.class) - Feign.Builder builder = feign(context);
- 拼接url为:http://stock-service/stock
- FeignClientFactoryBean#loadBalance
(T) loadBalance(builder, context,new HardCodedTarget<>(type, name, url)); 这里type是StockService,name是stock-service,url是http://stock-service/stock - Client client = getOptional(context, Client.class);从FeignContext获取Client,实际为LoadBalancerFeignClient
- Targeter targeter = get(context, Targeter.class);从FeignContext获取Targeter,实际为HystrixTargeter
- HystrixTargeter#target
- Feign.Builder#target(feign.Target<T>)
- ReflectiveFeign#newInstance
- 创建动态代理,核心的handler是ReflectiveFeign.FeignInvocationHandler
<T> T getTarget() {
FeignContext context = beanFactory != null
? beanFactory.getBean(FeignContext.class)
: applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(url)) {
if (LOG.isInfoEnabled()) {
LOG.info("For '" + name
+ "' URL not provided. Will try picking an instance via load-balancing.");
}
if (!name.startsWith("http")) {
url = "http://" + name;
}
else {
url = name;
}
url += cleanPath();
return (T) loadBalance(builder, context,
new HardCodedTarget<>(type, name, url));
}
if (StringUtils.hasText(url) && !url.startsWith("http")) {
url = "http://" + url;
}
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not load balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient) client).getDelegate();
}
if (client instanceof FeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
}
if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
client = ((RetryableFeignBlockingLoadBalancerClient) client)
.getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(type, name, url));
}
2.3 代理对象执行过程
执行调用过程ReflectiveFeign.FeignInvocationHandler#invoke
- dispatch.get(method).invoke(args);这里dispatch是Method到MethodHandler的映射
- SynchronousMethodHandler#invoke
- Request request = targetRequest(template);创建请求 GET http://stock-service/stock/deduct HTTP/1.1
- LoadBalancerFeignClient#execute (核心)
- lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse(); 这里clientName是stock-service,ribbonRequest里面有uriWithoutHost:http:///stock/deduct
- LoadBalancerFeignClient#lbClient通过CachingSpringLoadBalancerFactory创建FeignLoadBalancer
- FeignLoadBalancer#executeWithLoadBalancer
- 父类AbstractLoadBalancerAwareClient#executeWithLoadBalancer
- LoadBalancerCommand#submit
- LoadBalancerCommand#selectServer
- LoadBalancerContext#getServerFromLoadBalancer,这里实际是FeignLoadBalancer
- 获取ILoadBalancer是ZoneAwareLoadBalancer
- 调用ZoneAwareLoadBalancer#chooseServer负载均衡选择一个服务器,例如192.168.2.2.8021
- LoadBalancerContext#reconstructURIWithServer构建finalUri为http://192.168.2.2:8021/stock/deduct
- FeignLoadBalancer#execute
- Client.Default#execute
2.4 Feign如何整合Ribbon
spring-cloud-openfeign-core-2.2.9.RELEASE.jar
spring.factories中有自动配置类FeignRibbonClientAutoConfiguration
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
OkHttpFeignLoadBalancedConfiguration.class,
HttpClient5FeignLoadBalancedConfiguration.class,
DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
该自动配置类引入了两个重要类:
- 1)CachingSpringLoadBalancerFactory,入参是SpringClientFactory
- 2) DefaultFeignLoadBalancedConfiguration引入了LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,clientFactory);
重点看一下这里的SpringClientFactory
- 这里还是上面Ribbon为每个RPC服务生成的子容器,里面都是该RPC服务Ribbon相关的类


那负载均衡是哪里来的?
- 是从FeignLoadBalancer中获取负载均衡器的lb
- FeignLoadBalancer的lb是从SpringClientFactory中获取到
- SpringClientFactory是Ribbon生成的,也即拿到的就是ZoneAwareLoadBalancer
网友评论