Ribbon简介
源码下载 https://yangjing191928.coding.net/public/spring-cloud-alibaba/spring-cloud-alibaba/git/files/Ribbon
Ribbon是Spring Cloud的一个组件, 它可以让我们使用一个注解就能轻松的搞定负载均衡,内置多种负载均衡策略,内部负载均衡顶级接口为com.netflix.loadbalancer.IRule:具体策略如下
1:BestAvailableRule 选择一个最小的并发请求的server逐个考察Server,如果Server被tripped了,则忽略,在选择其中ActiveRequestsCount最小的server
2:AvailabilityFilteringRule 过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(activeconnections 超过配置的阈值)使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个server的运行状态
3:WeightedResponseTimeRule 根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低。一个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单responsetime 减去每个server自己平均的responsetime是server的权重。当刚开始运行,没有形成statas 时,使用roubine策略选择server。
4:RetryRule 对选定的负载均衡策略机上重试机制在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server
5:RoundRobinRule 轮询方式轮询选择server轮询index,选择index对应位置的server
6:RandomRule 随机选择一个server 在index上随机,选择index对应位置的server
7:ZoneAvoidanceRule 复合判断server所在区域的性能和server的可用性选择server 使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接 数过多的Server。
修改配置
在需要使用负载均衡的服务,修改配置(appliaction.yml)来调整Ribbon的负载均衡策略;例如:商品服务
server:
port: 8081
spring:
application:
name: product-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
#ribbon要执行的负载均衡
product-service: # 调用的提供者的名称
ribbon:
NFLoadBalancerRuleClassName: com.share.shareconter.configure.Nacosweight
RestTemplate的生成方法上添加@LoadBalanced注解
在调用服务的地方添加注解@LoadBalanced。例如我们这里是订单服务调用商品服务,则需要在订单服务的RestTemplate生成方法上面添加注解@LoadBalanced
package com.yangjing.config;
import org.springframework.boot.web.client.RestTemplateBuilder;
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;
/**
* <p>
*
* </p>
*
* @author:yangjing2@foreveross.com
* @date:2021-04-16
*/
@Configuration
public class ApplicationConfig {
@Bean
@LoadBalanced //使用Ribbon负载均衡
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
改造方法
由于此处使用了Ribbon,所以调用方法需要改下。
说通俗点Ribbon将nacos上面注册的服务拉取到本地通过每个服务的服务名称可以替换之前写的ip+prot。
package com.yangjing.cotroller;
import com.alibaba.fastjson.JSON;
import com.yangjing.entity.Order;
import com.yangjing.entity.Product;
import com.yangjing.entity.UserDo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* <p>
*
* </p>
*
* @author:yangjing2@foreveross.com
* @date:2021-04-16
*/
@RestController
@RequestMapping("/order")
@Slf4j
public class OrderCotroller {
private static final String PRODUCT_URL = "http://localhost:8081/product/";
private static final String PRODUCT_SERVER = "product-service";
private static final String USER_URL = "http://localhost:8071/user/";
private static final String USER_SERVER = "user-service";
@Autowired
RestTemplate restTemplate;
//专门负责服务注册和发现的,我们可以通过它获取到注册到注册中心的所有服务
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/prod/{pid}")
public Order order3(@PathVariable("pid") Integer pid){
log.info(">>>客户下单,调用商品微服务查询商品信息<<<");
// 采用restTemplate调用
Product product = restTemplate.getForObject(PRODUCT_URL+"getProduct/"+pid, Product.class);
UserDo user = restTemplate.getForObject(USER_URL+"getUserInfo/"+pid,UserDo.class);
log.info(">>商品信息,查询结果:" + JSON.toJSONString(product));
log.info(">>用户信息,查询结果:" + JSON.toJSONString(user));
Order order = new Order();
order.setUid(user.getId());
order.setUsername(user.getName());
order.setPid(product.getId());
order.setPname(product.getName());
order.setPprice(product.getPrice());
order.setNumber(1);
return order;
}
/**
* 使用nacos方式范问相关接口信息
* @param pid
* @return
*/
@GetMapping("/prodNacos/{pid}")
public Order prodNacos(@PathVariable("pid") Integer pid){
log.info(">>>客户下单,调用商品微服务查询商品信息<<<");
// 采用restTemplate调用
//从nacos中获取服务地址 获取的是个list集群信息
ServiceInstance userInstance = discoveryClient.getInstances("user-service").get(0);
String userUrl = userInstance.getHost() + ":" +userInstance.getPort();
log.info(">>从nacos中获取到的用户的微服务地址为:" + userUrl);
UserDo user = restTemplate.getForObject("http://"+userUrl+"/user/getUserInfo/"+pid,UserDo.class);
ServiceInstance productInstance = discoveryClient.getInstances("product-service").get(0);
String productUrl = productInstance.getHost() + ":" +productInstance.getPort();
log.info(">>从nacos中获取到的商品的微服务地址为:" + productUrl);
Product product = restTemplate.getForObject("http://"+productUrl+"/product/getProduct/"+pid, Product.class);
log.info(">>商品信息,查询结果:" + JSON.toJSONString(product));
log.info(">>用户信息,查询结果:" + JSON.toJSONString(user));
Order order = new Order();
order.setUid(user.getId());
order.setUsername(user.getName());
order.setPid(product.getId());
order.setPname(product.getName());
order.setPprice(product.getPrice());
order.setNumber(1);
return order;
}
/**
* 使用ribbon方式范问相关接口信息
* @param pid
* @return
*/
@GetMapping("/prodRibbon/{pid}")
public Order prodRibbon(@PathVariable("pid") Integer pid){
log.info(">>>客户下单,调用商品微服务查询商品信息<<<");
// 采用restTemplate调用
//从nacos中获取服务地址 获取的是个list集群信息
/*ServiceInstance productInstance = discoveryClient.getInstances("product-service").get(0);
String productUrl = productInstance.getHost() + ":" +productInstance.getPort();
log.info(">>从nacos中获取到的商品的微服务地址为:" + productUrl);
Product product = restTemplate.getForObject("http://"+productUrl+"/product/getProduct/"+pid, Product.class);*/
//此处我删除了用户相关,由于restTemplate 配置了ribbon注解,但是用户服务未配置ribbon,此处调用会报错
log.info(">>从Ribbon中范问商品的微服务地址为:");
String productUrl = "product-service";
Product product = restTemplate.getForObject("http://"+productUrl+"/product/getProduct/"+pid, Product.class);
log.info(">>商品信息,查询结果:" + JSON.toJSONString(product));
Order order = new Order();
order.setPid(product.getId());
order.setPname(product.getName());
order.setPprice(product.getPrice());
order.setNumber(1);
return order;
}
}
我们将商品服务在启动一个
启动查看nacos是否注册成功(下图可以看到product-service注册了两个实例)
现在进行测试结果调用(执行两次)结果日志如下
Order log
>>商品信息,查询结果:{"pid":1,"pname":"商品_1","pprice":1000.0,"stock":5000}
>>>客户下单,调用商品微服务查询商品信息<<<
>>从Ribbon中范问商品的微服务地址为:
>>商品信息,查询结果:{"pid":1,"pname":"商品_1","pprice":1000.0,"stock":5000}
product log
商品信息查询成功,内容为{"pid":1,"pname":"商品_1","pprice":1000.0,"stock":5000}
product2 log
商品信息查询成功,内容为{"pid":1,"pname":"商品_1","pprice":1000.0,"stock":5000}