服务注册发现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(); //默认的轮询策略
    }
}

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

服务消费端日志:

 

 

posted on 2016-12-01 20:42  duanxz  阅读(1625)  评论(0编辑  收藏  举报