一、什么是负载均衡?

做web开发都会接触到负载均衡,这里我们就不细说了。

(摘自百度百科)负载均衡,英文名称为Load Balance,其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器等,从而协同完成工作任务。

负载均衡主要分为软件负载和硬件负载,在微服务盛行的现在,软件负载在微服务里成为主流,netflix的ribbon就是其中之一

二、负载均衡要干什么事情?

我们这里只关注软件负载,硬件负载略过。软件负载也分为:

服务端负载(服务端发现模式)、客户端负载(客户端发现模式)(概念回顾我们前文《服务发现之eureka》

服务端负载

需要做三个事情:

1.接收请求

2.选择服务器地址

3.转发/执行请求(转发还是执行可以参考网关)

这里我们一笔带过,因为不是本文的重点

客户端负载

本文的重点,也是ribbon的实现方式:

如果让我们自己做一个软负载,试想一下怎么去做?

接收请求肯定是对应微服务自己的controller或者rpc服务接收请求

然后负载均衡这里负责:

1.选择服务器地址

2.发请求

选择服务器地址这里倒没什么问题,但是发请求是用httpclient还是resttemplate?由谁定?服务消费者自己定,还是负载均衡器定,服务消费者除了使用负载均衡器请求,有可能也可以直接发请求给服务提供者吧?

作为一个中间件,ribbon其实给到的答案也是由服务消费者自己去定义。

负载均衡器

负载均衡其实是一个很抽象的说法,为了让他更加形象化,我们抽象出一个可以量化的名词,负载均衡器:

1.每个服务提供者一个负载均衡器,还是所有服务提供者公用一个负载均衡器?

2.每个服务提供者的每个接口能否使用不同的负载均衡器?(dubbo就可以?)

ribbon给到的答案是:每个服务提供者(多节点)一个负载均衡器,负载均衡器之间互相独立且互不干扰

那么我们得出如下角色职责分工:

细心的观众会发现,为什么负载均衡器多了一个职责:记录请求统计信息?

这是因为负载均衡有的负载规则需要根据请求统计信息来决定选择哪个服务器,例如WeightedResponseTimeRule,根据平均请求响应时间来选择合适的服务器

我们再来看看ribbon官方是怎么定义的:

Components of load balancer(摘自netflix ribbon wiki

Rule - a logic component to determine which server to return from a list

Ping - a component running in background to ensure liveness of servers

ServerList - this can be static or dynamic. If it is dynamic (as used by DynamicServerListLoadBalancer), a background thread will refresh and filter the list at certain interval

负载均衡器有三大组件:

1.负载规则  ,从服务器列表中决定用哪个服务器

2.ping任务  ,后台运行的任务,用来验证服务器是否可用

3.服务器列表   ,可以是静态也可以是动态,如果是动态,那么就要有一个后台线程定时去刷新和过滤列表。我们微服务基于服务发现的情况,服务器列表肯定都是动态增减的,而且ribbon都是配套eureka使用(也可以单独使用,但我们这里不去研究场景)

发现跟我们的想发还是有出入的,服务器列表我们肯定是有的,不然没法选择,主要还是多了一个ping任务

把我们上面的想法整合一下,得到如下架构图:

上图大家肯定有很多疑惑,不要慌,下面我们再来一一分析:

1.定时获取ServerList,去哪取?

2.定时执行ping任务,怎么ping

3.ServerList过滤,为什么要过滤,过滤什么?

其实结合我们使用ribbon都是结合eureka,单独使用的场景其实基本没有,所以我们这里主要关注结合eureka如何使用即可

那么结合eureka服务发现,上面的很多问题都迎刃而解

1.定时获取ServerList,去哪取?【去eureka client取】

2.定时执行ping任务,怎么ping【eureka client有定时从server刷新服务列表(30s频率),我们再去ping的话感觉没太大必要,所以结合eureka的话,我们直接拿来用就行了】

3.ServerList过滤,为什么要过滤,过滤什么?【这个我们后面会揭晓】

三、如何将Http Client发送请求与ribbon负载均衡器进行融合?

如果不做融合这个事情,我们的代码可能就是

1.获取服务器地址【负载均衡器】

2.发送请求【服务消费者】

3.将请求耗时等信息记录到负载均衡器【负载均衡器】

主要分为如上三步,这样我们又会面临操作jdbc类似的问题,每个开发写出来的代码都可能会不一样,严重的可能还会有bug

所以类似spring jdbctemplate,ribbon也想到用类似模板来解决这个事情:

为什么负载均衡器对外提供方法只有get,没有set?其实这里统计信息是引用类型,get到了之后做修改,地址不变值改变,所以没有问题,这里我们不纠结

伪代码如下:

AbstractLoadBalancerAwareClient{
  public void executeWithLoadBalancer(){
    selectServer()
    this.execute()//【交由子类实现】
    recordstats()
  }
}

这样的话我们就能够控制动作一致,结果不一致(执行请求的细节交给子类,父类只管选择服务器、记录请求结果信息,将地址交给子类自己去使用)

所以netflix ribbon-loadbalancer包也是主要分为两块:负载均衡器(loadbalancer包)、客户端模板(client包)

feign结合ribbon

再进一步举例,spring-cloud-openfeign是怎么将feign与ribbon结合使用的?(右键新标签打开可查看大图)

其实就是我们刚刚说的,继承模板,只需要实现execute方法决定怎么发送请求即可,其他的交由模板去处理

四、ribbon的懒加载策略

准确来说,是spring-cloud-netflix-ribbon的懒加载策略,是spring cloud基于ribbon进行包装而来的,其最终目的还是为了给每个服务提供者提供各自独立的个性化配置,懒加载只是其中的实现过程

每个ribbon负载均衡器可以个性化配置的内容有哪些:

可参考spring-cloud-netflix-ribbon里

public SpringClientFactory() {
super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
}

RibbonClientConfiguration

默认所有的负载均衡器都使用如下默认的实现

①DefaultClientConfigImpl里的属性值如何换成自己的配置?具体有哪些配置?

service-hi.ribbon.ReadTimeout=339
<clientName>.<nameSpace>.<propertyName>=<value>

写在配置文件里即可,这里就能读到每个服务自己个性化的配置

如果想所有服务统一使用配置,则把服务名去掉即可

ribbon.MaxAutoRetries=100

如果想更换namespace也可以,将DefaultClientConfigImpl Bean换成自己的实现类即可

public class MyClientConfig extends DefaultClientConfigImpl {
// ...
public String getNameSpace() {
return "foo";
}
}

具体有哪些配置详见DefaultClientConfigImpl

②ZonePreferenceServerListFilter里的属性 zone

this.zone = ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone);

这里的属性值,由spring-cloud-netflix-eureka-client的 EurekaRibbonClientConfiguration

@PostConstruct set进去,先取

ConfigurationManager.getDeploymentContext().setValue(ContextKey.zone,availabilityZone);

优先顺序如下

eureka.instance.metadataMap.zone = zone2 //不支持逗号分割
eureka.client.availability-zones.gz=zone2,zone1 //逗号分割后第一个,注意是只取当前配置region下的az

ribbon的配置有四种(优先级由高到低)

1.每个服务提供者自己个性化的配置 @RibbonClient

@RibbonClient(value="service-hi",configuration= ServiceHiRibbonConfiguration.class)

针对每个服务提供者自行配置,可配置的内容见上面的列表,举例如下:

//不需要@Configution注解
public class ServiceHiRibbonConfiguration {
@Bean
public IRule iRule(){
return new ZoneAvoidanceRule();
}
}

2.全局配置 @RibbonsClient

如果有引用spring-cloud-netflix-eureka-client就是走的全局配置
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration

@RibbonClients(defaultConfiguration = EurekaRibbonClientConfiguration.class)
public class RibbonEurekaAutoConfiguration {
}
@Configuration
public class EurekaRibbonClientConfiguration {
  @Bean
  @ConditionalOnMissingBean
  public IPing ribbonPing(IClientConfig config) {
  ...

自己也可以加全局配置,但是要注意添加@Order注解来控制bean的优先级,否则可能因为优先级低被覆盖,举例:
@RibbonClients(defaultConfiguration=MyRibbonDefaultConfiguration.class) ,全局配置,配置文件同上

//不需要@Configuration注解
public class MyRibbonDefaultConfiguration {
@Bean
public IRule iRule(){
return new ZoneAvoidanceRule();
}
}

另外需要注意@RibbonClients注解除了可以定义全局的配置,也可以给每个服务提供者配置

@RibbonClients(value = {
@RibbonClient(value="service-hi",configuration = ServiceHiRibbonConfiguration.class),
@RibbonClient(value="service-order",configuration = ServiceOrderRibbonConfiguration.class)
},defaultConfiguration = RibbonDefaultConfiration.class)

即没有自己配置的走全局配置,自己配置了的走自己的配置

3.默认配置 RibbonClientConfiguration

@Autowired(required = false)
private List<RibbonClientSpecification> configurations = new ArrayList<>();
@Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
public SpringClientFactory() {
super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
}

@RibbonClient 和@RibbonClients都是注册BeanClass=RibbonClientSpecification 的spring bean
然后springclientfactory最后统统收到自己的 configurations属性里
最后获取配置的时候,通过如上的优先级顺序去注册和取对应的bean即可
SpringClientFactory extends NamedContextFactory

public abstract class NamedContextFactory
implements DisposableBean, ApplicationContextAware{
@Override
public void setApplicationContext(ApplicationContext parent) throws BeansException {
this.parent = parent;
}
protected AnnotationConfigApplicationContext createContext(String name) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
if (this.configurations.containsKey(name)) {
for (Class<?> configuration : this.configurations.get(name)
.getConfiguration()) {
context.register(configuration);
}
}
for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
if (entry.getKey().startsWith("default.")) {
for (Class<?> configuration : entry.getValue().getConfiguration()) {
context.register(configuration);
}
}
}
context.register(PropertyPlaceholderAutoConfiguration.class,
this.defaultConfigType);
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
this.propertySourceName,
Collections.<String, Object> singletonMap(this.propertyName, name)));
if (this.parent != null) {
// Uses Environment from parent as well as beans
context.setParent(this.parent);
}
context.setDisplayName(generateDisplayName(name));
context.refresh();
return context;
}

4.spring context里的bean

如上代码,NamedContextFactory继承了ApplicationContextAware,取bean时最后会把ApplicationContext作为parent注册进去,这样spring 扫描到的所有bean都会被取到
context.setParent(this.parent);

这里需要注意(摘自spring cloud netflix ribbon
@RibbonClient(name = "custom", configuration = CustomConfiguration.class)
The CustomConfiguration clas must be a @Configuration class, but take care that it is not in a @ComponentScan for the main application context. Otherwise, it is shared by all the @RibbonClients. If you use @ComponentScan (or @SpringBootApplication), you need to take steps to avoid it being included (for instance, you can put it in a separate, non-overlapping package or specify the packages to scan explicitly in the @ComponentScan).

如果每个服务提供者自己个性化的配置,要注意configuration文件不要被spring扫描到,否则会被注册到spring bean,这样SpringClientFactory getBean时会拿到,这样就会影响到其他服务提供者(如果优先级是最后倒也还好,但是如果有使用@Order把优先级提高的话,就会造成影响)

其实经过测试,spring cloud Finchley.RELEASE版本,spring-cloud-netflix-ribbon 2.0.0.RELEASE版本 下,CustomConfiguration.class 是不需要@Configuration注解也可以正常使用,这样就可以避免上面的问题发生(spring-cloud-openfeign的个性化配置一样可以,且官方文档也已经明确提到CustomConfiguration可以不需要@Configuration注解)

ribbon也支持饿汉

不过好像没什么应用场景?,详见:
RibbonAutoConfiguration

@Bean
@ConditionalOnProperty(value = "ribbon.eager-load.enabled")
public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
return new RibbonApplicationContextInitializer(springClientFactory(),
ribbonEagerLoadProperties.getClients());
}

通过如下配置即可,应用启动就会自动注册相关的bean到SpringClientFactory的contexts map里

ribbon.eager-load.clients=service-hi
ribbon.eager-load.clients=service-order

五、结合region AZ要干些什么事情?

region AZ的概念详见我们前文《服务发现之eureka》,我们的决策肯定是:优先使用相同zone的服务器
所以ServerList过滤时,只保留当前AZ的服务器,其他zone的过滤掉即可(如果当前AZ没有服务器地址,则保留其他AZ的来使用即可)

所以我们这里才需要ServerList过滤

六、将抽象出来的对象映射到类图

连连看,将如下类图与我们架构图里对象进行对应(右键新标签打开可查看大图)

负载均衡之ribbon的更多相关文章

  1. Spring Cloud官方文档中文版-客户端负载均衡:Ribbon

    官方文档地址为:http://cloud.spring.io/spring-cloud-static/Dalston.SR2/#_spring_cloud_netflix 文中例子我做了一些测试在:h ...

  2. 【Spring Cloud】客户端负载均衡组件——Ribbon(三)

    一.负载均衡 负载均衡技术是提高系统可用性.缓解网络压力和处理能力扩容的重要手段之一. 负载均衡可以分为服务器负载均衡和客户端负载均衡,服务器负载均衡由服务器实现,客户端只需正常访问:客户端负载均衡技 ...

  3. 负载均衡框架 ribbon 二

    Ribbon 负载均衡机制 官方文档地址:https://github.com/Netflix/ribbon/wiki/Working-with-load-balancers 1. Ribbon 内置 ...

  4. SpringCloud系列之客户端负载均衡Netflix Ribbon

    1. 什么是负载均衡? 负载均衡是一种基础的网络服务,它的核心原理是按照指定的负载均衡算法,将请求分配到后端服务集群上,从而为系统提供并行处理和高可用的能力.提到负载均衡,你可能想到nginx.对于负 ...

  5. Spring Cloud之负载均衡组件Ribbon原理分析

    目录 前言 一个问题引发的思考 Ribbon的简单使用 Ribbon 原理分析 @LoadBalanced 注解 @Qualifier注解 LoadBalancerAutoConfiguration ...

  6. SpringCloud 客户端负载均衡:Ribbon

    目录 Ribbon 介绍 开启客户端负载均衡,简化 RestTemplate 调用 负载均衡策略 Ribbon 介绍 Ribbon 是 Netflix 提供的一个基于 Http 和 TCP 的客户端负 ...

  7. SpringCloud系列五:Ribbon 负载均衡(Ribbon 基本使用、Ribbon 负载均衡、自定义 Ribbon 配置、禁用 Eureka 实现 Ribbon 调用)

    1.概念:Ribbon 负载均衡 2.具体内容 现在所有的服务已经通过了 Eureka 进行了注册,那么使用 Eureka 注册的目的是希望所有的服务都统一归属到 Eureka 之中进 行处理,但是现 ...

  8. springboot10-springcloud-eureka 服务注册与发现,负载均衡客户端(ribbon,feign)调用

    创建5个项目: 1.服务注册中心 2.服务提供者1 3.服务提供者2(与服务提供者1的代码实现一样,这是是为了模拟负载均衡) 4.ribbon客户端项目 5.feign客户端项目 如图: 一.注册中心 ...

  9. 客户端负载均衡Ribbon之一:Spring Cloud Netflix负载均衡组件Ribbon介绍

    Netflix:['netfliːks] ribbon:英[ˈrɪbən]美[ˈrɪbən]n. 带; 绶带; (打印机的) 色带; 带状物;v. 把…撕成条带; 用缎带装饰; 形成带状;     L ...

  10. 2.3负载均衡:Ribbon

    基于上一篇文章的工程,启动eureka-server 工程:启动service-hi工程,它的端口为8765:将service-hi的配置文件的端口改为8763,并启动,这时你会发现:service- ...

随机推荐

  1. Android ListView性能优化实例讲解

    前言: 对于ListView,大家绝对都不会陌生,只要是做过Android开发的人,哪有不用ListView的呢? 只要是用过ListView的人,哪有不关心对它性能优化的呢? 关于如何对ListVi ...

  2. H3C 主机接收IP包

  3. Mac MAMP 使用终端shell操作mysql数据库

    在MAMP中已经集成了phpMyAdmin,可以很方便的管理mysql数据库,但是有的情况是phpMyAdmin不能做到的.比如,导入sql文件,当sql文件非常大(大于20MB)的时候,apache ...

  4. HDU 1372

    题意:模拟国际象棋马的走棋方式,和中国象棋一样马走日,8X8的棋盘,问从起点到终点的最短步数,国际象棋中数字代表行row,字母代表列column, 思路:记忆化深搜. #include<cstd ...

  5. H3C RIPv2配置举例

  6. H3C FTP其他常用命令

  7. vue 改变数据DOM不更新,获取不到DOM的解决方法

    1.获取不到DOM的解决方案(使用$nextTick) 定义:在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的 DOM. 理解:nextTick(),是将回调 ...

  8. 基于Springboot+Junit+Mockito做单元测试

    前言 前面的两篇文章讨论过< 为什么要写单元测试,何时写,写多细 >和<单元测试规范>,这篇文章介绍如何使用Springboot+Junit+Mockito做单元测试,案例选取 ...

  9. 记一次奇葩事——html5可能不支持window.onscroll函数

    只在html5里遇到,html4没事:拿出来聊聊,路过帮忙解答下!!! 不正常的 <!doctype html><html><head><meta chars ...

  10. 【u033】地震逃生

    Time Limit: 1 second Memory Limit: 64 MB [问题描述] 汶川地震发生时,四川**中学正在上课,一看地震发生,老师们立刻带领x名学生逃跑,整个学校可以抽象地看成一 ...