SpringCloud Ribbon的分析
Spring Cloud Ribbon主要用于客户端的负载均衡。最基本的用法便是使用RestTemplate进行动态的负载均衡。我们只需要加入如下的配置便能完成客户端的负载均衡。
@Configuration
public class RestConfiguration {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
/**
* Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient
* @author Spencer Gibb
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
这里的@LoadBalanced使得
RestTemplate
可以使用LoadBalancerClient,
在LoadBalancerClient
在这个路劲下,还存在着LoadBalancerAutoConfiguration
这个配置类只要用于对LoadBalancerClient
做出配置。我们就以此为入口,开始分析ribbon
LoadBalancer的自动化配置
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration { @LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList(); @Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializer(
final List<RestTemplateCustomizer> customizers) {
return new SmartInitializingSingleton() {
@Override
public void afterSingletonsInstantiated() {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
}
};
} @Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList(); @Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
} @Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
} @Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate restTemplate) {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
};
}
}
}
这个配置类主要做了以下几件事
1.创建了一个LoadBalancerInterceptor
的bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。
2.创建了一个RestTemplateCustomizer
的bean,用于给RestTemplate增加LoadBalancerInterceptor
拦截器。
3.维护一个@LoadBalanced
注解修饰的RestTemplate
对象列表,并在这里进行维护,通过调用RestTemplateCustomizer
的实例来给需要的客户端负载均衡的RestTemplate
增加LoadBalancerInterceptor
拦截器。
现在我们看下 LoadBalancerInterceptor 的拦截,我们在这里打上断点,查看一个 RestTemplate 请求是怎么被拦截的。
我们在 org.springframework.http.client.InterceptingClientHttpRequest.InterceptingRequestExecution#execute
发现了如下代码
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}
这里和mybatis比较类似,都是通过责任链模式,一层层的拦截。最终就会到LoadBalancerInterceptor。现在再看LoadBalancerInterceptor的intercept方法
前两步是分别获取这个request的请求地址和ServiceName,这里截图供参考
最开始我们说过,
@LoadBalanced使得
RestTemplate
可以使用LoadBalancerClient,就是在这里使用LoadBalancerClient的executor方法,做出具体的负载均衡。由于这里的LoadBalancerClient是一个接口,他具体的实现类是
RibbonLoadBalancerClient,我们在这里分析具体的execute方法
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer);
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);
}
第一步获取 loadBalancer,loadBalancer是定义软件负载均衡器操作的接口,有以下方法。默认配置的是使用 ZoneAwareLoadBalancer 。
public interface ILoadBalancer { //向负载均衡器中维护的实例列表增加服务实例
public void addServers(List<Server> newServers); //从负载均衡器中挑选出一个具体的服务实例
public Server chooseServer(Object key); //用来通知和标记负载均衡器中的某个具体实例已经停止服务,不然负载均衡器在下一次获取服务实例清单前都会认为服务实例均是正常服务的。
public void markServerDown(Server server);
//获取当前正常服务的实例列表
public List<Server> getReachableServers(); //获取所有已知的服务实例列表,包括正常服务和停止服务实例。
public List<Server> getAllServers();
}
第二步是根据一定的算法去获取server,这一步也是整个ribbon的核心,关于具体的算法逻辑,我们后面分析
protected Server getServer(ILoadBalancer loadBalancer) {
if (loadBalancer == null) {
return null;
}
return loadBalancer.chooseServer("default"); // TODO: better handling of key
}
第三步 在获取到Server后包装成一个 RibbonServer
一个具体的server被选出来后,便可以接着请求,这时会将剩下的拦截器走完
public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
Server server = null;
if(serviceInstance instanceof RibbonServer) {
server = ((RibbonServer)serviceInstance).getServer();
}
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);
RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
try {
T returnVal = request.apply(serviceInstance);
statsRecorder.recordStats(returnVal);
return returnVal;
}return null;
}
最终,创建出一个具体的请求并执行。
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}
else {
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod());
for (Map.Entry<String, List<String>> entry : request.getHeaders().entrySet()) {
List<String> values = entry.getValue();
for (String value : values) {
delegate.getHeaders().add(entry.getKey(), value);
}
}
if (body.length > 0) {
StreamUtils.copy(body, delegate.getBody());
}
return delegate.execute();
}
}
以上就是对ribbon大致流程的分析
SpringCloud Ribbon的分析的更多相关文章
- SpringCloud Ribbon的分析(二)
上文我们分析到 loadBalancer 根据具体的算法选择相应的server. protected Server getServer(ILoadBalancer loadBalancer) { if ...
- SpringCloud Feign的分析
Feign是一个声明式的Web Service客户端,它使得编写Web Serivce客户端变得更加简单.我们只需要使用Feign来创建一个接口并用注解来配置它既可完成. @FeignClient(v ...
- Spring Cloud之负载均衡组件Ribbon原理分析
目录 前言 一个问题引发的思考 Ribbon的简单使用 Ribbon 原理分析 @LoadBalanced 注解 @Qualifier注解 LoadBalancerAutoConfiguration ...
- springcloud Ribbon学习笔记二
之前介绍了如何搭建eureka服务并开发了一个用户服务成功注册到了eureka中,接下来介绍如何通过ribbon来从eureka中获取用户服务: springcloud ribbon提供客户端的负载均 ...
- 客户端实现负载均衡:springCloud Ribbon的使用
Netfilx发布的负载均衡器,是一个基于http.tcp的客户端负载均衡工具,具有控制http.tcp客户端的行为,为ribbon配置服务提供者的地址后,ribbon就 可以经过springClou ...
- springcloud ribbon的 @LoadBalanced注解
在使用springcloud ribbon客户端负载均衡的时候,可以给RestTemplate bean 加一个@LoadBalanced注解,就能让这个RestTemplate在请求时拥有客户端负载 ...
- SpringCloud Ribbon 负载均衡 通过服务器名无法连接的神坑一个
一,问题 采取eureka集群.客户端通过Ribbon调用服务,Ribbon端报下列异常 java.net.UnknownHostException: SERVICE-HI java.lang.Ill ...
- springcloud Ribbon学习笔记一
上篇已经介绍了如何开发eureka服务并让多个服务进行相互注册,接下来记录如何开发一个服务然后注册到eureka中并能通过ribbon成功被调用 开发一个用户服务并注册到eureka中,用户服务负责访 ...
- springcloud源码分析(一)之采用redis实现注册中心
注册中心 在分布式架构中注册中心起到了管理各种服务功能包括服务的注册.发现.熔断.负载.降级等功能,在分布式架构中起到了不可替代的作用.常见的注册中心有eureka,zookeeper等等,在spri ...
随机推荐
- functools.wraps函数
原文地址:https://www.cnblogs.com/fcyworld/p/6239951.html 第一次见到functools.wraps是在 Flask Web开发 中,一直不明白怎么回事. ...
- kali安装vm tools
kali安装完毕后第一件事便是安装VMtools,tools能让kali和物理机进行交互,复制粘贴功能等. 在屏幕上会弹出一个光驱,点击进去,进入文件夹 将文件复制到home文件夹下. 1 tar z ...
- jquery固定表头和列头
1.对网上的开源方法稍作了些修改 <script type="text/javascript">// <![CDATA[ function FixTable(Ta ...
- Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to ;XX.XX.XX.XX:6379] with root cause
java.net.ConnectException: Connection refused: no further information at sun.nio.ch.SocketChannelImp ...
- MyBatis(七) 自定义映射结果ResultMap
(1)接口中对应的方法 public Emp getEmpById(Integer id); (2)Mapper文件 <resultMap type="com.eu.bean.Emp& ...
- msxfs.dll函数加载代码
msxfs.dll函数加载代码 #include "stdafx.h" #include "WSXFSLoader.h" NS_AWP_DEVICE_WOSA_ ...
- delegate异步
using System; using System.Runtime.Remoting.Messaging; using System.Threading; using System.Threadin ...
- WPF实现只打开一个窗口,并且重复打开时已经打开的窗口置顶
内容来自:https://codereview.stackexchange.com/questions/20871/single-instance-wpf-application 第一步:添加Syst ...
- pygame学习
http://eyehere.net/2011/python-pygame-novice-professional-3/ http://www.pygame.org/docs/ref/event.ht ...
- spring-cloud-Zuul学习(三)【中级篇】--Filter链 工作原理与Zuul原生Filter【重新定义spring cloud实践】
这里开始记录zuul中级进阶内容.前面说过了,zuul主要是一层一层的Filter过滤器组成,并且Zuul的逻辑引擎与Filter可用其他基于JVM的语言编写,比如:Groovy. 工作原理 Zuul ...