• 服务注册发现Eureka之三:Spring Cloud Ribbon实现客户端负载均衡(客户端负载均衡Ribbon之三:使用Ribbon实现客户端的均衡负载)


    在使用RestTemplate来消费spring boot的Restful服务示例中,我们提到,调用spring boot服务的时候,需要将服务的URL写死或者是写在配置文件中,但这两种方式,无论哪一种,一旦ip地址发生了变化,都需要改动程序,并重新部署服务,使用Ribbon的时候,可以有效的避免这个问题。

    前言:

    软负载均衡的实现方式有两种,分别是服务端的负载均衡和客户端的负载均衡

    服务端负载均衡:当浏览器向后台发出请求的时候,会首先向反向代理服务器发送请求,反向代理服务器会根据客户端部署的ip:port映射表以及负载均衡策略,来决定向哪台服务器发送请求,一般会使用到nginx反向代理技术。

    客户端负载均衡:当浏览器向后台发出请求的时候,客户端会向服务注册器(例如:Eureka Server),拉取注册到服务器的可用服务信息,然后根据负载均衡策略,直接命中哪台服务器发送请求。这整个过程都是在客户端完成的,并不需要反向代理服务器的参与。

    一、启动Eureka Server 和 启动微服务,并注册到Eureka Server上

    请参考该例: 《服务注册发现Eureka之一:Spring Cloud Eureka的服务注册与发现

    二、服务提供端

    2.1、为了更好的追踪负载均衡的分发,我将《服务注册发现Eureka之一:Spring Cloud Eureka的服务注册与发现》的示例修改一下,增加计数器的展示:

    package com.dxz.compute;
    import java.time.LocalDateTime;
    
    import org.apache.log4j.Logger;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.discovery.DiscoveryClient;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class ComputeController {
        private final Logger logger = Logger.getLogger(getClass());
        @Autowired
        private DiscoveryClient client;
    
        @RequestMapping(value = "/add", method = RequestMethod.GET)
        public Integer add(@RequestParam Integer a, @RequestParam Integer b, @RequestParam Integer sn) {
            ServiceInstance instance = client.getLocalServiceInstance();
            Integer r = a + b;
            logger.info("/add, host:" + instance.getHost() + ", service_id:" + instance.getServiceId() + ", result:" + r+ ",sn="+sn +",time="+ LocalDateTime.now());
            return r;
        }
    }

    2.2、为了演示负载均衡的效果,再启动一个为服务,注意需要将端口号改成不一致

    如在eclipse中再拷贝一个

    spring.application.name=compute-service
    server.port=2224
    eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

    三、服务消费端

    1、构建consumer-movie-ribbon项目,在pom.xml中引入ribbon依赖

      在引入Eureka依赖的时候,默认里面含有ribbon依赖

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.dxz</groupId>
        <artifactId>consume-movie-ribbon</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.3.5.RELEASE</version>   <!--配合spring cloud版本 -->
            <relativePath /> <!-- lookup parent from repository -->
        </parent>
        <properties>
            <!--设置字符编码及java版本 -->
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <!--增加eureka-server的依赖 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-eureka-server</artifactId>
            </dependency>
            <!--用于测试的,本例可省略 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <!--依赖管理,用于管理spring-cloud的依赖 -->
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-parent</artifactId>
                    <version>Brixton.SR3</version>   <!--官网为Angel.SR4版本,但是我使用的时候总是报错 -->
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <!--使用该插件打包 -->
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>

    2、添加@LoadBalanced注解,实现负载均衡

      ribbon负载均衡策略默认为轮循方式

    package com.dxz.compute.demo1;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.netflix.ribbon.RibbonClient;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.web.client.RestTemplate;
    
    import com.dxz.compute.loadbalance.ExcludeFromComponentScan;
    import com.dxz.compute.loadbalance.TestConfiguration;  
      
    @SpringBootApplication  
    @EnableEurekaClient  
    @RibbonClient(name = "compute-service", configuration = TestConfiguration.class)
    @ComponentScan(excludeFilters = {@ComponentScan.Filter(type=FilterType.ANNOTATION,value=ExcludeFromComponentScan.class)})
    public class SpringbootRestTemplateApplication {  
          
      
        @Bean  
        @LoadBalanced  // 添加负载均衡支持,很简单,只需要在RestTemplate上添加@LoadBalanced注解,那么RestTemplate即具有负载均衡的功能  
        public RestTemplate restTemplate() {  
            return new RestTemplate();
        }  
      
        public static void main(String[] args) {  
            SpringApplication.run(SpringbootRestTemplateApplication.class, args);  
        }  
    }  

    在consumer-movie-ribbon中通过RestTemplate 调用上面的2个服务提供方的服务。注意下面的url,是实例名称(不需要ip:port),restTemplate将有Ribbon提供的负载均衡功能。

    package com.dxz.compute.demo1;
    import java.util.concurrent.atomic.AtomicInteger;
    
    import org.springframework.beans.factory.annotation.Autowired;  
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;  
    import org.springframework.web.client.RestTemplate;  
      
      
    @RestController  
    public class RestTemplateController {  
        @Autowired  
        private RestTemplate restTemplate;  
        
        private AtomicInteger sn = new AtomicInteger(0);
        @RequestMapping(value = "/test", method = RequestMethod.GET)
        public void test(@RequestParam Integer a, @RequestParam Integer b) {// 将原来的ip:port的形式,改成注册到Eureka Server上的应用名即可
            System.out.println("==============================");
            String result = restTemplate.getForObject("http://compute-service/add?a="+a +"&b="+b + "&sn="+sn.incrementAndGet(), String.class);  
            System.out.println("返回结果:"+result);  
        }  
    }  

    3、自定义负载均衡策略

    package com.dxz.compute.loadbalance;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import com.netflix.loadbalancer.IRule;
    import com.netflix.loadbalancer.RandomRule;
    import com.netflix.loadbalancer.RetryRule;
    import com.netflix.loadbalancer.RoundRobinRule;
    
    /**
     * @Configuration注解不能放在@SpringBootApplication所在的包下 如果放在此包下,默认全部负载均衡使用此策略
     */
    @Configuration
    @ExcludeFromComponentScan
    public class TestConfiguration {
    
        @Bean
        public IRule ribbonRule() {
            //return new RandomRule(); //设置负载均衡的规则为随机
            return new RoundRobinRule(); //默认的轮询策略
        }
    }

    4、指定对某个客户端使用自定义负载均衡

    @RibbonClient(name = "compute-service", configuration = TestConfiguration.class)指定调用“compute-service”服务的客户端,使用TestConfiguration.class里配置的负载均衡策略。其他客户端不受影响。
    package com.dxz.compute.demo1;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.cloud.netflix.ribbon.RibbonClient;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.web.client.RestTemplate;
    
    import com.dxz.compute.loadbalance.ExcludeFromComponentScan;
    import com.dxz.compute.loadbalance.TestConfiguration;  
      
    @SpringBootApplication  
    @EnableEurekaClient  
    @RibbonClient(name = "compute-service", configuration = TestConfiguration.class)
    @ComponentScan(excludeFilters = {@ComponentScan.Filter(type=FilterType.ANNOTATION,value=ExcludeFromComponentScan.class)})
    public class SpringbootRestTemplateApplication {  
    ...
    }

    5、如果将上面的TestConfiguration@Configuration注解放在@SpringBootApplication所在的包下,所以的客户端都按照这个策略进行。

      a、在@Configuration包下创建ExcludeFromComponentScan注解,注解见下面:

    package com.dxz.compute.loadbalance;
    
    public @interface ExcludeFromComponentScan {
    }

      b、在入口类中排除此注解不扫描

    package com.dxz.compute.demo1;
    
    @SpringBootApplication  
    @EnableEurekaClient  
    @RibbonClient(name = "compute-service", configuration = TestConfiguration.class)
    @ComponentScan(excludeFilters = {@ComponentScan.Filter(type=FilterType.ANNOTATION,value=ExcludeFromComponentScan.class)})
    public class SpringbootRestTemplateApplication {  
          
      
        @Bean  
        @LoadBalanced  // 添加负载均衡支持,很简单,只需要在RestTemplate上添加@LoadBalanced注解,那么RestTemplate即具有负载均衡的功能  
        public RestTemplate restTemplate() {  
            return new RestTemplate();
        }  
      
       ...  
    }  

      c、在TestConfiguration中使用此注解

    @Configuration
    @ExcludeFromComponentScan
    public class TestConfiguration {
    
        @Bean
        public IRule ribbonRule() {
            //return new RandomRule(); //设置负载均衡的规则为随机
            return new RoundRobinRule(); //默认的轮询策略
        }
    }

    6、开启多个compute-service微服务,测试结果

         

    通过消费端服务调用服务提供方,负载均衡的结果如下:

    第一种用轮询策略:

    @Bean
        public IRule ribbonRule() {
            //return new RandomRule(); //设置负载均衡的规则为随机
            return new RoundRobinRule(); //默认的轮询策略
        }

     下面2台服务提供方日志:

     第二种:随机策略

    @Configuration
    @ExcludeFromComponentScan
    public class TestConfiguration {
    
        @Bean
        public IRule ribbonRule() {
            return new RandomRule(); //设置负载均衡的规则为随机
            //return new RoundRobinRule(); //默认的轮询策略
        }
    }

    两台服务提供方日志如下:

    服务消费端日志:

     

  • 相关阅读:
    Autofac 依赖注入
    C#高级语法
    @helper
    Spiral Matrix -- LeetCode
    Best Time to Buy and Sell Stock with Cooldown -- LeetCode
    Kth Smallest Element in a Sorted Matrix -- LeetCode
    Number of Connected Components in an Undirected Graph -- LeetCode
    Super Ugly Number -- LeetCode
    Ugly Number II -- LeetCode
    Missing Ranges -- LeetCode
  • 原文地址:https://www.cnblogs.com/duanxz/p/6123502.html
Copyright © 2020-2023  润新知