客户端负载均衡与服务端负载均衡

服务端负载均衡

通过服务端负载均衡设备维护的服务清单根据算法(轮训 权重负载 流量负载)取出服务地址 进行转发

客户端负载

将指定服务的服务清单订单(注册中心)下来 在客户端根据算法取出服务地址进行请求

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实现客户端的服务均衡(三)的更多相关文章

  1. 服务注册发现Eureka之三:Spring Cloud Ribbon实现客户端负载均衡(客户端负载均衡Ribbon之三:使用Ribbon实现客户端的均衡负载)

    在使用RestTemplate来消费spring boot的Restful服务示例中,我们提到,调用spring boot服务的时候,需要将服务的URL写死或者是写在配置文件中,但这两种方式,无论哪一 ...

  2. Spring Cloud Ribbon源码分析---负载均衡实现

    上一篇结合 Eureka 和 Ribbon 搭建了服务注册中心,利用Ribbon实现了可配置负载均衡的服务调用.这一篇我们来分析Ribbon实现负载均衡的过程. 从 @LoadBalanced入手 还 ...

  3. Spring Cloud Ribbon实现客户端负载均衡

    1.构建microservice-consumer-movie-ribbon项目,在pom.xml中引入ribbon依赖 在引入Eureka依赖的时候,默认里面含有ribbon依赖 2.添加@Load ...

  4. Spring Cloud Ribbon 源码分析---负载均衡算法

    上一篇分析了Ribbon如何发送出去一个自带负载均衡效果的HTTP请求,本节就重点分析各个算法都是如何实现. 负载均衡整体是从IRule进去的: public interface IRule{ /* ...

  5. 为Spring Cloud Ribbon配置请求重试(Camden.SR2+)

    当我们使用Spring Cloud Ribbon实现客户端负载均衡的时候,通常都会利用@LoadBalanced来让RestTemplate具备客户端负载功能,从而实现面向服务名的接口访问. 下面的例 ...

  6. Spring Cloud微服务笔记(四)客户端负载均衡:Spring Cloud Ribbon

    客户端负载均衡:Spring Cloud Ribbon 一.负载均衡概念 负载均衡在系统架构中是一个非常重要,并且是不得不去实施的内容.因为负载均衡对系统的高可用性. 网络压力的缓解和处理能力的扩容的 ...

  7. 笔记:Spring Cloud Ribbon 客户端负载均衡

    Spring Cloud Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,基于 Netflix Ribbon 实现,通过Spring Cloud 的封装,可以让我们轻松的将面向服 ...

  8. 基于Spring cloud Ribbon和Eureka实现客户端负载均衡

    前言 本案例将基于Spring cloud Ribbon和Eureka实现客户端负载均衡,其中Ribbon用于实现客户端负载均衡,Eureka主要是用于服务注册及发现: 传统的服务端负载均衡 常见的服 ...

  9. 第四章 客户端负载均衡:Spring Cloud Ribbon

    spring cloud ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于Netflix Ribbon 实现.通过Spring Cloud 的封装,可以轻松的将面向服务的R ...

随机推荐

  1. Android之自己定义(上方标题随ViewPager手势慢慢滑动)

    近期非常蛋疼,项目要模仿网易新闻的样式去做.上次把仿网易新闻client的下拉刷新写出来了.这次是ViewPager的滑动,同一时候ViewPager的上面标题下划线尾随者移动.本来通过ViewPag ...

  2. uboot 解压缩

    在uboot中进行解压缩是非常实用的 uboot中完毕delay 用户进行交互的段 if(BootType == '3') { char *argv[3]; printf("   \n3: ...

  3. elasticsearch _field_stats 源码分析

    _field_stats 实现的功能:https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-field-stats.ht ...

  4. 【POJ 2449】 Remmarguts' Date

    [题目链接] http://poj.org/problem?id=2449 [算法] A*(启发式搜索) 首先,求第k短路可以用优先队列BFS实现,当T第k次入队时,就求得了第k短路,但是,这种做法的 ...

  5. Java项目打包发布

    Java项目打包发布 如果只想发布为一个可执行的jar包,使用eclipse的Export功能就可以了 使用eclipse的Export功能,将项目中的所有package打包为一个pet.jar文件, ...

  6. unity3D 使用欧拉角控制视野注意点

    变量声明: public PlayerInput p; //表示控制代码用来获得用户是否按下 public float rotateSpeed = 50f; //旋转速度 private GameOb ...

  7. J2EE框架(Struts&Hibernate&Spring)的理解

    SSH:Struts(表示层)+Spring(业务层)+Hibernate(持久层)Struts:Struts是一个表示层框架,主要作用是界面展示,接收请求,分发请求.在MVC框架中,Struts属于 ...

  8. 【python】os.getcwd和getcwdu

    print os.getcwd(), type(os.getcwd()) print os.getcwdu(), type(os.getcwdu()) 结果如下: C:\Users\Administr ...

  9. 【原创】redhat5安装oracle10g

    安装缺失的包: 用 root 用户身份运行以下命令: rpm -q gcc make binutils openmotif setarch compat-db compat-gcc compat-gc ...

  10. scala类型系统:24) 理解 higher-kinded-type

    首先我们从最基本的泛型来看: 现在我们对上面泛型中的类型参数再进一步,也是个泛型会如何呢? 可以看到,java中不支持类型参数也是泛型类型的情况,而scala支持.这是一个很重要的区别,scala在类 ...