zoukankan      html  css  js  c++  java
  • Spring Cloud入门教程Ribbon实现客户端负载均衡

    简介

    结构

    我们继续以之前博客的代码为基础,增加Ribbon组件来提供客户端负载均衡。负载均衡是实现高并发、高性能、可伸缩服务的重要组成部分,它可以把请求分散到一个集群中不同的服务器中,以减轻每个服务器的负担。客户端负载均衡是运行在客户端程序中的,如我们的web项目,然后通过获取集群的IP地址列表,随机选择一个server发送请求。相对于服务端负载均衡来说,它不需要消耗服务器的资源。

    基础环境

    • JDK 1.8
    • Maven 3.3.9
    • IntelliJ 2018.1
    • Git

    项目源码

    Gitee码云

    更新配置

    我们这次需要在本地启动两个产品服务程序,用来验证负载均衡,所以需要为第二个程序提供不同的端口。Spring Cloud配置服务中心的配置默认会覆盖本地系统环境变量,而我们需要通过系统环境变量来设置产品服务的端口,所以需要在配置中心git仓库中修改产品服务的配置文件product-service.yml

    server:
      port: 8081
    spring:
      cloud:
        config:
          allow-override: true
          override-system-properties: false
    

    allow-override的默认值即为true,写出它来是想作说明,它的意思是允许远程配置中心的配置项覆盖本地的配置,并不是说允许本地的配置去覆盖远程的配置。当然我们可以把它设置成false,但是为了提供更精确的覆盖规则,这里保留了默认值。
    我们添加了override-system-properties=false,即虽然远程配置中心的配置文件可以覆盖本地的配置,但是不要覆盖本地系统变量。修改完成后提交到git仓库。

    另外,在productService项目的ProductController中添加一些log,用来验证负载均衡是否生效:

    package cn.zxuqian.controllers;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    
    @RestController
    public class ProductController {
    
        private static Logger log = LoggerFactory.getLogger(ProductController.class);
    
        @RequestMapping("/products")
        public String productList() {
            log.info("Access to /products endpoint");
            return "外套,夹克,毛衣,T恤";
        }
    }
    
    

    为web配置Ribbon

    首先在pom.xml中添加Ribbon的依赖:

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

    然后修改Application类,添加如下代码:

    @EnableCircuitBreaker
    @EnableDiscoveryClient
    @RibbonClient(name = "product-service")
    @SpringBootApplication
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
        @Bean
        @LoadBalanced
        public RestTemplate rest(RestTemplateBuilder builder) {
            return builder.build();
        }
    }
    

    这里用到了@RibbonClient(name = "product-service")注解,用来标记此项目为Ribbon负载均衡的客户端,它需要选择产品服务集群中其中的一台来访问所需要的服务,这里的name属性对应于productService项目中配置的spring.application.name属性。
    @LoadBalanced注解标明了RestTemplate会被配置为自动使用Ribbon的LoadBalancerClient来选择服务的uri并发送请求。

    在我们在ProductService类中添加如下代码:

    @Service
    public class ProductService {
    
        private final RestTemplate restTemplate;
    
        @Autowired
        private DiscoveryClient discoveryClient;
    
        public ProductService(RestTemplate restTemplate) {
            this.restTemplate = restTemplate;
        }
    
        @HystrixCommand(fallbackMethod = "backupProductList")
        public String productList() {
            List<ServiceInstance> instances = this.discoveryClient.getInstances("product-service");
            if(instances != null && instances.size() > 0) {
                return this.restTemplate.getForObject(instances.get(0).getUri() + "/products", String.class);
            }
    
            return "";
        }
    
        public String backupProductList() {
            return "夹克,毛衣";
        }
    
    
        public String productListLoadBalanced() {
            return this.restTemplate.getForObject("http://product-service/products", String.class);
        }
    }
    

    这里新添加了一个productListLoadBalanced方法,跟之前的productList方法访问的是同一服务,只不过是用Ribbon Client去做了负载均衡,这里的uri的host变成了product-service即要访问的服务的名字,跟@RibbonClient中配置的name属性保持一致。最后在我们的ProductController中添加下面的代码:

    @RestController
    public class ProductController {
    
        @Autowired
        private ProductService productService;
    
        @RequestMapping("/products")
        public String productList() {
            return productService.productList();
        }
    
        @RequestMapping("/productslb")
        public String productListLoadBalanced() {
            return productService.productListLoadBalanced();
        }
    }
    

    来创建一个专门处理/productslb请求的方法,调用productServie提供负载均衡的方法。

    到这里我们的代码就完成了,代码看似简单,其实是所有的配置都使用了默认值。Ribbon提供了编程式和配置式两种方式来配置Ribbon Client。现简单介绍下,后续深入Ribbon时再和大家一起看看如何修改它的配置。Ribbon提供如下配置(左边是接口,右边是默认实现):

    • IClientConfig ribbonClientConfig: DefaultClientConfigImpl
    • IRule ribbonRule: ZoneAvoidanceRule
    • IPing ribbonPing: DummyPing
    • ServerList<Server> ribbonServerList: ConfigurationBasedServerList
    • ServerListFilter<Server> ribbonServerListFilter: ZonePreferenceServerListFilter
    • ILoadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer
    • ServerListUpdater ribbonServerListUpdater: PollingServerListUpdater

    因为我们这个项目用了Eureka,所以有些配置项和默认实现有所不同,如Eureka使用DiscoveryEnabledNIWSServerList取代ribbonServerList来获取在Eureka上注册的服务的列表。下边有一个简单的Congiguration类,来自Spring官网:

    public class SayHelloConfiguration {
    
      @Autowired
      IClientConfig ribbonClientConfig;
    
      @Bean
      public IPing ribbonPing(IClientConfig config) {
        return new PingUrl();
      }
    
      @Bean
      public IRule ribbonRule(IClientConfig config) {
        return new AvailabilityFilteringRule();
      }
    
    }
    

    Ribbon默认不会发送Ping检查server的健康状态,默认均正常,然后IRune默认实现为ZoneAvoidanceRule用来避免AWS EC2问题较多的zone,这在本地测试环境来说是用不到的,然后替换成了AvailabilityFilteringRule,这个可以开启Ribbon自带的断路器功能,来过滤不正常工作的服务器。

    测试

    首先启动我们的configserver配置中心服务,然后启动registry Eureka注册与发现服务,然后启动两个productService,第一个我们可以正常使用spring-boot:run插件来启动,第二个我们需要给它提供一个新的端口,可以用如下命令启动:

    $ SERVER_PORT=8082 mvn spring-boot:run
    

    最后启动我们的web客户端项目,访问http://localhost:8080/productslb,然后刷新几次,会看到运行着productService的两个命令行窗口会随机出现我们的log:

     Access to /products endpoint
    

    欢迎访问我的博客张旭乾的博客

  • 相关阅读:
    【纯水题】POJ 1852 Ants
    【树形DP】BZOJ 1131 Sta
    【不知道怎么分类】HDU
    【树形DP】CF 1293E Xenon's Attack on the Gangs
    【贪心算法】CF Emergency Evacuation
    【思维】UVA 11300 Spreading the Wealth
    【树形DP】NOI2003 逃学的小孩
    【树形DP】BZOJ 3829 Farmcraft
    【树形DP】JSOI BZOJ4472 salesman
    【迷宫问题】CodeForces 1292A A NEKO's Maze Game
  • 原文地址:https://www.cnblogs.com/zxuqian/p/8994758.html
Copyright © 2011-2022 走看看