Spring Cloud-Ribbon实现客户端的服务均衡(三)
客户端负载均衡与服务端负载均衡
服务端负载均衡
通过服务端负载均衡设备维护的服务清单根据算法(轮训 权重负载 流量负载)取出服务地址 进行转发
客户端负载
将指定服务的服务清单订单(注册中心)下来 在客户端根据算法取出服务地址进行请求
Ribbon实现客户端负载均衡
rabbon是通过代理RestTemplate来实现负载均衡的 只需要在application配置并引入pom文件
同时还有从注册中心订阅服务的相关配置
//LoadBalanced 通过代理RestTemplate 实现客户端负载均衡的能力
@LoadBalanced
@Bean
RestTemplate restTemplate(){
return new RestTemplate();
}
<!--ribbon客户端的负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
RestTemplate使用
Get请求
String str=restTemplate.getForEntity("http://PROVIDER/hello",String.class).getBody();
//url传递参数
User user =restTemplate.getForEntity("http://PROVIDER/hello/{1}", User.class,1).getBody();
//多参数
Map<String,Object> paramters=new HashMap<String,Object>();
paramters.put("name","小明");
paramters.put("age",12);
User user=restTemplate.getForEntity("http://PROVIDER/hello?name={name}&id={id}",User.class,paramters).getBody();
或者使用getForObject 可以理解成getForEntity的封装
String str=restTemplate.getForObject("http://PROVIDER/hello",String.class);
//url传递参数
User user =restTemplate.getForObject("http://PROVIDER/hello/{1}", User.class,1);
//多参数
Map<String,Object> paramters=new HashMap<String,Object>();
paramters.put("name","小明");
paramters.put("age",12);
User user=restTemplate.getForObject("http://PROVIDER/hello?name={name}&id={id}",User.class,paramters);
Post请求
User parameter=new User();
//返回string
String str=restTemplate.postForEntity("http://PROVIDER/hello",parameter,String.class).getBody();
Map<String,Object> paramters=new HashMap<String,Object>();
//url传递参数
User user =restTemplate.postForEntity("http://PROVIDER/hello/{1}",parameter, User.class,1).getBody();
//多参数
Map<String,Object> paramters=new HashMap<String,Object>();
paramters.put("name","小明");
paramters.put("age",12);
User user=restTemplate.postForEntity("http://PROVIDER/hello?name={name}&id={id}",parameter,User.class,paramters).getBody(); User parameter=new User();
//返回string
String str=restTemplate.postForObject("http://PROVIDER/hello",parameter,String.class);
Map<String,Object> paramters=new HashMap<String,Object>();
//url传递参数
User user =restTemplate.postForObject("http://PROVIDER/hello/{1}",parameter, User.class,1);
//多参数
Map<String,Object> paramters=new HashMap<String,Object>();
paramters.put("name","小明");
paramters.put("age",12);
User user=restTemplate.postForObject("http://PROVIDER/hello?name={name}&id={id}",parameter,User.class,paramters);
Put Delete
与上面类似
ribbon负载均衡实现原理
LoadBalancerClient是rabbon的一个重要接口
execute 从负载均衡器中挑选一个实例执行请求
reconstructURL负责重构url restTemplate.getForEntity("http://PROVIDER/hello",String.class) 将PROVIDER转换为 host:port形式
choose从负载均衡器中选择一个实例
LoadBalancerAutoConfiguration
ribbon自动化配置类
@Configuration
@ConditionalOnClass(RestTemplate.class) //RestTemplate 必须存在当前环境中
@ConditionalOnBean(LoadBalancerClient.class)//LoadBalancerClient 必须存在当前环境中
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
/**
* 维护一个被 @LoadBalanced 标识的 RestTemplate对象 通过RestTemplateCustomizer增加LoadBalancerInterceptor拦截器
* @param restTemplateCustomizers
* @return
*/
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}
.......
@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
/**
* LoadBalancerInterceptor 用于实现对客户端请求进行拦截 实现负载均衡
* @param loadBalancerClient
* @param requestFactory
* @return
*/
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
/**
* 用于对RestTemplate增加 LoadBalancerInterceptor拦截器
* @param loadBalancerInterceptor
* @return
*/
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
} }
自动化配置类主要做以下几件事
1.LoadBalancerInterceptor 初始化一个拦截器对象 主要用于拦截器RestTemplate请求 实现负载均衡
2.初始化RestTemplateCustomizer 用于给RestTemplate增加LoadBalancerInterceptor拦截器
3.通过loadBalancedRestTemplateInitializerDeprecated 加载@LoadBalanced 标识的 RestTemplate对象 通过RestTemplateCustomizer增加LoadBalancerInterceptor拦截器
LoadBalancerinterceptor类
/**
* 拦截通过LoadBalancer注解修饰的Restempate的请求信息
*/
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));
}
/**
* 对restempate请求进行拦截
* @param request
* @param body
* @param execution
* @return
* @throws IOException
*/
@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);
//通过注入LoadBalancerClient 实现负载均衡 requset封装了restrempate的请求信息
return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
}
}
LoadBalancerinterceptor拦截@LoadBalanced 标识的 RestTemplate对象发送的请求 并交给LoadBalancerClient的实现类的execute方法处理
RibbonLoadBalancerClient
public class RibbonLoadBalancerClient implements LoadBalancerClient { public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
//serviceId此时已经是服务名字如前面demoPROVIDER
return this.execute(serviceId, (LoadBalancerRequest)request, (Object)null);
} public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
//通过ILoadBalancer 根据ServiceId获得具体服务实例
Server server = this.getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
} else {
RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
}
}
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
//根据loadBalancer 获得服务实例
return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");
}
protected ILoadBalancer getLoadBalancer(String serviceId) {
return this.clientFactory.getLoadBalancer(serviceId);
} public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
Server server = null;
if (serviceInstance instanceof RibbonLoadBalancerClient.RibbonServer) {
server = ((RibbonLoadBalancerClient.RibbonServer)serviceInstance).getServer();
} if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
} else {
RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server); try {
T returnVal = request.apply(serviceInstance);
statsRecorder.recordStats(returnVal);
return returnVal;
} catch (IOException var8) {
statsRecorder.recordStats(var8);
throw var8;
} catch (Exception var9) {
statsRecorder.recordStats(var9);
ReflectionUtils.rethrowRuntimeException(var9);
return null;
}
}
}
}
最终是通过loadBalancer.chooseServe(serverId) 来获得具体实例 而不是
ILoadBalancer
public interface ILoadBalancer {
//向负载均衡器中维护的实例列表增加服务实例。
public void addServers(List<Server> newServers);
//通过某种策略, 从负载均衡器中挑选出 一 个具体的服务实例
public Server chooseServer(Object key);
//用来通知和标识负载均衡器中某个具体实例已经停止服务, 不
//然负载均衡器在下 一 次获取服务实例清单前都会认为服务实例均是正常服务的。
public void markServerDown(Server server); // 获取服务列表 已过期 已过时
@Deprecated
public List<Server> getServerList(boolean availableOnly);
//获取当前正常服务的实例列表。
public List<Server> getReachableServers();
//获取所有已知的服务实例列表, 包括正常服务和停止服务的实例
public List<Server> getAllServers();
}
BaseLoadBalancer对负载均衡做了基本的实现
DynamicServerListLoadBalancer继承BaseLoadBalanceer 做了扩展
ZoneAwareLoadBalancer继承DynamicServerListLoadBalancer 在原有的基础上做了扩展
@Configuration
@EnableConfigurationProperties
@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration { @ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerList<Server> serverList, ServerListFilter<Server> serverListFilter, IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
return (ILoadBalancer)(this.propertiesFactory.isSet(ILoadBalancer.class, this.name) ? (ILoadBalancer)this.propertiesFactory.get(ILoadBalancer.class, config, this.name) : new ZoneAwareLoadBalancer(config, rule, ping, serverList, serverListFilter, serverListUpdater));
} }
通过RibbonClientConfiguration 可以看出默认是使用ZoneAwareLoadBalancer
根据ZoneAwareLoadBalancer负载均衡策略获得server后 继续调用RibbonLoadBalancerClient.execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request)
public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
Server server = null;
if (serviceInstance instanceof RibbonLoadBalancerClient.RibbonServer) {
server = ((RibbonLoadBalancerClient.RibbonServer)serviceInstance).getServer();
} if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
} else {
RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server); try {
//调用拦截器传入LoadBalancerRequest回调apply 这个时候serviceInstance封装了host信息端口等信息
T returnVal = request.apply(serviceInstance);
statsRecorder.recordStats(returnVal);
return returnVal;
} catch (IOException var8) {
statsRecorder.recordStats(var8);
throw var8;
} catch (Exception var9) {
statsRecorder.recordStats(var9);
ReflectionUtils.rethrowRuntimeException(var9);
return null;
}
}
}
serviceInstance接口定义
public interface ServiceInstance {
default String getInstanceId() {
return null;
} String getServiceId(); String getHost(); int getPort(); boolean isSecure(); URI getUri(); Map<String, String> getMetadata(); default String getScheme() {
return null;
}
}
当拿到实例最终会根据RibbonLoadBalancerClient.reconstructURI 将服务组织正常url形式并发起请求
Spring Cloud-Ribbon实现客户端的服务均衡(三)的更多相关文章
- 服务注册发现Eureka之三:Spring Cloud Ribbon实现客户端负载均衡(客户端负载均衡Ribbon之三:使用Ribbon实现客户端的均衡负载)
在使用RestTemplate来消费spring boot的Restful服务示例中,我们提到,调用spring boot服务的时候,需要将服务的URL写死或者是写在配置文件中,但这两种方式,无论哪一 ...
- Spring Cloud Ribbon源码分析---负载均衡实现
上一篇结合 Eureka 和 Ribbon 搭建了服务注册中心,利用Ribbon实现了可配置负载均衡的服务调用.这一篇我们来分析Ribbon实现负载均衡的过程. 从 @LoadBalanced入手 还 ...
- Spring Cloud Ribbon实现客户端负载均衡
1.构建microservice-consumer-movie-ribbon项目,在pom.xml中引入ribbon依赖 在引入Eureka依赖的时候,默认里面含有ribbon依赖 2.添加@Load ...
- Spring Cloud Ribbon 源码分析---负载均衡算法
上一篇分析了Ribbon如何发送出去一个自带负载均衡效果的HTTP请求,本节就重点分析各个算法都是如何实现. 负载均衡整体是从IRule进去的: public interface IRule{ /* ...
- 为Spring Cloud Ribbon配置请求重试(Camden.SR2+)
当我们使用Spring Cloud Ribbon实现客户端负载均衡的时候,通常都会利用@LoadBalanced来让RestTemplate具备客户端负载功能,从而实现面向服务名的接口访问. 下面的例 ...
- Spring Cloud微服务笔记(四)客户端负载均衡:Spring Cloud Ribbon
客户端负载均衡:Spring Cloud Ribbon 一.负载均衡概念 负载均衡在系统架构中是一个非常重要,并且是不得不去实施的内容.因为负载均衡对系统的高可用性. 网络压力的缓解和处理能力的扩容的 ...
- 笔记:Spring Cloud Ribbon 客户端负载均衡
Spring Cloud Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,基于 Netflix Ribbon 实现,通过Spring Cloud 的封装,可以让我们轻松的将面向服 ...
- 基于Spring cloud Ribbon和Eureka实现客户端负载均衡
前言 本案例将基于Spring cloud Ribbon和Eureka实现客户端负载均衡,其中Ribbon用于实现客户端负载均衡,Eureka主要是用于服务注册及发现: 传统的服务端负载均衡 常见的服 ...
- 第四章 客户端负载均衡:Spring Cloud Ribbon
spring cloud ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于Netflix Ribbon 实现.通过Spring Cloud 的封装,可以轻松的将面向服务的R ...
随机推荐
- UI设计--->全心全意为人民服务的宗旨---->注重客户体验--->软件持久的生命力
UI即User Interface(用户界面)的简称. UI设计是指对软件的人机交互.操作逻辑.界面美观的总体设计. 好的UI设计不仅是让软件变得有个性有品味,还要让软件的操作变得舒适简单.自由.充分 ...
- 从头认识java-15.2 Collection的经常用法(2)-注意点
上一章节我们讲述了Collection的经常用法.还有之前的章节我们介绍了Collection的addAll方法,这一章节我们介绍一下它的注意点. 注意点就是,在经常用法里面,基本都是可选操作. 什么 ...
- android 添加一个按键键值【转】
本文转载自:http://blog.csdn.net/u012719256/article/details/52526046 1.frameworks/base/data/keyboards/Gene ...
- Get Length 使用dynamic关键字
http://www.codewars.com/kata/566f571ed78037c7b6000036/train/csharp You don't have any idea what is t ...
- Android热更新实现原理
最近Android社区的氛围很不错嘛,连续放出一系列的android动态加载插件和热更新库,这篇文章就来介绍一下Android中实现热更新的原理. ClassLoader 我们知道Java在运行时加载 ...
- Codeforces--626B--Cards(模拟)
Cards Time Limit: 2000MS Memory Limit: 262144KB 64bit IO Format: %I64d & %I64u Submit Sta ...
- 【POJ 3635】 Full Tank
[题目链接] http://poj.org/problem?id=3635 [算法] 优先队列BFS 实现类似于堆优化dijkstra [代码] #include <algorithm> ...
- PCB Genesis增加点阵字 实现原理
我们采用Genesis增加点阵字时,用Genesis增加Canned Text即可,但奥宝中文不支持,且字符种类是有限的呀 不过没关系,没有自己造呀.在这里我分享一种增加点阵字的实现方法 一.通过代码 ...
- js form settimeout
<html><head><meta charset="utf8"><script type="text/javascript&q ...
- SfMLearner论文笔记——Unsupervised Learning of Depth and Ego-Motion from Video
1. Abstract 提出了一种无监督单目深度估计和相机运动估计的框架 利用视觉合成作为监督信息,使用端到端的方式学习 网络分为两部分(严格意义上是三个) 单目深度估计 多视图姿态估计 解释性网络( ...