思考一个问题:如果为同一个提供者在Eureka中注册多个服务?客户端应该如何选择服务呢?

这时,就需要在客户端实现服务的负载均衡。而在Spring Cloud中推荐使用Ribbon来实现负载均衡。

 
1、Ribbon简介

Ribbon是Netflix发布的负载均衡器。它有助于控制HTTP和TCP客户端的行为。为Ribbon配置服务提供者地址列表后, Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。Ribbon默认为我们提供了很多的负载均衡算法,例如轮询、随机等。当然,我们也可为 Ribbon实现自定义的负载均衡算法。

 
2、架构
如何在客户端实现服务的负载均衡

 
3、开始使用Ribbon

 

3.1. 为microservice order增加ribbon依赖
如何在客户端实现服务的负载均衡
<dependency>    <groupId>org.springframework.cloud</groupId>    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId></dependency>

其实,该依赖是可以省略的,因为spring-cloud-starter-netflix-eureka-client中已经包含了spring-cloud-starter-netflix-ribbon:
 如何在客户端实现服务的负载均衡


3.2. 为RestTemplate设置@LoadBalanced注解

@SpringBootApplication@EnableEurekaClient@EnableFeignClientspublic class OrderApp {    public static void main(String[] args) {        SpringApplication.run(OrderApp.class, args);    }    @Bean    @LoadBalanced //使用负载均衡    public RestTemplate restTemplate() {        return new RestTemplate();    }}

3.3. 改造ItemService的实现

 

@Servicepublic class ItemService {    // Spring框架对RESTful方式的http请求做了封装,来简化操作    @Autowired    private RestTemplate restTemplate;    public Item queryItemById(Long id) {        // 该方法走eureka注册中心调用(去注册中心根据app-item查找服务,这种方式必须先开启负载均衡@LoadBalanced)        String itemUrl = "http://app-item/item/{id}";        Item result = restTemplate.getForObject(itemUrl, Item.class, id);        System.out.println("订单系统调用商品服务,result:" + result);        return result;    }}

可以发现,实现更加简化了。

这种方式关键在于获取RestTemplat对象时要加上@LosdBalanced注解,否则restTemplate.getFor Object方法会报java.net.UnknownHostException: app-item,而前面一种方式是手动指定了获取的服务实例,不需要加此注解。

 

提示: 如果这里使用了@FeignClient完成的Restful的调用同样也适用。

 

3.4. 重启订单服务进行测试

测试结果:
 
microservice-item

3.5. 测试负载均衡

 

测试方法:
第一步,启动2个microservice-item服务(多个也可以):
 microservice-item

第二步,导入测试依赖,编写单元测试用例:

<!-- 引入SpringBoot的单元测试 --><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-test</artifactId>    <scope>test</scope></dependency>

第三步,编写测试用例:

@RunWith(SpringJUnit4ClassRunner.class)@SpringBootTest@Import(OrderApp.class)public class ItemServiceTest {    @Autowired    private LoadBalancerClient loadBalancerClient;//自动注入    @Test    public void test() {        String serviceId = "app-item";        for (int i = 0; i < 100; i++) {            ServiceInstance serviceInstance = this.loadBalancerClient.choose(serviceId);            System.out.println("第" + (i + 1) + "次:" + serviceInstance.getHost() + ": " + serviceInstance.getPort());        }    }}

4、设置负载均衡策略

只需要在配置文件中添加配置
serviceId.ribbon.NFLoadBalancerRuleClassName=自定义的负载均衡策略类,

例如在order微服务的yml配置文件中添加:

app-item:  ribbon:    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

其中app-item是要访问的服务id。

测试:

第1次:127.0.0.1: 8081

第2次:127.0.0.1: 9081

第3次:127.0.0.1: 9081

第4次:127.0.0.1: 8081

第5次:127.0.0.1: 9081

第6次:127.0.0.1: 8081

第7次:127.0.0.1: 9081

第8次:127.0.0.1: 9081

第9次:127.0.0.1: 8081

第10次:127.0.0.1: 8081

...

5、其它策略

接口:com.netflix.loadbalancer.IRule,其实现类:
策略描述:

 

AbstractLoadBalancerRule

负载均衡的抽象类,负责获得负载均衡器并保存在内部,通过负载均衡器维护的信息作为分配的依据,并以此设计一些算法来实现针对特定场景的高效策略。


RoundRobinRule

Ribbon默认的负载均衡机制,该策略实现了按照线性轮询的方式以此选择每个服务实例的功能。轮询index,选择index对应位置的server。

 

RandomRule

该策略实现了从服务实例清单中随机选择一个服务实例的功能。通过在index上随机,选择index对应位置的server。


RetryRule

该策略实现了一个具备重试机制的实例选择功能。该策略下在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server。