负载均衡Robbin
在刚才的案例中,我们启动了一个user-service,然后通过DiscoveryClient来获取服务实例信息,然后获取ip和端口来访问。
但是实际环境中,我们往往会开启很多个user-service的集群。此时我们获取的服务列表中就会有多个,到底该访问哪一个呢?
一般这种情况下我们就需要编写负载均衡算法,在多个实例列表中进行选择。
不过Eureka中已经帮我们集成了负载均衡组件:Ribbon,简单修改代码即可使用。
什么是Ribbon:

接下来,我们就来使用Ribbon实现负载均衡。
7.1.启动3个user-service服务实例
根据前面的 user-service 子工程创建的步骤来创建 user-service1和 user-service2 工程。所有的文件配置信息都一样。然后修改user-service1的 server.port 为 8085,user-service2的 server.port 为 8086
项目结构如下:

user-service的 application.yml
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/leyou
username: root
password: root
application:
name: user-service
mybatis:
type-aliases-package: cn.clocktt.pojo
eureka:
client:
service-url:
defaultZone: http://localhost:8083/eureka ,http://localhost:8084/eureka
instance:
#10秒即过期
lease-expiration-duration-in-seconds: 10
#5秒一次心跳
lease-renewal-interval-in-seconds: 5
user-service1和user-service2的配置信息和上述一样,只是端口不同
user-service1的 server.port 为 8085,user-service2的 server.port 为 8086
启动这三个服务 ,我们可以看到 这三个服务都注册成功了

7.2.开启负载均衡
在 user-consumer
因为Eureka中已经集成了Ribbon,所以我们无需引入新的依赖。直接修改代码:

在RestTemplate的配置方法上添加@LoadBalanced注解:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
完整的user-consumer启动类
package cn.clocktt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
public class UserConsumerApplication {
@Bean
//开启负载均衡
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(UserConsumerApplication.class,args);
}
}
修改调用方式,不再手动获取ip和端口,而是直接通过服务名称调用:
package cn.clocktt.controller;
import cn.clocktt.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
@Controller
public class WebController {
@Autowired
private RestTemplate restTemplate;
@ResponseBody
@RequestMapping("/consumer/user")
public User queryUserById(long id){
//格式
//http://服务id/RequestMapping
String url = "http://user-service/user/"+id;
System.out.println(url);
User user = restTemplate.getForObject(url, User.class);
return user;
}
}
访问页面,查看结果:

成功
7.3.源码跟踪
为什么我们只输入了service名称就可以访问了呢?之前还要获取ip和端口。
显然有人帮我们根据service名称,获取到了服务实例的ip和端口。它就是LoadBalancerInterceptor
我们进行源码跟踪:

继续跟入execute方法:发现获取了8086端口的服务

再跟下一次,发现获取的是8081:

7.4.负载均衡策略
Ribbon默认的负载均衡策略是简单的轮询,我们可以测试一下:
编写测试类,在刚才的源码中我们看到拦截中是使用RibbonLoadBalanceClient来进行负载均衡的,其中有一个choose方法,是这样介绍的:

现在这个就是负载均衡获取实例的方法。
我们对注入这个类的对象,然后对其测试:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = UserConsumerDemoApplication.class)
public class LoadBalanceTest {
@Autowired
RibbonLoadBalancerClient client;
@Test
public void test(){
for (int i = 0; i < 100; i++) {
ServiceInstance instance = this.client.choose("user-service");
System.out.println(instance.getHost() + ":" + instance.getPort());
}
}
}
结果:

符合了我们的预期推测,确实是轮询方式。
我们是否可以修改负载均衡的策略呢?
继续跟踪源码,发现这么一段代码:

我们看看这个rule是谁:

这里的rule默认值是一个RoundRobinRule,看类的介绍:

这不就是轮询的意思嘛。
我们注意到,这个类其实是实现了接口IRule的,查看一下:

定义负载均衡的规则接口。
它有以下实现:

SpringBoot也帮我们提供了修改负载均衡规则的配置入口:
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
格式是:{服务名称}.ribbon.NFLoadBalancerRuleClassName,值就是IRule的实现类。
服务名称:要对哪个服务指定负载均衡规则
3种规则
com.netflix.loadbalancer.RandomRule 随机
com.netflix.loadbalancer.RoundRobinRule 轮询
com.netflix.loadbalancer.RetryRule
再次测试,发现结果变成了随机:


浙公网安备 33010602011771号