1.简介
Ribbon是Netflix公司开源的一个 客户端
负载均衡的项目(https://github.com/Netflix/ribbon),它是一个基于HTTP、TCP的客户端负载均衡器。
2. 什么是负载均衡?
负载均衡是微服务架构中必须使用的技术,通过负载均衡来实现系统的高可用、集群扩容等功能。负载均衡可通过硬件设备及软件来实现,硬件比如:F5、Array等,软件比如:LVS、Nginx等。
2.1 服务端负载均衡
用户请求先到达负载均衡器(也相当于一个服务),负载均衡器根据负载均衡算法将请求转发到微服务。负载均衡算法有:轮训、随机、加权轮训、加权随机、地址哈希等方法,负载均衡器维护一份服务列表,根据负载均衡算法将请求转发到相应的微服务上,所以负载均衡可以为微服务集群分担请求,降低系统的压力。
2.2 客户端负载均衡
1、在消费微服务中使用Ribbon实现负载均衡,Ribbon先从EurekaServer中获取服务列表。
2、Ribbon根据负载均衡的算法在本地去调用微服务。
3. Ribbon原理/架构
总结: Ribbon其实就是一个软负载均衡的客户端组件, 他可以和其他所需请求的客户端结合使用,和eureka结合只是其中一个实例。
4. LB(Load Balance)
4.1 集中式LB
4.2 进程内LB
5. Ribbon核心组件IRule
5.1 IRule
根据特定算法从服务列表中选取一个要访问的服务,大致有7种算法:
com.netflix.loadbalancer.RoundRobinRule
—- 轮询com.netflix.loadbalancer.RandomRule
—-随机com.netflix.loadbalancer.RetryRule
—-先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务WeightedResponseTimeRule
—-对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择BestAvailableRule
—-会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务AvailabilityFilteringRule
—- 先过滤掉故障实例,再选择并发较小的实例ZoneAvoidanceRule
—- 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
5.2 如何修改算法
5.2.1 配置细节
意思就是不要放在启动类下的包下
5.2.2 新建package
5.2.3 配置类
package com.atguigu.myrule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author JShawn 2021/3/23 11:26
*/
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
// 定义为随机
return new RoundRobinRule();
}
}
5.2.4 主启动类添加@RibbonClient
指定调用的服务名+负载均衡算法配置类
5.3 Ribbon负载均衡算法
5.3.1 原理
5.3.2 源码(略)
5.3.3 自己实现
5.3.3.1 服务消费方
5.3.3.1 RestTemplate去掉注解@LoadBalanced
/**
* 充当applicationContext.xml <bean id="" class="">
* @author JShawn 2021/3/14 23:46
*/
@Configuration
public class ApplicationContextConfig {
@Bean
// 轮循多节点实例
// @LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
5.3.3.2 LoadBalancer接口
public interface LoadBalaner {
ServiceInstance instance(List<ServiceInstance> instances);
}
5.3.3.3 MyLB
/**
* @author JShawn 2021/3/23 12:42
* 自己实现LoadBalance负载均衡 轮询 算法
*/
@Component
public class MyLB implements LoadBalaner {
private AtomicInteger atomicInteger = new AtomicInteger(0);
public final int getIncrement() {
int current;
int next;
do {
current = this.atomicInteger.get();
next = current >= Integer.MAX_VALUE ? 0 : current+1;
}while (!this.atomicInteger.compareAndSet(current,next));
System.out.println("------------第几次访问:次数next"+next);
return next;
}
@Override
public ServiceInstance instance(List<ServiceInstance> instances) {
// 总请求数 % 实例数
int index = getIncrement() % instances.size();
return instances.get(index);
}
}
5.3.3.4 Controller
@GetMapping("payment/lb")
public String getPaymentLB() {
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
if (instances == null || instances.size() == 0) {
return null;
}
ServiceInstance serviceInstance = loadBalaner.instance(instances);
URI uri = serviceInstance.getUri();
System.out.println(uri+"/payment/lb");
return restTemplate.getForObject(uri+"/payment/lb",String.class);
// return uri+"/payment/lb";
}
5.3.3.2 服务提供方
@GetMapping(value = "/payment/lb")
public String getPaymentLB() {
return port;
}
6. RestTemplate
6.1 配置
package com.atguigu.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* @author JShawn 2021/3/14 23:46
*/
@Configuration
public class ApplicationContextConfig {
//相当于applicationContext.xml <bean id="" class="">,加入IOC容器
@Bean
// 使用Ribbon提供的负载均衡算法,轮训多节点实例
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
6.2 API
6.3 getForObject方法/getForEntity方法
6.3.1 getForObject()
返回的对象为响应体中数据转化成的对象,基本可以理解为Json
6.3.2 getForEntity()
更强大,返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等
7. 在客户端实现负载均衡
即
服务消费方
可以直接调用服务提供方的多实例
而不用再关心地址和端口号,且该服务还有负载功能了,一句话Ribbon+RestTemplate
远程调用
Spring Cloud引入Ribbon配合 restTemplate 实现客户端负载均衡与远程调用,实际就是轮询服务端多节点实例(默认采用的是轮询机制)。Java中远程调用的技术有很多,如:webservice、socket、rmi、Apache HttpClient、OkHttp等,互联网项目使用基于http的客户端较多,本项目使用OkHttp。
原理:由于Ribbon实现客户端负载均衡是基于RestTemplate,所以在RestTemplate的@Bean上加@LoadBalanced注解,添加@LoadBalanced注解后,restTemplate会走LoadBalancerInterceptor拦截器,此拦截器中会通过RibbonLoadBalancerClient查询服务地址,可以在此类打断点观察每次调用的服务地址和端口,两个cms服务(服务端)会轮流被调用。
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
8. 客户端配置
ribbon:
MaxAutoRetries: 2 #最大重试次数,当Eureka中可以找到服务,但是服务连不上时将会重试
MaxAutoRetriesNextServer: 3 #切换实例的重试次数
OkToRetryOnAllOperations: false #对所有操作请求都进行重试,如果是get则可以,如果是post,put等操作没有实现幂等的情况下是很危险的,所以设置为false
ConnectTimeout: 5000 #请求连接的超时时间
ReadTimeout: 6000 #请求处理的超时时间
9. 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring‐cloud‐starter‐ribbon</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
10. 使用
/**
* 测试负载均衡调用
*/
@Test
public void testRibbon() {
//服务id
String serviceId = "XC‐SERVICE‐MANAGE‐CMS";
for(int i=0;i<10;i++){
//通过服务id调用
ResponseEntity<CmsPage> forEntity = restTemplate.getForEntity("http://" + serviceId
+ "/cms/page/get/5a754adf6abb500ad05688d9", CmsPage.class);
CmsPage cmsPage = forEntity.getBody();
System.out.println(cmsPage);
}
}