1 简介
本文主要实现有:
- 配合Eureka使用Ribbon实现负载均衡
- 自定义Ribbon配置整合(java代码和属性配置两种实现)
- Ribbon独立使用配置整合
2 整合Ribbon实现负载均衡
通过上一篇文章《多节点高可用Eureka集群与服务注册》我们搭建了一个Eureka的集群,本文为了测试搭建后的效果稍作修改。
首先复制上文中artifactId为microservice-provider-user的项目,修改端口为8001,这样我们就有了两个微服务的提供者,端口分别为8000和8001
2.1 为消费者整合Ribbon
首先给消费者添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
注:由于我们的项目配合Eureka使用,spring-cloud-starter-eureka包含了spring-cloud-starter-ribbon,所以无需再次引入依赖。
在上文中我们通过RestTemplate来实现请求,那我们在RestTemplate上面添加@LoadBalanced注解
/**
* ConsumerMovieApplication class
*
* @author TransientBa
* @date 2018/5/5
*/
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerMovieApplication {
//添加LoadBalanced注解来整合Ribbon使其具有负载均衡的能力
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerMovieApplication.class,args);
}
}
2.2 修改Controller以便使用测试
package com.TransientBa.cloud.controller;
import com.TransientBa.cloud.pojo.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* MovieController class
*
* @author TransientBa
* @date 2018/5/5
*/
@RestController
public class MovieController {
private static Logger LOGGER = LoggerFactory.getLogger(MovieController.class);
//使用restTemplate请求User服务
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private LoadBalancerClient loadBalancerClient;
//读取配置文件Url路径
@Value("${user.userServiceUrl}")
private String userServiceUrl;
@GetMapping(value = "/user/{id}")
// @GetMapping(value = "/user/{id}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public User findById(@PathVariable Long id){
//下面这个userServiceUrl 为配置文件中读取的http://microservice-provider-user/地址 该地址为服务提供者的虚拟主机名 配合Eureka使用会自动将其映射成微服务的网络地址 即前面提到的8001 8000
return this.restTemplate.getForObject(userServiceUrl + id,User.class);
}
/***
* 查询microservice-provider-user服务的信息并返回
* @return microservice-provider-user服务的信息
*
* 使用DiscoveryClient.getInstances(serviceId),可查询指定微服务在Eureka上的实例列表
*/
@GetMapping(value = "/user-instance", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
// @GetMapping(value = "/user-instance", produces = MediaType.APPLICATION_XML_VALUE)
public List<ServiceInstance> showInfo(){
return this.discoveryClient.getInstances("microservice-provider-user");
// return this.discoveryClient.getInstances("MICROSERVIECE-CONSUMER-MOVIE");
}
/**=
* 打印当前选择的是哪个节点
*/
@GetMapping("/log-instance")
public void logUserInstance(){
ServiceInstance serviceInstance = this.loadBalancerClient.choose("microservice-provider-user");
MovieController.LOGGER.info("{}:{}:{}",serviceInstance.getServiceId(),serviceInstance.getHost(),serviceInstance.getPort());
}
}
注:此处有一点需要注意,restTemplate.getForObject和loadBalancerClient.choose不能同时在一个方法中使用,因为restTemplate上的@LoadBalanced注解使其在这里成为了一个Ribbon客户端,本身已经包含了choose的行为,放在一起会产生冲突。
2.3 测试
启动四个服务,访问检查页面
可以看到Server分别发现了两个叫MICROSERVICE-PROVIDER-USER的服务提供者,一个叫MICROSERVIECE-CONSUMER-MOVIE的消费者,下面我们通过多次请求/user/{id}接口来观察下两个服务提供者打印的日志。
多次访问http://localhost:8010/user/1接口
多次请求后,两个微服务提供者的控制台都输出了查询语句
为了更直观的看到访问的情况 ,我们再多次访问上面2.2中的/log-instance接口
请求均匀的分布到了两个微服务节点上。
3 自定义Ribbon配置整合
上面配置了一个简单的负载均衡,那如何自定义负载均衡的规则呢?
要实现有两种方式
- 通过Java代码配置
- 通过配置文件中的属性去配置
在SpringCloud中,Ribbon默认的配置类是RibbonClientConfiguration。也可使用一个POJO自定义Ribbon的配置(自定义配置会覆盖默认配置)。这种配置是细粒度的,不同的Ribbon客户端可以使用不同的配置。
3.1 通过Java代码配置
创建一个Ribbon的配置类
package com.TransientBa.cloud.config;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* RibbonConfiguration class
* 该类不应该在主程序上下文的@ComponentScan中
* @author TransientBa
* @date 2019/1/28
*/
@Configuration
public class RibbonConfiguration {
@Bean
public IRule ribbonRule(){
return new RandomRule();
}
}
在创建一个空类用来选择应用指定的Ribbon配置类。
package com.TransientBa.cloud.config;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Configuration;
/**
* TestConfiguration class
* 使用RibbonClient注解来为特定name的Ribbon client定义配置
* configuration属性指向了刚刚我们自定义的RibbonConfiguration配置类
* @author TransientBa
* @date 2019/1/28
*/
@Configuration
@RibbonClient(name="microservice-provider-user",configuration = RibbonConfiguration.class)
public class TestConfiguration {
}
启动服务测试一下请求分布情况,依然访问http://localhost:8010/log-instance接口
可以看到【1】中日志显示请求被随机分发了。但这里需要注意的是:
RibbonConfiguration类不能包含在主应用程序上下文的@ComponentScan中,否则该类中的配置信息就被所有的@RibbonClient共享。
所以可以看到在红框【2】中目录的结构,SpringBoot默认扫描的位置在Application启动类的同级及下属子目录,我们将RibbonConfiguration类拿到了外面。
3.2 通过配置文件中的属性去自定义配置
上面通过Java代码进行了配置,这种方式要产生两个类,那有没有办法简化配置?
下面我们通过属性配置来达到同样的效果。
删除上面两个添加的配置类,只需要在yml配置文件中添加:
microservice-provider-user:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
启动服务测试后效果同Java配置。到这里我们可以看出使用属性配置明显比Java配置简化的多。
4 Ribbon独立使用配置整合
很多旧项目并不是配合Eureka使用,可能也不是SpringCloud开发的,这种情况下就需要单独的集成Ribbon。想要达到的架构效果如下:
4.1 配置使用
首先引用Ribbon的依赖spring-cloud-starter-ribbon,在POM中添加:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
引入好依赖后在yml文件中添加listOfServers,例如下图:
此处listOfServers为名字叫microservice-provider-user的Ribbon客户端设置请求的地址列表,即服务提供方的地址列表。