SpringCloud学习笔记(四、SpringCloud Netflix Ribbon)
目录:
- 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)的更多相关文章
- SpringCloud学习笔记(2):使用Ribbon负载均衡
简介 Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具,在注册中心对Ribbon客户端进行注册后,Ribbon可以基于某种负载均衡算法,如轮询(默认 ...
- SpringCloud学习笔记:负载均衡Ribbon(3)
1. RestTemplate简介 RestTemplate是Spring Resource中一个访问第三方RESTful API接口的网络请求框架. RestTemplate是用来消费REST服务的 ...
- SpringCloud学习笔记:SpringCloud简介(1)
1. 微服务 微服务具有的特点: ◊ 按照业务划分服务 ◊ 每个微服务都有独立的基础组件,如:数据库.缓存等,且运行在独立的进程中: ◊ 微服务之间的通讯通过HTTP协议或者消息组件,具有容错能力: ...
- SpringCloud学习笔记(3):使用Feign实现声明式服务调用
简介 Feign是一个声明式的Web Service客户端,它简化了Web服务客户端的编写操作,相对于Ribbon+RestTemplate的方式,开发者只需通过简单的接口和注解来调用HTTP API ...
- SpringCloud学习笔记(4):Hystrix容错机制
简介 在微服务架构中,微服务之间的依赖关系错综复杂,难免的某些服务会出现故障,导致服务调用方出现远程调度的线程阻塞.在高负载的场景下,如果不做任何处理,可能会引起级联故障,导致服务调用方的资源耗尽甚至 ...
- SpringCloud学习笔记(5):Hystrix Dashboard可视化监控数据
简介 上篇文章中讲了使用Hystrix实现容错,除此之外,Hystrix还提供了近乎实时的监控.本文将介绍如何进行服务监控以及使用Hystrix Dashboard来让监控数据图形化. 项目介绍 sc ...
- SpringCloud学习笔记(6):使用Zuul构建服务网关
简介 Zuul是Netflix提供的一个开源的API网关服务器,SpringCloud对Zuul进行了整合和增强.服务网关Zuul聚合了所有微服务接口,并统一对外暴露,外部客户端只需与服务网关交互即可 ...
- SpringCloud学习笔记:服务支撑组件
SpringCloud学习笔记:服务支撑组件 服务支撑组件 在微服务的演进过程中,为了最大化利用微服务的优势,保障系统的高可用性,需要通过一些服务支撑组件来协助服务间有效的协作.各个服务支撑组件的原理 ...
- SpringCloud学习笔记(7):使用Spring Cloud Config配置中心
简介 Spring Cloud Config为分布式系统中的外部化配置提供了服务器端和客户端支持,服务器端统一管理所有配置文件,客户端在启动时从服务端获取配置信息.服务器端有多种配置方式,如将配置文件 ...
- C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻
前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...
随机推荐
- 使用 vs code 创建 Django 项目
操作流程: 1.前期准备工作 2.vs code配置Python环境 3.新建 Django 项目 4.vs code 配置 Debug Django 环境 5.浏览器查看效果 1.前期准备工作 安装 ...
- linux内核的冷热页分配器
先说说cpu的cache,和cpu的cache比起来访问主内存是非常慢的,为了加快速度根据本地性原则,cpu在访问主内存的时候会把附近的一块数据都加载到cpu的cache里,之后读写这块数据都是在ca ...
- Java使用JDBC连接SQL Server数据库|实现学生成绩信息系统
Java实验四 JDBC 使用SQL Server数据库或者MySQL数据库各自的客户端工具,完成如下任务: (1)创建数据库students: (2)在数据students中创建表scores,包括 ...
- 201871010113-刘兴瑞《面向对象程序设计(java)》第十周学习总结
项目 内容 这个作业属于哪个课程 <任课教师博客主页链接> https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 <作业链接地址>htt ...
- 小程序-引用的两种方式:import和include
import import可以在该文件中使用目标文件定义的template,如: 在mine.wxml中定义了一个叫item的template: <template name="ite ...
- flex——justify-content属性引起的一个样式问题
前言 在flex布局出现以前,我一般习惯使用浮动布局(float)来实现下列布局 现在尽量少用浮动布局,虽然好用,但有时会带来一些意想不到的问题,甚至导致布局错位, 一开始浮动布局只是为了 ...
- sql慢查询工具(配置代码)
# 在mysql的配置文件/etc/mysql/mysql.conf.d/mysqld.cnf[mysqld]中配置懒查询 slow_query_log = ON # 是否已经开启慢查询 long_q ...
- MySQL中的group_concat函数的使用
本文通过实例介绍了MySQL中的group_concat函数的使用方法,比如select group_concat(name) . MySQL中group_concat函数 完整的语法如下: grou ...
- Annotation-specified bean name 'userDaoImpl' for bean class [***] conflicts with existing, non-compatible bean definition of same name and class [***]
使用Spring开发的时候报错如下: Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionExcept ...
- log4net的配置及使用
网上查了有很多种写法和配置,结果百度出来都是几种方法混合写法,拷在一起结果还不能正常运行.因此把自己做成功的代码写上来做个备份. 运行环境:log4net 2.03版本,.net 4.5 大体步骤为: ...