一,前言
在生产环境中,一般微服务都会部署多个实例,那么服务消费者如何将请求分摊到服务提供者身上呢?
二,Ribbon简介
1,Ribbon是Netflix发布的负载均衡器,它有助于控制Http和Tcp客户端行为。为Ribbon配置服务提供者列表后,Ribbon就可基于某种负载均衡算法自动的帮助消费者去请求。Ribbon默认提供的负载均衡算法有:轮询、随机等,也可以自定义实现负载均衡算法。
在Spring Cloud中,Ribbon与Eureka Server配合使用时,可自动从Eureka Server获取服务提供者地址列表,并基于负载均衡算法去请求其中一个服务提供者实例,如下图所示:
2,为消费者整合Ribbon
1),引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
2),为RestTemplate添加@LoadBalance注解
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
回顾以前代码,使用以下方法实例化RestTemplate:
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
两者对比发现,只需要添加@LoadBalance注解就可为RestTemplate 整合Ribbon,使其具备负载均衡能力。
3),controller代码如下:
@RestController
@Slf4j
public class UserController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@Autowired //注入负载均衡客户端
private LoadBalancerClient loadBalancerClient;
@GetMapping("/user/{id}")
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://movie-provider:8091/" + id, User.class);
}
@GetMapping("/user/list")
public List<User> findAll() {
return restTemplate.getForObject("http://movie-provider:8091/list", List.class);
}
/**
* 获取提供者元数据信息
* @return
*/
@GetMapping("/provider/instance")
public List<ServiceInstance> getServiceInstance(){
return discoveryClient.getInstances("movie-provider");
}
/**
* 打印当前选择的是哪个节点
* 注意:
* 不能将restTemplate.getForObject()与loadBalancerClient.choose()写在一个方法中,两者会引起冲突,因为代码中restTemplate实际上
* 是一个Ribbon客户端,本身已经包含了choose行为。
*/
@GetMapping("/print-instance")
public void printInstance(){
ServiceInstance serviceInstance = loadBalancerClient.choose("movie-provider");
log.info("serviceId--->{},host--->{},port--->{}",serviceInstance.getServiceId(),serviceInstance.getHost(),serviceInstance.getPort());
}
}
4),使用java代码自定义Ribbon配置
很多场景下需要使用Ribbon自定义的配置,例如修改Ribbon的负载均衡规则等。Spring Cloud Camden允许使用java代码或者属性自定义Ribbon配置,两种方式是等价的。
在Spring Cloud中,Ribbon的默认配置如下(格式,BeanType beanName:ClassName):
4.1 创建Ribbon配置类
@Configuration
public class RibbonConfiguration {
public IRule ribbonRule(){
//负载均衡规则改为随机
return new RandomRule();
}
}
4.2 创建一个类,并在其上添加@Configuration和@RibbonClient注解
/**
*使用@RibbonClient为特定name的Ribbon Client自定义配置
*使用@RibbonClient的configuration属性指定Ribbon的配置类
*/
@Configuration
@RibbonClient(name="movie-provider",configuration = RibbonConfiguration.class)
public class TestConfiguration {
}