Ribbon入门介绍,
Ribbon负载均衡演示,
RestTemplate详解,
Ribbon核心组件IRule,
Ribbon负载均衡算法,
Ribbon入门介绍
- Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具.
- Ribbon主要功能是提供客户端的软件负载均衡算法和服务调用.
- Ribbon客户端组件提供一系列完整的配置项如连接超时, 重试等. 简单的说, 就是在配置文件中列出Load Balancer后所有的机器, Ribbon会自动帮我们基于某种规则(如简单轮询, 随机连接)去链接这些机器, 我们很容易使用Ribbon实现自定义的负载均衡算法.
- 简单的说就是将用户的请求平摊到多个服务上, 从而达到系统的高可用, 常见的负载均衡有软件Nginx, LVS, 硬件F5等.
- Ribbon负载均衡 VS Nginx负载均衡
- Nginx是服务器负载均衡, 客户端所有请求都会交给nginx, 然后由nginx实现转发请求, 即负载均衡是由服务端实现的.
- Ribbon是客户端负载均衡, 在调用微服务接口的时候, 会在注册中心上获取注册信息服务列表之后缓存到JVM本地, 从而在本地实现RPC远程服务调用技术.
- 集中式LB
- 在服务的消费方和提供方之间使用独立的LB设施(如Nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方.
- 进程内LB
- 将LB逻辑集成到消费方, 消费方从服务注册中心获知哪些地址可用, 然后自己再从这些地址中选出一个合适的服务器.
- Ribbon就属于进程内LB, 它只是一个类库, 集成于消费方进程, 消费方通过它来获取到服务提供方的地址.
Ribbon负载均衡演示
- 所谓Ribbon, 简单的说就是负载均衡 + RestTemplate
- 架构说明
- Ribbon在工作时分两步
- 第一步先选择Eureka Server, 它优先选择在同一个区域内负载较少的server.
- 第二步再根据用户指定的策略, 在从server取到的服务注册列表中选择一个地址.
- 策略有多种可选, 如轮询, 随机, 根据响应时间加权.
- 这里需要注意, Ribbon其实就是一个软负载均衡的客户端组件, 它可以和其他所需请求的客户端结合使用, 这里仅用Eureka举例.
- 我们只加入了eureka的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 而在Eureka中是有Ribbon的依赖的
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.1.RELEASE</version>
<scope>compile</scope>
</dependency>
Ribbon核心组件IRule
- IRule接口: 根据特定算法从服务列表中选取一个要访问的服务
- 有7种负载均衡策略
- 我们要写的自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下, 否则我们自定义的配置类就会被所有的Ribbon客户端共享, 达不到特殊化定制的目的了.
- 我们新建package及配置类
- 配置类代码
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
//将负载均衡策略改为随机
return new RandomRule();
}
}
- 主启动类上要加@RibbonClient注解
- name属性: 服务提供者(负载均衡请求的接收方)
- configuration属性: 指明我们的配置类
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
@EnableEurekaClient
@SpringBootApplication
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class, args);
}
}
- 测试: http://localhost/consumer/payment/get/1
Ribbon负载均衡算法
- rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标.
- 集群总数量: 2台. (List = 2 instance)
- 第一次: 1 % 2 = 1
- 第二次: 2 % 2 = 0
- 第三次: 3 % 2 = 1
- ......
- 如果重启了, 那么会从1重新开始.
- 在8001/8002微服务的controller添加如下方法
@GetMapping("/payment/lb")
public String getPaymentLB() {
return serverPort;
}
- 80订单微服务改造
- 去掉@LoadBalanced注解
- LoadBalancer接口
public interface LoadBalancer {
ServiceInstance instances(List<ServiceInstance> serviceInstances);
}
- MyLB
@Component
public class MyLB implements LoadBalancer {
private AtomicInteger atomicInteger = new AtomicInteger(0);
public final int getAndIncrement() {
int current;
int next;
do {
current = this.atomicInteger.get();
next = current >= 2147483000 ? 0 : current+1;
} while(!this.atomicInteger.compareAndSet(current, next));
return next;
}
@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index = getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
}
- controller方法
@Resource
private RestTemplate restTemplate;
@Resource
private LoadBalancer loadBalancer;
@Resource
private DiscoveryClient discoveryClient;
@GetMapping("/consumer/payment/lb")
public String getPaymentLB() {
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
if(instances == null || instances.size() <= 0) {
return null;
}
ServiceInstance serviceInstance = loadBalancer.instances(instances);
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri+"/payment/lb", String.class);
}
- 测试: http://localhost/consumer/payment/lb