1、Spring Cloud Ribbon
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。它是一个基于HTTP和TCP的客户端负载均衡器。它可以通过在客户端中配置ribbonServerList来设置服务端列表去轮询访问以达到均衡负载的作用。
当Ribbon与Eureka联合使用时,ribbonServerList会被DiscoveryEnabledNIWSServerList重写,扩展成从Eureka注册中心中获取服务实例列表。同时它也会用NIWSDiscoveryPing来取代IPing,它将职责委托给Eureka来确定服务端是否已经启动。
2、代码演示
创建子模块,命名为microservice-consumer-movie-ribbon,并且在pom.xml文件中添加相关依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies>
在eureka的坐标中,已经包含了ribbon的坐标。
application.yml
spring:
application:
name: microservice-consumer-movie
server:
port: 3001
eureka:
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://admin:admin123@localhost:1001/eureka
instance:
prefer-ip-address: true
instance-id: ${spring.application.name}:${spring.cloud.client.ipAddress}:${spring.application.instance_id:${server.port}}
应用主类
@SpringBootApplication @EnableEurekaClient public class MainAppConsumer { @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(MainAppConsumer.class, args); } }
可以看到,我们在RestTemplate上面添加了一个注解@LoadBalanced,它可以让这个RestTemplate在请求的时候拥有负载均衡的能力。
User实体类
public class User implements Serializable { private Long id; private String username; private String name; private short age; private BigDecimal balance; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getName() { return name; } public void setName(String name) { this.name = name; } public short getAge() { return age; } public void setAge(short age) { this.age = age; } public BigDecimal getBalance() { return balance; } public void setBalance(BigDecimal balance) { this.balance = balance; } }
创建接口
@RestController public class MovieController { @Autowired private RestTemplate restTemplate; @Autowired private LoadBalancerClient loadBalancerClient; @GetMapping("/movie/{id}") public User getById(@PathVariable("id") Long id){ return this.restTemplate.getForObject("http://microservice-provider-user/getUser/"+id,User.class); } @GetMapping("/test") public void test(){ ServiceInstance instance = loadBalancerClient.choose("microservice-provider-user"); System.out.println(instance.getServiceId() + "===" + instance.getHost() + "===" + instance.getPort()); } }
可以看到这里,我们注入了LoadBalancerClient
和RestTemplate
,并在/test接口中,先通过loadBalancerClient
的choose
函数来负载均衡的选出一个microservice-provider-user的服务实例,这个服务实例的基本信息存储在ServiceInstance
中,我们打印其中的Host和Port,最后再利用RestTemplate
对象实现对服务提供者接口的调用。
我们启动microservice-discovery-eureka,然后再分别用2001,2002这两个端口启动microservice-provider-user,最后启动microservice-consumer-movie-ribbon这个模块,访问http://localhost:3001/movie/1接口进行测试,页面响应:
{"id":1,"username":"user1","name":"张三","age":18,"balance":100.00}
多次访问http://localhost:3001/test,控制台打印
microservice-provider-user===192.168.1.7===2001 microservice-provider-user===192.168.1.7===2002 microservice-provider-user===192.168.1.7===2001 microservice-provider-user===192.168.1.7===2002 microservice-provider-user===192.168.1.7===2001 microservice-provider-user===192.168.1.7===2002
因为我们的服务提供者通过两个端口,启动了两次,所以,当我们多次访问/test接口的时候,ribbon会基于轮询的负载均衡算法,依次调用这两个服务,所以看到2001调用一次之后,接着会调用2002端口。
ribbon中除了轮询的负载均衡算法,还存在随机负载均衡(Random),加权响应时间负载均衡(WeightedResponseTime)等,可以参考https://yq.aliyun.com/articles/61823
我们通过自定义配置,来修改ribbon默认的轮询负载均衡。
在src目录下创建一个包,命名为config(避免与应用主类同包或在其子包下面),然后创建一个类,命名为TestConfiguration,代码如下所示:
@Configuration public class TestConfiguration { @Bean public IRule ribbonRule() { return new RandomRule(); } }
@Configuration是spring中的一个注解,用于定义配置类,可以替换掉xml文件。@Bean注解定义在方法上,可以将方法的返回值作为一个对象注入IOC容器中,以供使用。
之后,修改应用启动主类,在类上添加:
@RibbonClient(name = "microservice-provider-user", configuration = TestConfiguration.class)
来引入我们创建的这个配合类。
上述完成后,我们还是按照之前的启动顺序,依次启动eureka,user提供者(2001,2002),movie消费者,然后访问消费者中的两个接口进行测试。
访问/movie/1接口,页面响应
{"id":1,"username":"user1","name":"张三","age":18,"balance":100.00}
多次访问/test接口,控制台打印
microservice-provider-user===192.168.1.7===2002 microservice-provider-user===192.168.1.7===2001 microservice-provider-user===192.168.1.7===2002 microservice-provider-user===192.168.1.7===2001 microservice-provider-user===192.168.1.7===2001 microservice-provider-user===192.168.1.7===2001 microservice-provider-user===192.168.1.7===2002 microservice-provider-user===192.168.1.7===2002
可以看到它们使用哪个端口进行响应是随机的,这就是ribbon中的随机负载均衡。
我们还可以使用配置文件进行配置ribbon的负载均衡算法。
只需要在application.yml中添加如下内容:
microservice-provider-user:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
当然要将之前配置类的方式去掉。