理解 Ribbon 并自己实现负载均衡
负载均衡(LB)是什么
对于用户的某个请求,将有多个相同功能的服务点服务该请求,某个服务点挂了,其他服务点还是可以进行服务,这样就实现了系统的高可用。
关于集中式 LB 和进程内 LB
集中式 LB
在服务的消费方和提供方之间使用独立的 LB 设施,(软硬件均可,软件如 Nginx,硬件如 F5),由该设施负责把访问请求通过某种策略(可自行指定)转发至服务的提供方。
进程内 LB
将 LB 逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务点进行服务。
Ribbon 属于进程内 LB,它只是一个类库,集成于消费方进程,消费方通过它获取服务提供方的地址。
使用 Ribbon 实现负载均衡
关于导包
1<dependency>
2 <groupId>org.springframework.cloud</groupId>
3 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <!-- 已经包含了ribbon -->
4</dependency>
开启注解
1@Configuration
2public class ApplicationContextConfig {
3 @Bean
4 @LoadBalanced // 赋予负载均衡能力
5 public RestTemplate getRestTemplate() {
6 return new RestTemplate();
7 }
8}
访问相同服务名地址即可。
修改 Ribbon 负载均衡规则
所有规则均实现了 IRule 接口,通过查看接口实现类即可知道规则的种类。
默认是 RoundRobinRule(轮询)这一规则。
下面修改为 RandomRule(随机)这一规则:
在启动类扫描不到的包下创建规则:
1@Configuration
2public class MyRibbonRule {
3 @Bean
4 public IRule myRule() {
5 return new RandomRule();
6 }
7}
在启动类指定规则:
1@SpringBootApplication
2@EnableEurekaClient
3@RibbonClient(name="CLOUD-PAYMENT-SERVICE", configuration = MyRibbonRule.class)
4public class OrderMain80 {
5 public static void main(String[] args) {
6 SpringApplication.run(OrderMain80.class, args);
7 }
8}
自己实现负载均衡
编写 LB 接口即实现类
要实现负载均衡,首先应获取得到所有的服务实例 ServiceInstance
。
1public interface LoadBalancer {
2 ServiceInstance getServiceInstance(List<ServiceInstance> instances);
3}
通过自旋锁获取新值,取余 ServiceInstance 个数,得到目标 ServiceInstance 下标。
1@Component
2public class MyLoadBalancer implements LoadBalancer {
3 private AtomicInteger aint = new AtomicInteger(0);
4 public final int myCAS() {
5 int expect, next;
6 for (;;) {
7 expect = aint.get();
8 next = (expect + 1) % Integer.MAX_VALUE;
9 if (aint.compareAndSet(expect, next)) return next;
10 }
11 }
12 @Override
13 public ServiceInstance getServiceInstance(List<ServiceInstance> instances) {
14 if (instances == null || instances.size() <= 0) return null;
15 return instances.get(myCAS() % instances.size());
16 }
17}
编写 Controller
注意需要通过获取得到的 ServiceInstance 的 uri 作为访问前缀。
1@Resource
2private RestTemplate restTemplate;
3
4@Resource
5private EurekaDiscoveryClient discoveryClient;
6
7@Resource
8private LoadBalancer loadBalancer;
9
10@GetMapping("/consumer/payment/lb")
11public String lbTest() {
12 List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
13 ServiceInstance instance = loadBalancer.getServiceInstance(instances);
14 log.info("lbtest: " + instance.getUri().toString());
15
16 return restTemplate.getForObject(instance.getUri() + "/payment/lb", String.class);
17}
接着访问 /consumer/payment/lb
接口即可完成负载均衡的测试。