目录:

  • Ribbon简介
  • Ribbon的应用
  • RestTemplate简介
  • Ribbon负载均衡源码分析

Ribbon简介:

1、负载均衡是什么

负载均衡,根据其字面意思来说就是让集群服务具有共同完成工作的能力,通过负载均衡可以在多个应用实例之间自动分配程序对外服务的能力;从而通过消除单点机器的故障,提升应用的容错能力,让应用更加高效、稳定、安全。

2、SpringCloud Ribbon是什么

SpringCloud Ribbon是基于Http和TCP的一种负载工具,基于Netflix Ribbon实现;它可以将基于服务的rest请求自动转换成客户端负载均衡的服务调用。

Ribbon的应用:

1、Ribbon配置

)添加依赖

 <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

)重新构造RestTemplate(使得RestTemplate的请求能够实现负载均衡)

 @Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}

)配置properties

 ## 局部配置-- 单独制定客户端(eureka-provider客户端)
eureka-provider.ribbon.listOfServers=localhost:8072,localhost:8073

配置格式:<clientName>.<nameSpace>.<propertyName>=<value>

propertyName见com.netflix.client.config.CommonClientConfigKey

2、Ribbon重试机制

说到Ribbon的重试机制就需要先看看Ribbon的一些基本配置:

然后我们看最后两个配置,MaxAutoRetriesNextServer、MaxAutoRetries,这两个分别是切换实例的重试次数和实例的重试次数;例如我们有三个实例A、B、C,如果第一次请求A失败,会继续请求A(这便是对实例的重试),此时还是请求失败的话就会去重试实例B,以此类推,直到请求C失败两次后便算是失败,也就是说如上图的重试配置的话请求 2 * 3 = 6次,6次失败就算真的失败。

当然,如果你配置了短路器超时时间,也就是上图的第一个配置的话,那么你总体的重试时间加上第一次正常请求的时间也不能超时1秒。

RestTemplate简介:

RestTemplate封装了多个Http客户端,如HttpClient、OKHttp3等。

详见:org.springframework.web.client.RestTemplate#RestTemplate(org.springframework.http.client.ClientHttpRequestFactory)

1、构造自己的RestTemplate

 @Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate(new OkHttp3ClientHttpRequestFactory(okHttpClient()));
} @Bean
public OkHttpClient okHttpClient() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10,TimeUnit.SECONDS)
.retryOnConnectionFailure(true);
return builder.build();
}

Ribbon负载均衡源码分析:

我们知道要想是使客户端的请求能够负载均衡的话,只需要重新构造RestTemplate,并为其加上@LoadBalanced注解即可,那为什么加上这个注解就行了呢?

1、首先我们要知道@LoanBalanced是对RestTemplate增强,而RestTemplate是对Http请求的一个封装,所以我们猜测增强时使用的应该是拦截器

)我们进入RestTemplate,并找到父类org.springframework.http.client.support.InterceptingHttpAccessor发现其有一个属性private List<ClientHttpRequestInterceptor> interceptors,这便是spring对http请求的封装,interceptors便是请求所需要经过的拦截器集合;

 private List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();

 /**
* Sets the request interceptors that this accessor should use.
*/
public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
this.interceptors = interceptors;
} /**
* Return the request interceptor that this accessor uses.
*/
public List<ClientHttpRequestInterceptor> getInterceptors() {
return interceptors;
}

)然后我们看看这个拦截器集合在哪set的 >>> public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors)

根据类名我们猜测不是上图中的第2个,就是第3个,然后我们分别查看,得知是第2个,我们现在看看第2个的实现:

 @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);
}
};
}
}

从18到21行代码我们可以看出其在原来http拦截器集合的基础上又增加了loadBalancerInterceptor这个拦截器,而这个拦截器正好就是第5行的那个bean。

根据上述结论我们可以得知:RestTemplate加上了@LoadBalanced注解后,其实就是为http增强了一个拦截器,也就是org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor

)那么为什么添加了拦截器会生效呢,我们可以根据RestTemplate的一个请求debug(请求有点深)

如:String result = restTemplate.getForObject("http://eureka-provider/updateProduct/" + productName + "/" + num, String.class);

经过debug后我们可以找到这样一个方法 >>> org.springframework.http.client.InterceptingClientHttpRequest.InterceptingRequestExecution#execute

 @Override
public ClientHttpResponse execute(HttpRequest request, final byte[] body) throws IOException {
if (this.iterator.hasNext()) {
// 拿到当前拦截器集
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
// 执行当前拦截器的intercept方法,添加的LoanBalancerInterceptor拦截器就是这样执行的
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) {
if (delegate instanceof StreamingHttpOutputMessage) {
StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() {
@Override
public void writeTo(final OutputStream outputStream) throws IOException {
StreamUtils.copy(body, outputStream);
}
});
} else {
StreamUtils.copy(body, delegate.getBody());
}
}
return delegate.execute();
}
}

2、然后我们来看看LoanBalancerInterceptor的实现

 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, requestFactory.createRequest(request, body, execution));
}
}

)从LoanBalancerInterceptor的拦截方法intercept中可以看出execute执行的是loanBanlancer的execute

)这也验证了@LoadBalanced注解上的那句Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient(将RestTemplate bean标记为使用LoadBalancerClient的注解)

)然后我们来看看execute的实现 >>> public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException

 @Override
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);
}
RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, isSecure(server,
serviceId), serverIntrospector(serviceId).getMetadata(server));
// 得到服务后,最终执行请求
return execute(serviceId, ribbonServer, request);
}

获取可用服务列表、负载均衡算法这里就不讲解了,有兴趣的同学可以自己去看看 (*^▽^*)

SpringCloud学习笔记(四、SpringCloud Netflix Ribbon)的更多相关文章

  1. SpringCloud学习笔记(2):使用Ribbon负载均衡

    简介 Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具,在注册中心对Ribbon客户端进行注册后,Ribbon可以基于某种负载均衡算法,如轮询(默认 ...

  2. SpringCloud学习笔记:负载均衡Ribbon(3)

    1. RestTemplate简介 RestTemplate是Spring Resource中一个访问第三方RESTful API接口的网络请求框架. RestTemplate是用来消费REST服务的 ...

  3. SpringCloud学习笔记:SpringCloud简介(1)

    1. 微服务 微服务具有的特点: ◊ 按照业务划分服务 ◊ 每个微服务都有独立的基础组件,如:数据库.缓存等,且运行在独立的进程中: ◊ 微服务之间的通讯通过HTTP协议或者消息组件,具有容错能力: ...

  4. SpringCloud学习笔记(3):使用Feign实现声明式服务调用

    简介 Feign是一个声明式的Web Service客户端,它简化了Web服务客户端的编写操作,相对于Ribbon+RestTemplate的方式,开发者只需通过简单的接口和注解来调用HTTP API ...

  5. SpringCloud学习笔记(4):Hystrix容错机制

    简介 在微服务架构中,微服务之间的依赖关系错综复杂,难免的某些服务会出现故障,导致服务调用方出现远程调度的线程阻塞.在高负载的场景下,如果不做任何处理,可能会引起级联故障,导致服务调用方的资源耗尽甚至 ...

  6. SpringCloud学习笔记(5):Hystrix Dashboard可视化监控数据

    简介 上篇文章中讲了使用Hystrix实现容错,除此之外,Hystrix还提供了近乎实时的监控.本文将介绍如何进行服务监控以及使用Hystrix Dashboard来让监控数据图形化. 项目介绍 sc ...

  7. SpringCloud学习笔记(6):使用Zuul构建服务网关

    简介 Zuul是Netflix提供的一个开源的API网关服务器,SpringCloud对Zuul进行了整合和增强.服务网关Zuul聚合了所有微服务接口,并统一对外暴露,外部客户端只需与服务网关交互即可 ...

  8. SpringCloud学习笔记:服务支撑组件

    SpringCloud学习笔记:服务支撑组件 服务支撑组件 在微服务的演进过程中,为了最大化利用微服务的优势,保障系统的高可用性,需要通过一些服务支撑组件来协助服务间有效的协作.各个服务支撑组件的原理 ...

  9. SpringCloud学习笔记(7):使用Spring Cloud Config配置中心

    简介 Spring Cloud Config为分布式系统中的外部化配置提供了服务器端和客户端支持,服务器端统一管理所有配置文件,客户端在启动时从服务端获取配置信息.服务器端有多种配置方式,如将配置文件 ...

  10. C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻

    前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...

随机推荐

  1. mysql8之坑

    一.具体"坑" 1.修改密码和修改加密方式 mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码' PASSWORD ...

  2. Linux 内核虚拟地址到物理地址转换讨论【转】

    转自:https://blog.csdn.net/sunlei0625/article/details/59476987 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请 ...

  3. java8 的files、path类相关文件遍历API

    Path的两种初始化(应该还有别的方式) Path file = new File(path).toPath(); Paths.get 判断是文件.是目录 Files.isRegularFile(fi ...

  4. limit的优化

    SELECT * FROM t_fly WHERE fly_id IN (8888,1,24,6666); 查询速度很快,对于一些过万数据的查询,mysql也能轻松的查询出来

  5. XOR加密作业

    思路 -1.对需要加密的内容进行MD5加密 -2.随机生产32位的十六进制密钥 -3.对密钥和MD5加密内容进行异或运算. 主要问题: -1.如何实现MD5加密 -2.如何随机生成32位16进制密钥 ...

  6. 01. Go 语言简介

    Go语言简介 引用原文地址:http://m.biancheng.net/golang/ Go语言也称 Golang,兼具效率.性能.安全.健壮等特性.这套Go语言教程(Golang教程)通俗易懂,深 ...

  7. NLP中的数据增强

    相关方法合集见:https://github.com/quincyliang/nlp-data-augmentation 较为简单的数据增强的方法见论文:https://arxiv.org/pdf/1 ...

  8. 基于Django的Rest Framework框架的分页组件

    本文目录 一 简单分页(查看第n页,每页显示n条) 二 偏移分页(在第n个位置,向后查看n条数据) 三 CursorPagination(加密分页,只能看上一页和下一页,速度快) 回到目录 一 简单分 ...

  9. 【JS】JS校验密码复杂度(必须包含字母、数字、特殊符号)

    #场景一:密码中必须包含大小写 字母.数字.特称字符,至少8个字符,最多30个字符: var pwdRegex = new RegExp('(?=.*[0-9])(?=.*[A-Z])(?=.*[a- ...

  10. 1+x 证书 Web 前端开发中级理论考试(试卷 6 )

    1+x 证书 Web 前端开发中级理论考试(试卷 6 ) 官方QQ群 1+x 证书 web 前端开发初级对应课程分析 http://blog.zh66.club/index.php/archives/ ...