SpringCloud-Ribbon
1. Ribbon概述
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。
简单的说Ribbon就是netfix公司的一个开源项目,主要功能是提供客户端负载均衡算法和服务调用。Ribbon客户端组件提供了一套完善的配置项,比如连接超时,重试等。
在Spring Cloud 构建的微服务系统中, Ribbon 作为服务消费者的负载均衡器,有两种使用方式,一种是和RestTemplate相结合,另一种是和OpenFeign相结合。OpenFeign 已经默认集成了Ribbon。Ribbon 有很多子模块,但很多模块没有用于生产环境!
1.1控制器
模拟多种请求类型以及参数传递
@RestController
public class ProductController {
@GetMapping("/testGet")
public String getTest(String name)
{
return "hello,spring..cloud...get,name="+name;
}
@PostMapping("/testPost")
public String testPost(String name,Integer age)
{
return "hello,spring...cloud..post,name="+name+",age="+age;
}
@PostMapping("/testJson")
public String testJson(@RequestBody User user)
{
return "json,{user.name:"+user.getName()+",user.age:"+user.getAge()+
",user.hobby:"+user.getHobby()+"}";
}
}
1.2测试参数传递
/**
* 测试发送get请求
*/
@Test
void testGet() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/testGet?name=zhangsan";
ResponseEntity<String> result = restTemplate.getForEntity(url, String.class);
System.out.println(result.getStatusCodeValue());
}
/**
* 测试发送post表单参数
*/
@Test
void testPost() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/testPost";
LinkedMultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("name", "zhangsan");
map.add("age", "18");
ResponseEntity<String> result = restTemplate.postForEntity(url, map, String.class);
System.out.println(result.getStatusCodeValue());
}
/**
* 测试发送post JSON参数
*/
@Test
void testPost2() {
RestTemplate restTemplate = new RestTemplate();
String url = "http://localhost:8080/testPost2";
User user = new User();
user.setName("zhangsan");
user.setAge(18);
user.setHobby("编码");
ResponseEntity<String> result = restTemplate.postForEntity(url, user, String.class);
System.out.println(result.getStatusCodeValue());
}
2. 负载均衡
负载均衡,英文名称为Load Balance(LB)http:// lb:// ,其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,例如Web服务器、企业核心应用服务器和其它主要任务服务器等,从而协同完成工作任务。
负载均衡构建在原有网络结构之上,它提供了一种透明且廉价有效的方法扩展服务器和网络设备的带宽、加强网络数据处理能力、增加吞吐量、提高网络的可用性和灵活性。
Ribbon支持多种负载均衡算法,还支持自定义的负载均衡算法。
Ribbon只是一个工具类框架,比较小巧,Spring Cloud对它封装后使用也非常方便,它不像服务注册中心、配置中心、API网关那样需要独立部署,Ribbon只需要在代码直接使用即可;
Ribbon 与 Nginx 的区别
Ribbon是客户端的负载均衡工具,而客户端负载均衡和服务端负载均衡最大的区别在于服务清单所存储的位置不同,在客户端负载均衡中,所有客户端节点下的服务端清单,需要自己从服务注册中心上获取,比如Eureka服务注册中心。同服务端负载均衡的架构类似,在客户端负载均衡中也需要心跳去维护服务端清单的健康性,只是这个步骤需要与服务注册中心配合完成。在Spring Cloud中,由于Spring Cloud对Ribbon做了二次封装,所以默认会创建针对Ribbon的自动化整合配置;
在Spring Cloud中,Ribbon主要与RestTemplate对象配合起来使用,Ribbon会自动化配置RestTemplate对象,通过@LoadBalanced开启RestTemplate对象调用时的负载均衡。
3.自定义实现负载均衡
3.1创建注册中心eureka
server:
port: 9100
spring:
application:
name: eureka-server
eureka:
client:
register-with-eureka: false #由于我们目前创建的应用是一个服务注册中心,而不是普通的应用,默认情况下,这个应用会向注册中心(也是它自己)注册它自己,设置为false表示禁止这种自己向自己注册的默认行为
fetch-registry: false #由于我们目前创建的应用是一个服务注册中心,而不是普通的应用,默认情况下,这个应用会向注册中心(也是它自己)注册它自己,设置为false表示禁止这种自己向自己注册的默认行为
3.2创建提供者项目product-service1
3.3配置服务器信息
server:
port: 8080
spring:
application:
name: product-service
eureka:
client:
service-url:
defaultZone: http://localhost:9100/eureka
3.4创建控制器代码
@GetMapping("/product")
public String queryProduct(){
return "商品查询成功,小米笔记本,质量杠杠滴-8081";
}
3.5复制一份项目product-service2
将配置和代码中的端口改为8082,应用程序名称produc-service
3.6创建消费者项目orderservice
server.port=8080
spring.application.name=order-service
eureka.client.service-url.defaultZone=http://localhost:9100/eureka
7.自定义负载均衡
@RestController
public class TestController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
static Random random = new Random();
@GetMapping("/testBalance")
public String testBalance(String serviceId) {
//获取服务列表
List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
if (ObjectUtils.isEmpty(instances)) {
return "服务列表为空";
}
//如果服务列表不为空,先自己做一个负载均衡
ServiceInstance serviceInstance = loadBalance(instances);
String host = serviceInstance.getHost();
int port = serviceInstance.getPort();
String url = "http://" + host + ":" + port + "/product";
System.out.println("本次我调用的是" + url);
String forObject = restTemplate.getForObject(url, String.class);
System.out.println(forObject);
return forObject;
}
private ServiceInstance loadBalance(List<ServiceInstance> instances) {
//拼接url去调用 ip:port 先自己实现不用ribbon
ServiceInstance serviceInstance = instances.get(random.nextInt(instances.size()));
return serviceInstance;
}
}
8.使用Ribbon实现负载均衡
@Bean
@LoadBalanced // ribbon的负载均衡注解
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Autowired
private RestTemplate restTemplate;
@GetMapping("/order")
public String order(){
String url="http://product-service/product";
String result=restTemplate.getForEntity(url,String.class).getBody();
return "订单成功了..."+result;
}
4.负载均衡原理
先通过 “http://” + serviceId + “/info” 我们思考ribbon在真正调用之前需要做什么?
restTemplate.getForObject(“http://product-service/product”, String.class);
想要把上面这个请求执行成功,我们需要以下几步
1. 拦截该请求;
2. 获取该请求的URL地址:http://product-service/product
3. 截取URL地址中的provider
4. 从服务列表中找到key为provider的服务实例的集合(服务发现)
5. 根据负载均衡算法选出一个符合的实例
6. 拿到该实例的host和port,替换原来URL中的provider
7. 真正的发送restTemplate.getForObject(“http://ip:port/product”,String.class)
4.1负载均衡测试
@Autowired
private LoadBalancerClient loadBalancerClient;
@RequestMapping("/testChoose")
public String testChoose(String serviceId) {
ServiceInstance choose = loadBalancerClient.choose(serviceId);
System.out.println(choose.getHost() + ":" + choose.getPort());
return choose.toString();
}
4.2 负载均衡之前的服务列表是从何而来呢?
Ribbon里面有没有服务列表?
Ribbon只做负载均衡和远程调用
服务列表从哪来?从eureka来
Ribbon有一个核心接口 ILoadBalance(承上(eureka)启下(Rule))
我们发现在负载均衡之前,服务列表已经有数据了
Ribbon没有服务发现的功能,但是eureka有,所以ribbon和eureka完美结合
Ribbon中使用了一个PING机制
从eureka中拿到服务列表,缓存到本地,ribbon搞了个定时任务,隔一段时间就去循环ping一下每个服务节点是否存活
4.3 Ribbon负载均衡的算法
1.RoundRobinRule–轮询 请求次数 % 机器数量
2.RandomRule–随机
3.AvailabilityFilteringRule --会先过滤掉由于多次访问故障处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对于剩余的服务列表按照轮询的策略进行访问
4.WeightedResponseTimeRule–根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越大。刚启动时如果同统计信息不足,则使用轮询的策略,等统计信息足够会切换到自身规则
5.RetryRule-- 先按照轮询的策略获取服务,如果获取服务失败则在指定的时间内会进行重试,获取可用的服务
6.BestAvailableRule --会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量小的服务
7.ZoneAvoidanceRule – 默认规则,复合判断Server所在区域的性能和Server的可用行选择服务器。
5.LoadBalancer
SpringCloud官方推出的用于负载均衡的组件,用法和ribbon一样,在新版的springCloud中Ribbon已经被官方弃用了。如果使用eureka作为注册中心,那么可以使用ribbon来做负载均衡,如果不使用eureka来做注册中心,也可以选择loadBalancer来做负载均衡。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>