定制app开发微服务架构 | 4.1 基于 Ribbon 的负载均衡详解

4.1 基于 定制app开发的负载均衡详解


前言

参考资料
《Spring Microservices in Action》
《Spring Cloud Alibaba 定制app开发微服务原理与实战》
《B站 尚硅谷 SpringCloud 定制app开发框架开发教程 周阳》

Spring Cloud Ribbon 是基于 Ribbon 定制app开发定制app开发实现的一套客户端负载定制app开发定制app开发均衡的工具;定制app开发定制app开发提供客户端的软件负载定制app开发定制app开发均衡算法和服务调用;


1. Ribbon 基础知识

1.1 Ribbon 是什么

  • Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡的工具;提供客户端的软件负载均衡算法和服务调用;
  • Ribbon 定制app开发客户端组件提供一系列定制app开发完善的配置项如连接超时,重试等。简单的说,定制app开发就是在配置文件中列出 Load Balancer(简称LB)定制app开发后面所有的机器,Ribbon 定制app开发会自动基于某种规则(如简单轮询,随机连接等)去连接这些机器;
  • 可以很容易使用Ribbon实现自定义的负载均衡算法;

1.2 与 Ribbon 交互的三种级别层次

  • Spring DiscoveryClient:提供了对 Ribbon 和 Ribbon 中缓存的注册服务的最低层次访问;
  • 启用了 RestTemplate 的 Spring DiscoveryClient
  • Netflix Feign 客户端

1.3 Ribbon在工作时分成两步

  • 第一步先选择 EurekaServer,它优先选择在同一个区域内负载较少的 server;
    第二步再根据用户指定的策略,在从 server 取到的服务注册列表中选择一个地址;

1.4 服务的提供者与消费者

  • 提供者:服务提供者将自己注册进注册中心,让消费者发现;在本例中有多个提供者给消费者提供服务;
  • 消费者:消费者使用服务发现,找到提供者并调用提供者服务;在本例中只有一个消费者在多个提供者中选出一个为自己服务;

1.5 Ribbon 核心组件 IRule

  • 根据特定算法中从服务列表中选取一个要访问的服务;
  • 定义了负载均衡的方式;
  • 有以下几种负载均衡的实现方式:
    • RoundRobinRule:轮询;
    • RandomRule:随机;
    • RetryRule:先按照 RoundRobinRule 的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务;
    • WeightedResponseTimeRule:对 RoundRobinRule 的扩展,响应速度越快的实例选择权重越大,越容易被选择;
    • BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务;
    • AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例;
    • ZoneAvoidanceRule:默认规则,复合判断 server 所在区域的性能和 server 的可用性选择服务器;

2. 服务消费者获取提供者的三个层次示例

2.1 引入 pom.xml 依赖

<dependency>  <groupId>org.springframework.cloud</groupId>  <artifactId>spring-cloud-starter-netflix-ribbon</artifactId></dependency>
  • 1
  • 2
  • 3
  • 4
  • 如果使用 Eureka 作为注册中心,则不用引入该依赖,因为 Eureka 的依赖里包含 ribbon 相关依赖 jar 包;

2.2 使用 Spring DiscoveryClient 查找服务实例

2.2.1 在主程序类上添加注解

  • @EnableDiscoveryClient:表明可以被注册中心发现。是 Sring Cloud 的触发器,其作用是使应用程序能够使用 DiscoveryClient 和 Ribbon 库;

2.2.2 使用 DiscoveryClient 查找信息

在服务消费者的 client 包下;

@Componentpublic class ProviderDiscoveryClient {    //自动注入 DiscoveryClient 类,该类用于与 Ribbon 交互    @Autowired    private DiscoveryClient discoveryClient;    public Provide getProvide(String providerId) {        RestTemplate restTemplate = new RestTemplate();        //获取服务提供者的所有实例列表,ServiceInstance 用于保存关于服务的特定实例(包括主机名、端口荷 URL)        List<ServiceInstance> instances = discoveryClient.getInstances("provider-instance-name");        if (instances.size()==0) return null;        //检索要调用的服务端点        String serviceUri = String.format("%s/providers/%s",instances.get(0).getUri().toString(), providerId);		//使用标准的 Spring REST 模板类去调用服务        ResponseEntity< provider > restExchange =                restTemplate.exchange(                        serviceUri,                        HttpMethod.GET,                        null, Provider.class, providerId);                return restExchange.getBody();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 这种方法存在以下问题:

    • 没有利用 Ribbon 的客户端负载均衡,调用哪个服务实例需要开发人员定义;
    • 开发人员必须构建一个用来调用服务的 URL;
    • 实例化 ResTemplate 类,不符合 Spring IoC 规范;
  • 结合本篇《5. 本地负载均衡器的实现(消费者)》即可用到客户端负载均衡,即:开发人员定义了本地负载均衡器来实现了负载均衡;

2.3 使用带有 Ribbon 功能的 Spring 调用服务

2.3.1 在主程序类上添加注解

@SpringBootApplication //只需要这个注解即可public class Application {  @LoadBalanced //告诉 Spring Cloud 创建一个支持 Ribbon 的 RestTemplate  @Bean  public RestTemplate getRestTemplate(){      return new RestTemplate();  }  public static void main(String[] args) {        SpringApplication.run(Application.class, args);  }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • Spring Cloud 早期版本中,RestTemplate 类默认自动支持 Ribbon;
  • 自从 Spring Cloud 发布 Angel 版本后,Spring Cloud 中的 RestTemplate 不再支持 Ribbon;
  • 因此,后续版本必须使用 @LoadBalanced 注解显式标注,才能将 Ribbon 和 RestTemplate 一起使用;
  • *RestTemplate 不一定放在主程序类里;也可以在 config 包下新建一个 ApplicationContextConfig 配置类,将 RestTemplate 放在该类里:
@Configurationpublic class ApplicationContextConfig{    @Bean    @LoadBalanced    public RestTemplate getRestTemplate(){        return new RestTemplate();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.3.2 使用 Ribbon 的 RestTemplate 来调用服务

在服务消费者的 client 包下;

@Componentpublic class ProviderRestTemplateClient {    //自动注入即可,不用实例化    @Autowired    RestTemplate restTemplate;    public Provider getProvider(String providerId){        ResponseEntity<Provider> restExchange =                restTemplate.exchange(                        //使用 Eureka 服务 ID 来构建目标 URL                        "http://provider-instance-name/providers/{providerId}",                        HttpMethod.GET,                        null, Provider.class, providerId);        return restExchange.getBody();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 通过使用 RestTemplate 类,Ribbon 将在所有服务实例之间轮询负载均衡所有请求;

2.4 使用 Netflix Feign 客户端调用服务

Feign 相关知识将在下篇《4.2 基于 Feign 与 OpenFeign 的服务接口调用》中说明,这里仅把重点放在与上述两种调用提供者服务的区别与对比;

2.4.1 在主程序类上添加注解

@EnableFeignClients:表示启用 Feign 客户端;

2.4.2 定义用于调用服务提供者的 Feign 接口

@FeignClient("provider-instance-name") //标识服务为 feign 的客户端public interface ProviderFeignClient {    //定义端点的路径和动作    @RequestMapping(             method= RequestMethod.GET,            value="/providers/{providerId}",            consumes="application/json")    //定义传入端点的参数,该方法可以由客户端调用以触发组织服务            Provider getProvider(@PathVariable("providerId") String providerId);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 要是用 ProviderFeignClient 类,开发人员只需要自动装配并使用它即可;

3. 通过 java 配置类自定义负载均衡算法示例(消费者服务)

指切换默认的负载均衡算法,切换后的仍为现成的(与本地负载均衡器有所区别,本地负载均衡器要自己实现);

3.1 编写配置类

  • 注意:自定义配置类不能放在 @ComponentScan 所扫描的当前包下以及子包下,否则自定义的配置类会被所有的Ribbon客户端所共享,达不到自定义的目的;
  • @ComponentScan 注解被封装到主启动类上的 @SpringBootApplication 注解。其默认扫描主启动类所在包及其子包,因此我们要返回上一级目录新建一个 myRule 目录存放我们自定义的负载均衡配置类;

@Configurationpublic class MySelfRule {    @Bean    public IRule myRule(){        return new RandomRule();//定义为随机    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.2 主启动类上添加注解

  • @RibbonClient(name = “provider-instance-name” ,configuration=MySelfRule.class):表示使用自定义负载均衡算法;
    • name:指定服务提供者的实例名称;
    • configuration:指定需要使用哪个配置类的负载均衡;
    • 表示 provider 服务使用 MySelfRule 对应的 Ribbon 配置;
  • 同样,需要对 RestTemplate 类用 @LoadBalanced 注解显示声明;

4. 通过配置自定义负载均衡算法示例(消费者服务)

指切换默认的负载均衡算法,切换后的仍为现成的(与本地负载均衡器有所区别,本地负载均衡器要自己实现);

4.1 修改 bootstrap.yml 配置文件

  • 上述 java 配置类的效果等价于下面这样的配置文件:
#服务提供者的实例名称provider-instance-name:  ribbon:    #代表 Ribbon 使用的负载均衡策略,属性的值为:IRule 的实现类    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule        #其他可用的配置属性    # NFLoadBalancerClassName : 配置 ILoadBalancer 的实现类    # NFLoadBalancerPingClassName : 配置 IPing 的实现类    # NIWSServerListClassName: 配置 ServerList 的实现类    # NIWSServerListFilterClassName: 配置 ServerListtFilter 的实现类
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

4.2 主程序类

  • 不需要 @RibbonClient 注解;
  • 同样,需要对 RestTemplate 类用 @LoadBalanced 注解显示声明;

5. 本地负载均衡器的实现(消费者)

本地负载均衡器不同于自定义负载均衡算法;前者的负载均衡算法需要自己手动实现,后者只是切换成另一种现成的负载均衡算法;

5.1 不使用 RestTemplate

  • 即主程序类不需要对 RestTemplate 类用 @LoadBalanced 注解显示声明;
  • 可以删去也可以注释 @LoadBalanced 注解;

5.2 定义负载均衡接口

可以新建一个包,专门存放我们自己写的负载均衡算法;

public interface LoadBalancer{    ServiceInstance instances(List<ServiceInstance> serviceInstances);}
  • 1
  • 2
  • 3

5.3 实现负载均衡接口

  • 可以根据业务要求写不同的负载均衡算法,这里仅提供一种示例;
  • 该示例实现了一种较为简单的原子性的负载均衡算法;
@Componentpublic class MyLB implements LoadBalancer{    private AtomicInteger atomicInteger = new AtomicInteger(0);    public final int getAndIncrement(){        int current;        int next;        do {            current = this.atomicInteger.get();            next = current >= 2147483647 ? 0 : current + 1;        }while(!this.atomicInteger.compareAndSet(current,next));        System.out.println("*****第几次访问,次数next: "+next);        return next;    }    //负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标  ,每次服务重启动后rest接口计数从1开始。    @Override    public ServiceInstance instances(List<ServiceInstance> serviceInstances){        int index = getAndIncrement() % serviceInstances.size();        return serviceInstances.get(index);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

5.4 在 controller 接口中使用本地负载均衡器

  • 类似于本篇《2.2 使用 Spring DiscoveryClient 查找服务实例》;
  • 不同之处在于 2.2 没有负载均衡功能,这里在 2.2 的基础上,开发人员自己定义了本地负载均衡器,不使用 Ribbon 提供的负载均衡,故《5.1 不使用 RestTemplate》中提到的不用对 RestTemplate 类使用 @LoadBalanced 注解显示声明
@RestControllerpublic class OrderController{    //服务提供者示例的名字    public static final String PAYMENT_URL = "http://provider-instance-name";    @Resource    private RestTemplate restTemplate;    @Resource    private LoadBalancer loadBalancer;    @Resource    private DiscoveryClient discoveryClient;     @GetMapping(value = "/provider/mylb")    public String getProviderLB(){        //获取服务提供者的所有实例列表        List<ServiceInstance> instances = discoveryClient.getInstances("provider-instance-name");        if(instances == null || instances.size() <= 0){            return null;        }        //使用本地负载均衡器选出提供者服务        ServiceInstance serviceInstance = loadBalancer.instances(instances);        URI uri = serviceInstance.getUri();        return restTemplate.getForObject(uri+"/provider/mylb",String.class);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24


最后

新人制作,如有错误,欢迎指出,感激不尽!
欢迎关注公众号,会分享一些更日常的东西!
如需转载,请标注出处!
网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发