Spring Cloud (四):负载均衡 - Robbin (Eureka & Consul)

Robbin 是 Netflix 开发的实现客户端负载均衡的组件,通常和 Eureka 一起使用,前面的例子中 Eureka Consumer 已经使用了负载均衡

在前面的 Eureka 服务发现的基础上做一些修改

因为 Eureka 依赖已经包含了 Robbin,所以无需导入 Robbin

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        
        <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-actuator</artifactId>
        </dependency>

启动两个 Producer,只是 port 以及返回的内容不同

server.port=8762
    @RequestMapping("/api")
    public String index() {
        return "Greetings from Eureka Producer 1!";
    }
server.port=8763
    @RequestMapping("/api")
    public String index() {
        return "Greetings from Eureka Producer 2!";
    }

在 Consumer 加入 RestTemplate 的配置

@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced  // 实现 Ribbon 负载均衡的 RestTemplate
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
    @Bean
    public IRule loadbalanceRule(){
        return new RandomRule();   // 随机策略
    }
}

添加使用这个 RestTemplate 的接口

@RestController
public class Controller {
    @Autowired
    private LoadBalancerClient loadBalancer;
    
    @Autowired
    private DiscoveryClient discoveryClient;
 
    @Autowired
    private RestTemplate restTemplate;  // 具有负载均衡的 restTemplate
    
    @RequestMapping("/queryService")
    public String query() {
        List<ServiceInstance> instances = discoveryClient.getInstances("service-provider-1");
        StringBuilder urls= new StringBuilder();
        for(ServiceInstance instance : instances){
            urls.append(instance.getHost()+":"+instance.getPort()).append(",");
        }
        return "service name : service-provider-1<br>" + "host : " + urls.toString();
    }
    
    @RequestMapping("/discover")
    public Object discover() {
        // 通过 LoadBalancerClient 获取 service-provider-1 服务的其中一个 host
        // 可以看到有时返回 8762 端口,有时返回 8763 端口,这样就实现了负载均衡
        return loadBalancer.choose("service-provider-1").getUri().toString();
    }
    
    @RequestMapping("/rest")
    public String rest() {
        // 这里直接使用注册在 Eureka 的 service id 也就是 service-provider-1
        // 这样 RestTemplate 会自动实现负载均衡
        String forObject = restTemplate.getForObject("http://service-provider-1/api", String.class);
        return forObject;
    }
}

Ribbon 的负载均衡策略可以用代码配置,也可以通过配置文件配置

    @Bean
    public IRule loadbalanceRule(){
        return new RandomRule();   // 随机策略
    }
@Configuration
@RibbonClient(name="service-provider-1",configuration=RestTemplateConfig.class)
public class NewRobbinConfig {

}
service-provider-1.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule


Robbin 也可以和 Consul 集成,实际上前面的例子中 Consul Consumer 同样已经使用了负载均衡

在前面的 Consul 服务发现的基础上做一些修改

因为 Consul 依赖已经包含了 Ribbon,所以无需导入 Ribbon

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

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

启动两个 Producer,只是 port 以及返回的内容不同

# application.yml
server:
  port: 8501
// Controller.java
    @RequestMapping("/hello")
    public String hello() {
        return "consul-service-1 instance-1";
    }
# application.yml
server:
  port: 8502
    @RequestMapping("/hello")
    public String hello() {
        return "consul-service-1 instance-2";
    }

在 Consumer 端加入 RestTemplate 的配置

@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced  // 实现 Ribbon 负载均衡的 RestTemplate
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
    @Bean
    public IRule loadbalanceRule(){
        return new RandomRule();   // 随机策略
    }
}

添加使用这个 RestTemplate 的接口

public class Controller {
    @Autowired
    private LoadBalancerClient loadBalancer;
    
    @Autowired
    private DiscoveryClient discoveryClient;
 
    @Autowired
    private RestTemplate restTemplate;  // 具有负载均衡的restTemplate
    
    @RequestMapping("/services")
    public Object services() {
        // 获取 consul-service-1 服务的信息
        // 这里会返回两个,因为有两个服务注册了这个名字
        return discoveryClient.getInstances("consul-service-1");
    }
 
    @RequestMapping("/discover")
    public Object discover() {
        // 通过 LoadBalancerClient 获取 consul-service-1 服务的其中一个 host
        // 可以看到有时返回 8501 端口,有时返回 8502 端口,这样就实现了负载均衡
        return loadBalancer.choose("consul-service-1").getUri().toString();
    }
    
    @RequestMapping("/rest")
    public String rest(){
        // 这里直接使用注册在 Consul 的 service id 也就是 consul-service-1
        // 这样 RestTemplate 会自动实现负载均衡
        String forObject = restTemplate.getForObject("http://consul-service-1/hello", String.class);
        return forObject;
    }
}

Ribbon 的负载均衡策略可以用代码配置,也可以通过配置文件配置

    @Bean
    public IRule loadbalanceRule(){
        return new RandomRule();   // 随机策略
    }
@Configuration
@RibbonClient(name="consul-service-1",configuration=RestTemplateConfig.class)
public class NewRobbinConfig {

}
consul-service-1:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule


上一篇:java-Spring RestTemplate getForObject()提供401未经授权的异常


下一篇:.net core 环境安装失败,错误:0x80072EE2