天天看点

ribbon 默认负载均衡 是什么_Spring Cloud学习笔记--负载均衡(Ribbon)

ribbon 默认负载均衡 是什么_Spring Cloud学习笔记--负载均衡(Ribbon)
Ribbon is a client-side load balancer that gives you a lot of control over the behavior of HTTP and TCP clients. Feign already uses Ribbon, so, if you use

@FeignClient

, this section also applies. [1]

Ribbon是在客户端实现的负载均衡器,可以有助于你对HTTP和TCP客户端的行为进行控制。

架构图如下:

ribbon 默认负载均衡 是什么_Spring Cloud学习笔记--负载均衡(Ribbon)

如何创建spring boot项目请参看以前文章。

注册中心A的pom文件:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
           

注册中心A的yml文件:

server:
  #注册中心A使用的端口号
  port: 30001
eureka:
  instance:
    #注册中心A主机地址
    hostname: eurekaserver1
  client:
    #本服务为注册中心,所以不需要向注册中心注册自己
    register-with-eureka: false
    #本服务为注册中心,不需要进行检索服务
    fetch-registry: false
    service-url:
      defaultZone: http://eurekaserver2:30002/eureka/
spring:
  application:
    name: eurekaserver1
           

在启动类上,需要加上EnableEurekaServer注解。

ribbon 默认负载均衡 是什么_Spring Cloud学习笔记--负载均衡(Ribbon)

注册中心B的创建与注册中心A一致。

注册中心B的yml文件:

server:
  #注册中心B使用的端口号
  port: 30002
eureka:
  instance:
    #注册中心B主机地址
    hostname: eurekaserver2
  client:
    #本服务为注册中心,所以不需要向注册中心注册自己
    register-with-eureka: false
    #本服务为注册中心,不需要进行检索服务
    fetch-registry: false
    service-url:
      defaultZone: http://eurekaserver1:30001/eureka/
spring:
  application:
    name: eurekaserver2
           

服务提供者C的pom文件:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
           

服务提供者C的yml文件:

server:
  #服务提供者A的端口号
  port: 40001
spring:
  application:
    name: ServerProvide
eureka:
  client:
    service-url:
      defaultZone: http://eurekaserver1:30001/eureka/
           

服务提供者C创建一个Controller提供服务:

@RestController
public class ServerProvideController {
    //描述eureka客户端信息的类
    @Autowired
    private DiscoveryClient eurekaClient;

    @RequestMapping(value="/ServerTest", method=RequestMethod.GET, produces= MediaType.APPLICATION_JSON_VALUE)
    public TestModel getTest(HttpServletRequest request){
        TestModel tm = new TestModel();
        tm.setUrl(request.getRequestURL().toString());
        tm.setMsg("请求访问成功");
        return tm;
    }
}
           

服务提供者C使用的domain类:

public class TestModel {
    private String id;
    private String url;
    private String msg;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
}
           

服务提供者D的搭建与服务提供者C一致。

服务提供者D的yml文件

server:
  #服务提供者B的端口号
  port: 40002
spring:
  application:
    name: ServerProvide
eureka:
  client:
    service-url:
      defaultZone: http://eurekaserver1:30001/eureka/
           

我们现在来改造一下服务调用者:

服务调用者的pom文件中需要

增加对spring-cloud-starter-ribbon的引用

我们来看一下服务调用者的pom文件:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
    <version>1.3.0.RC1</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
           

服务调用者的yml文件:

server:
  #服务调用者端口号
  port: 50002
spring:
  application:
    name: ServerClient
eureka:
  client:
    service-url:
      #将当前服务也注册到注册中心
      defaultZone: http://eurekaserver1:30001/eureka/
           

服务调用者对外提供的服务(Controller):

@RestController
@Configuration
public class ClientTestController {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

    @RequestMapping("/getTestMsg")
    public String getTestMsg(){
        RestTemplate rt = getRestTemplate();
        String result = rt.getForObject("http://ServerProvide/ServerTest", String.class);
        return result;
    }
}
           

服务调用者的启动类需要增加EnableDiscoveryClient注解。

ribbon 默认负载均衡 是什么_Spring Cloud学习笔记--负载均衡(Ribbon)

运行一下,看看效果。

ribbon 默认负载均衡 是什么_Spring Cloud学习笔记--负载均衡(Ribbon)

我们再用浏览器请求一下服务调用者:

ribbon 默认负载均衡 是什么_Spring Cloud学习笔记--负载均衡(Ribbon)

再访问一下:

ribbon 默认负载均衡 是什么_Spring Cloud学习笔记--负载均衡(Ribbon)

可以看到,使用默认的负载均衡策略已经起作用了。

如何使用自定义的负载均衡策略呢?

定义配置类(也可以使用配置文件)

/*
配置类,设置规则仅仅对ServerProvide服务有效,
 */
@RibbonClient(name="ServerProvide", configuration=LbConfig.class)
public class LbConfig {
    @Bean
    public IRule getRule(){
        return new MyRule();
    }
}
           

重写负载均衡策略

/*
自定义负载均衡器的策略
 */
public class MyRule implements IRule {
    private ILoadBalancer lb;// 声明负载均衡器的接口

    /*
    这里主要实现了自定义分配请求的逻辑
     */
    @Override
    public Server choose(Object key) {
        // 获取服务器列表
        List<Server> servers = lb.getAllServers();
        Random r = new Random();
        //生成随机数
        int rand = r.nextInt(10);
        //这里设置自定义的负载均衡策略,如果随机数大于6,则转发到端口为40001的服务提供者上
        //反之则转发到端口为40002的服务提供者上
        //这里仅仅做模拟,后续需要根据业务逻辑重新实现
        if(rand > 6){
            return getServerByPort(servers, 40001);
        }else{
            return getServerByPort(servers, 40002);
        }
    }

    private Server getServerByPort(List<Server> servers, int port){
        for(Server s : servers){
            if(s.getPort() == port){
                return s;
            }
        }
        return null;
    }

    @Override
    public void setLoadBalancer(ILoadBalancer lb) {
        this.lb = lb;
    }

    @Override
    public ILoadBalancer getLoadBalancer() {
        return this.lb;
    }
}
           

运行一下,请求服务调用者,可以看出来,自定义的负载均衡策略已经起作用了。

Ribbon的组成:

  • ILoadBalancer:负载均衡器的入口
  • IPing:检查服务实例是否正常服务
  • IRule:负载均衡器处理规则
  • ServerList<Server>:服务列表的操作对象
  • ServerListUpdater:服务更新器
  • ServerListFilter<Server>:拦截器,用于实现对服务列表的过滤
  • IclientConfig:配置接口
其中IRule最为重要,它为Ribbon提供了负载均衡规则。

代码使用:

public class LbConfig
{
    @Autowired
    private IClientConfig clientConfig;

    @Bean
    public IRule myRule(IClientConfig config) {
        return new RandomRule();
        // return new RoundRobinRule();
        // return new RetryRule();
        // return new WeightedResponseTimeRule();
        // return new BestAvailableRule();
        // return new AvailabilityFilteringRule();
        // return new ZoneAvoidanceRule();
    }

    @Bean
    public IPing myPing(IClientConfig config) {
        return new PingUrl(false, "/Provide/");
    }
}
           
Ribbon负载均衡支持的7中算法解释如下:
  • RandomRule:从服务清单中随机选择一个服务实例
  • RoundRobinRule:按照线性轮询的方式依次选择每个服务实例
  • RetryRule:具备重试机制的实例选择功能,对内部定义的策略进行反复尝试,若期间能够选择到具体的服务实例就返回,超过设置的尝试时间就返回null
  • WeightedResponseTimeRule:根据实例的运行情况来计算权重,并根据权重来挑选实例
  • BestAvailableRule:遍历负责均衡器中维护的所有实例,找出并发请求数最小的一个,即选出最空闲的实例
  • AvailabilityFilteringRule:先遍历所有节点进行过滤,在过滤的集合中选择实例
  • ZoneAvoidanceRule:PredicateBasedRule的实现类,先过滤一部分实例,再以线性轮询的方式从过滤后的实例清单中选出一个

参考

  1. ^Ribbon解释(官方) https://cloud.spring.io/spring-cloud-netflix/reference/html/#spring-cloud-ribbon

继续阅读