美文网首页spring cloudspring cloudspring cloud
Spring Cloud入门教程(二):客户端负载均衡(Ribb

Spring Cloud入门教程(二):客户端负载均衡(Ribb

作者: CD826 | 来源:发表于2017-08-03 18:04 被阅读5998次

    上一篇:《Spring Cloud入门教程(一):服务治理(Eureka)》

    对于大型应用系统负载均衡(LB:Load Balancing)是首要被解决一个问题。在微服务之前LB方案主要是集中式负载均衡方案,在服务消费者和服务提供者之间又一个独立的LB,LB通常是专门的硬件,如F5,或者是基于软件的,如VS、HAproxy等。LB上有所有服务的地址映射表,当服务消费者调用某个目标服务时,它先向LB发起请求,由LB以某种策略(比如:Round-Robin)做负载均衡后将请求转发到目标服务。

    而微服务的出现,则为LB的实现提供了另外一种思路:把LB的功能以库的方式集成到服务消费方的进程内,而不是由一个集中的设备或服务器提供。这种方案称为软负载均衡(Soft Load Balancing)或者客户端负载均衡。在Spring Cloud中配合Eureka的服务注册功能,Ribbon子项目则为REST客户端实现了负载均衡。

    接下来我们将通过扩展上一篇中的示例代码来搭建一个基于Ribbon的客户端负载均衡示例。

    1. 示例代码

    保持上一篇中的代码不变。

    2. 如何启用Ribbon

    在上一篇中的consumer-hello工程(也就是我们的服务消费方)的pom的依赖中声明了:

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-ribbon</artifactId>
    </dependency>   
    

    也就是说在本项目中将会启动Ribbon。

    2.1 启动类

    @EnableDiscoveryClient
    @SpringBootApplication
    public class Application {
    
        @Bean
        @LoadBalanced
        RestTemplate restTemplate() {
            return new RestTemplate();
        }
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
    }
    

    在该启动类中:

    • @EnableDiscoveryClient注解,说明这是一个Eureka客户端,同时也可以获取到Eureka服务器中所注册的服务列表;
    • RestTemplate中我们增加了一个@LoadBalanced注解,该注解就是能够让RestTemplate启用客户端负载均衡。

    备注:如果你对Spring的RestTemplate使用还不是很了解,可以参考我的这篇文章《Spring Cloud入门教程(番外篇一):RestTemplate使用指南》

    3. 负载均衡测试

    1) 启动Eureka服务器;
    2) 启动多个服务提供者:SERVICE-HELLO,这里我启动了两个:

    java -jar service-hello-1.0.0-SNAPSHOT.jar
    
    java -jar service-hello-1.0.0-SNAPSHOT.jar --server.port=2200
    
    

    通过Maven将Service-Hello打包成一个FatJar,可以使用上面的命令启动多个实例。这里SERVICE-HELLO的两个实例服务器端口分别为:21002200

    3) 启动服务消费者:consumer-hello;
    4) 打开Eureka服务器管理页面:http://localhost:8260,可以看到已经有两个SERVICE-HELLO服务和一个CONSUMER-HELLO服务注册进来了。

    Ribbon - Eureka Server

    5) 打开Consumer-Hello:http://localhost:8080/hello,多次刷新,在页面中我们可以看到下面两个输出交替出现:

    Ribbon - Eureka Server
    Ribbon - Eureka Server

    说明了consumer-hello在交替访问SERVICE-HELLO的两个服务实例,也就是说我们的负载均衡测试是成功的。

    4. 直接使用Ribbon的API

    另外,除了使用@LoadBalanced注解外,我们还可以直接使用Ribbon所提供的LoadBalancerClient来实现负载均衡:

    @RestController
    public class HelloController {
        protected Logger logger = LoggerFactory.getLogger(HelloController.class);
    
        @Autowired
        private RestTemplate restTemplate;
    
        @Autowired
        private LoadBalancerClient loadBalancerClient;
    
        @RequestMapping(value = "/hello", method = RequestMethod.GET)
        public String hello() {
            return restTemplate.getForEntity("http://SERVICE-HELLO/hello", String.class).getBody();
        }
    
        @RequestMapping(value = "/helloEx", method = RequestMethod.GET)
        public String helloEx() {
            ServiceInstance instance = this.loadBalancerClient.choose("SERVICE-HELLO");
            URI helloUri = URI.create(String.format("http://%s:%s/hello", instance.getHost(), instance.getPort()));        
            logger.info("Target service uri = {}. ", helloUri.toString());
            return new RestTemplate().getForEntity(helloUri, String.class).getBody();
        }
    }
    

    同样多次访问http://localhost:8080/helloEx后,在控制台中将会输出以下日志:

    2017-06-26 21:31:30 891 [http-nio-8080-exec-3] INFO  i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2100/hello. 
    2017-06-26 21:31:33 008 [http-nio-8080-exec-4] INFO  i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2200/hello. 
    2017-06-26 21:31:34 447 [http-nio-8080-exec-6] INFO  i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2100/hello. 
    2017-06-26 21:31:35 395 [http-nio-8080-exec-7] INFO  i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2200/hello. 
    2017-06-26 21:31:36 220 [http-nio-8080-exec-8] INFO  i.t.c.c.h.controller.HelloController - Target service uri = http://cd826dembp.lan:2100/hello. 
    

    说明负载均衡是一样启作用的。

    注意:helloEx方法中不能够使用之前自动织入的restTemplate,否则会报以下错误:

    java.lang.IllegalStateException: No instances available for cd826dembp.lan
    

    这个是因为自动织入的restTemplate会把服务器的逻辑名称当作服务名称来使用,造成找不到相应的服务实例,从而报错。

    另外,我们还可以采用下面的方式:

    @Primary
    @Bean(name="lbcRestTemplate")
    RestTemplate lbcRestTemplate() {
        return new RestTemplate();
    }
    

    上面的类更改为:

    @RestController
    public class HelloController {
        protected Logger logger = LoggerFactory.getLogger(HelloController.class);
    
        @Autowired
        @Qualifier(value = "restTemplate")
        private RestTemplate restTemplate;
    
        @Autowired
        @Qualifier(value = "lbcRestTemplate")
        private RestTemplate lbcRestTemplate;
    
        @Autowired
        private LoadBalancerClient loadBalancerClient;
    
        @RequestMapping(value = "/hello", method = RequestMethod.GET)
        public String hello() {
            return restTemplate.getForEntity("http://SERVICE-HELLO/hello", String.class).getBody();
        }
    
        @RequestMapping(value = "/helloEx", method = RequestMethod.GET)
        public String helloEx() {
            ServiceInstance instance = this.loadBalancerClient.choose("SERVICE-HELLO");
            URI helloUri = URI.create(String.format("http://%s:%s/hello", instance.getHost(), instance.getPort()));
            logger.info("Target service uri = {}. ", helloUri.toString());
            return this.lbcRestTemplate.getForEntity(helloUri, String.class).getBody();
        }
    }
    

    5. Ribbon负载均衡策略

    Ribbon本身提供了下面几种负载均衡策略:

    • RoundRobinRule: 轮询策略,Ribbon以轮询的方式选择服务器,这个是默认值。所以示例中所启动的两个服务会被循环访问;
    • RandomRule: 随机选择,也就是说Ribbon会随机从服务器列表中选择一个进行访问;
    • BestAvailableRule: 最大可用策略,即先过滤出故障服务器后,选择一个当前并发请求数最小的;
    • WeightedResponseTimeRule: 带有加权的轮询策略,对各个服务器响应时间进行加权处理,然后在采用轮询的方式来获取相应的服务器;
    • AvailabilityFilteringRule: 可用过滤策略,先过滤出故障的或并发请求大于阈值一部分服务实例,然后再以线性轮询的方式从过滤后的实例清单中选出一个;
    • ZoneAvoidanceRule: 区域感知策略,先使用主过滤条件(区域负载器,选择最优区域)对所有实例过滤并返回过滤后的实例清单,依次使用次过滤条件列表中的过滤条件对主过滤条件的结果进行过滤,判断最小过滤数(默认1)和最小过滤百分比(默认0),最后对满足条件的服务器则使用RoundRobinRule(轮询方式)选择一个服务器实例。

    我们可以通过继承ClientConfigEnabledRoundRobinRule,来实现自己负载均衡策略。

    这里,大家先对Ribbon提供的负载均衡有一个概念,后续我们再详细分析各个负载均衡策略的具体处理逻辑及配置方式。

    6. 小结

    Ribbon的主要功能是为REST客户端实现负载均衡,其工作原理可以概括为下面四个步骤:

    1. Ribbon首先根据其所在Zone优先选择一个负载较少的Eureka Server;
    2. 定期从Eureka Server更新并过滤服务实例列表;
    3. 根据指定的负载均衡策略,从可用的服务器列表中选择一个服务实例的地址;
    4. 然后通过RestClient进行服务调用。

    这时我们系统的架构变成为:

    Ribbon - 040

    你可以到这里下载本篇的代码。

    下一篇:《Spring Cloud入门教程(三):声明式服务调用(Feign)》

    相关文章

      网友评论

      • d21eee9a2b5d:feign默认开启了负载均衡嘛?
      • d21eee9a2b5d:如果RestTemplate不加注解@LoadBalanced,调用就会报错:java.net.UnknownHostException: SYSTEM-USER。为什么呢?
        @Bean
        public RestTemplate restTemplate() {
        return new RestTemplate();
        }
        f9bd917b62c1:不加@LoadBalanced则restTemplate会正常解析为一个uri而非实例来调用,SYSTEM-USER是服务实例id而非一个uri服务,所以报些错误。如果不使用@LoadBalanced可以参考博主此文章使用LoadBalancerClient来解析SYSTEM-USER实例选择其中服务uri,这样也是可以的
      • 2a441a851d1c:请问我不导入ribbon包、只是注册两个Provider到Eureka Server 然后多次调用consumer也会出现轮询的情况。是不是Eureka Server已经提供了负载均衡、那通过ribbon来实现负载均衡和直接默认用Eureka Server来完成负载均衡有啥不同么?
        CD826:@端上来尝尝 Feign已经使用了Ribhon做负载均衡
        2a441a851d1c:@CD826 我是参照纯洁的微笑的博客 来做的例子 他只依赖了spring-cloud-starter-feign这个包 并没有用Ribbon、就实现了负载均衡。下面是他的博文地址,请看一下。
        http://www.ityouknow.com/springcloud/2017/05/12/eureka-provider-constomer.html
        CD826:你是怎么调用的?

      本文标题:Spring Cloud入门教程(二):客户端负载均衡(Ribb

      本文链接:https://www.haomeiwen.com/subject/kjoplxtx.html