我现在启动2个微服务 user和Order
我现在user--->order服务
@Autowired
DiscoveryClient discoveryClient;
@GetMapping("/getUser2")
public String getUser2(){
List<ServiceInstance> orderList = discoveryClient.getInstances("mall-order");
if(ObjectUtils.isEmpty(orderList)){
return "微服务没有调用";
}
String url = orderList.get(0).getUri().toString();
ResponseEntity<String> responseEntity = restTemplate. getForEntity(url, String.class);
String resp = responseEntity.getBody().toString();
log.info("invoke do Res "+ resp);
System.out.println(resp);
return "helloUser。。。"+resp;
}
我此时第一步 就是
1.discoveryClient 先通过nacos 拉取服务列表~
2.再选择第一个 发起RestTemplate调用~
Ribbon 其实也是这么做的 ~
先通过nacos 拉取一个服务列表 然后再通过Lb负载均衡器 选一个 之后 再发起RestTemplate调用~
Ribbon此时这里是一个客户端负载均衡技术 意思是
具体选择调用哪一个下游服务是由我调用方说了算,
我现在user--->order服务 ,具体调用哪一个是由于我user说了算的
就像nginx 是服务端负载均衡的 你比如说 user--->order 具体调用那个服务 不是我user说了算的 是nginx 说了算的 user 只需要吧请求发给nginx
当然我们不能用getUser2 这样的方式去实现,
先找到 服务列表 ,然后再选择一个 发起RestTemplate调用
我们可以用ribbon
@LoadBalanced 在nacos中集成了,当然ribbon功能比较复杂一些 有各种不一样的负载均衡 算法 比如说 轮询 随机
我现在是这样子的 现在已经看到了 我们的请求已经发到2个Order服务上了 默认是轮询算法
我们可以在调用方@Bean注入一个RandomRule(随机)代表此时发送请求就是随机的了 Irule 是Rabbon中负载均衡算法的顶层接口实现
因为我们@Bean是全局定义的, 我们此时ribbon 做到更细粒度的划分 就是 user---->order(随机)
user---->product(轮询)
此时我们就不能用@Bean了
我们此时需要在配置文件中声明
我们可以这样在 yaml中声明 这样可以做到负载均衡更细粒~
我们可以自定义Irule实现自己的负载均衡策略
Step1 自定义实现权重的算法
点击详情进行更改权重
首先我们吧mall-order的权重改了~,之后我们需要自定义实现Irule 去根据权重选择一个服务 然后通过RestTemplate发起调用
1. 自定义MyIrule 继承 AbstractLoadBalancerRule
@Slf4j
public class MyIrule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties discoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object key) {
try {
log.info("key:{}", key);
//1. 获取负载均衡器
BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
log.info("baseLoadBalancer‐‐‐>:{}", baseLoadBalancer);
//2.获取微服务的名称
String serviceName = baseLoadBalancer.getName();
log.info("serviceName‐‐‐>:{}", serviceName);
//获取Nocas服务发现的相关组件API nameservice 是nacos 与服务有关的组件
NamingService namingService =
discoveryProperties.namingServiceInstance();
//获取 一个基于nacos client 实现权重的负载均衡算法 这里借助于nacos 实现的负载均衡
Instance instance =
namingService.selectOneHealthyInstance(serviceName);
//返回一个server
return new NacosServer(instance);
} catch (NacosException e) {
log.error("自定义负载均衡算法错误");
}
return null;
}
}
然后@Bean 注入到容器中
看当调用了10笔之后 我们就看到 8081的日志比较多唉~ 说明请求大多数都打到8081了~ 而少部分落在了8080上了
再讲下 ~ 我们上次也说 同一个在nacos 中 同一个namespace,同一个group 同一个服务可能会有不同的集群,
我们在发起调用的时候 要同集群优先调用,这个该怎么做呢
改这里的cluster-name,我们吧user设置为Bj-cluster,Order 一个设置为bj-cluster,一个设置为nj-cluster
我们在管理页面点击详情就可以看到
我们可以看到Order 在2个集群上 ,user 在北京集群上,理论上可以相互调用
但是我们要尽量在同集群下相互调用
@Slf4j
public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
/**
* 读取配置文件,并初始化 NacosWeightedRule
*
* @param iClientConfig
*/
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
/**
* 同一集群优先调用
*
* @param o
* @return
*/
@Override
public Server choose(Object o) {
try {
// 拿到配置文件中的集群名称 kunming
String clusterName = nacosDiscoveryProperties.getClusterName();
log.info("当前我的集群是在...."+clusterName);
BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
// 想要请求的微服务的名称
String name = loadBalancer.getName();
// 拿到服务发现的相关API
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
// 1. 找到指定服务的所有实例 A
List<Instance> instances = namingService.selectInstances(name, true);
// 2. 过滤出相同集群下的所有实例 B
List<Instance> sameClusterInstances = instances.stream()
.filter(instance -> Objects.equals(instance.getClusterName(), clusterName))
.collect(Collectors.toList());
// 3. 如果B是空,就用A
List<Instance> instancesToBeChosen = new ArrayList<>();
if (CollectionUtils.isEmpty(sameClusterInstances)) {
instancesToBeChosen = instances;
log.warn("发生跨集群的调用, name = {}, clusterName = {}, instances = {}",
name,
clusterName,
instances
);
} else {
instancesToBeChosen = sameClusterInstances;
}
// 4. 基于权重的负载均衡算法,返回1个实例
Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToBeChosen);
log.info("选择的实例是 port = {}, instance = {}", instance.getPort(), instance);
return new NacosServer(instance);
} catch (NacosException e) {
log.error("Nacos同一集群优先调用异常", e);
return null;
}
}
}
// 然后我们再@Bean 注入就可以了 接下来我们发10笔交易看看 情况~
看日志 此时我就完成了 同集群优先调用, 现在我吧BJ-Cluster的order 下线了
他就会发生跨集群调用
这样就完成了同集群优先调用 接下来我们要说一下 Ribbon可以实现金丝雀发布
还是这个例子 我们现在User服务通过注册中心拉取Order 进行消费,现在比如说user和order都改了代码并且进行发版,我们要让老版本的user 调用老版本的order,新版本的User--->新版本的order ,新版本稳定了 我们再部署其他机子,讲老版本做替换,如果新版本有问题 我们可以回滚 到之前的版本~ 大致是这个意思
在这里插入代码片
我们吧user的 metadata 设置为V1,order一个设置为V1 一个设置为V2
我们发送下请求看看 日志
同版本优先调用
@Slf4j
public class GrayReleasedRule extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public Server choose(Object key) {
try {
String version = this.nacosDiscoveryProperties.getMetadata().get("version");
log.warn("my version {}", version);
BaseLoadBalancer loadBalancer = (BaseLoadBalancer) getLoadBalancer();
String name = loadBalancer.getName();
log.warn("name {}", name);
NamingService namingService = nacosDiscoveryProperties
.namingServiceInstance();
List<Instance> instances = namingService.selectInstances(name, true);
if (CollectionUtils.isEmpty(instances)) {
log.warn("no instance in service {}", name);
return null;
}
List<Instance> instancesToChoose = instances;
if (StringUtils.isNotBlank(version)) {
List<Instance> sameClusterInstances = instances.stream()
.filter(instance -> Objects.equals(version,
instance.getMetadata().get("version")))
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(sameClusterInstances)) {
instancesToChoose = sameClusterInstances;
}
else {
log.warn(
"A version-service scall occurs,name = {}, version = {}, instance = {}",
name, version, instances);
}
}
Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToChoose);
log.info("选择的实例是 port = {}, instance = {}", instance.getPort(), instance);
return new NacosServer(instance);
}
catch (Exception e) {
log.warn("GrayReleasedRule error", e);
return null;
}
}
}
2022-04-13 01:00:06.988 WARN 18340 --- [nio-9091-exec-9] com.lvhao.MyIrule.GrayReleasedRule : my version v1
2022-04-13 01:00:06.988 WARN 18340 --- [nio-9091-exec-9] com.lvhao.MyIrule.GrayReleasedRule : name mall-order
2022-04-13 01:00:06.988 INFO 18340 --- [nio-9091-exec-9] com.lvhao.MyIrule.GrayReleasedRule : 选择的实例是 port = 8081, instance = Instance{instanceId='192.168.1.4#8081#DEFAULT#DEFAULT_GROUP@@mall-order', ip='192.168.1.4', port=8081, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName='DEFAULT', serviceName='DEFAULT_GROUP@@mall-order', metadata={preserved.register.source=SPRING_CLOUD, version=v1}}
2022-04-13 01:00:06.991 INFO 18340 --- [nio-9091-exec-9] com.lvhao.controller.UserController : invoke do Res hellOrder
hellOrder
当我把order 的v1 版本下线之后
2022-04-13 01:02:05.732 WARN 18340 --- [nio-9091-exec-6] com.lvhao.MyIrule.GrayReleasedRule : name mall-order
2022-04-13 01:02:05.732 WARN 18340 --- [nio-9091-exec-6] com.lvhao.MyIrule.GrayReleasedRule : A version-service scall occurs,name = mall-order, version = v1, instance = [Instance{instanceId='192.168.1.4#8080#DEFAULT#DEFAULT_GROUP@@mall-order', ip='192.168.1.4', port=8080, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName='DEFAULT', serviceName='DEFAULT_GROUP@@mall-order', metadata={preserved.register.source=SPRING_CLOUD, version=v2}}]
2022-04-13 01:02:05.732 INFO 18340 --- [nio-9091-exec-6] com.lvhao.MyIrule.GrayReleasedRule : 选择的实例是 port = 8080, instance = Instance{instanceId='192.168.1.4#8080#DEFAULT#DEFAULT_GROUP@@mall-order', ip='192.168.1.4', port=8080, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName='DEFAULT', serviceName='DEFAULT_GROUP@@mall-order', metadata={preserved.register.source=SPRING_CLOUD, version=v2}}
2022-04-13 01:02:05.739 INFO 18340 --- [nio-9091-exec-6] com.lvhao.controller.UserController : invoke do Res hellOrder
hellOrder
2022-04-13 01:02:05.967 WARN 18340 --- [nio-9091-exec-7] com.lvhao.MyIrule.GrayReleasedRule : my version v1
2022-04-13 01:02:05.967 WARN 18340 --- [nio-9091-exec-7] com.lvhao.MyIrule.GrayReleasedRule : name mall-order
2022-04-13 01:02:05.968 WARN 18340 --- [nio-9091-exec-7] com.lvhao.MyIrule.GrayReleasedRule : A version-service scall occurs,name = mall-order, version = v1, instance = [Instance{instanceId='192.168.1.4#8080#DEFAULT#DEFAULT_GROUP@@mall-order', ip='192.168.1.4', port=8080, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName='DEFAULT', serviceName='DEFAULT_GROUP@@mall-order', metadata={preserved.register.source=SPRING_CLOUD, version=v2}}]
2022-04-13 01:02:05.968 INFO 18340 --- [nio-9091-exec-7] com.lvhao.MyIrule.GrayReleasedRule : 选择的实例是 port = 8080, instance = Instance{instanceId='192.168.1.4#8080#DEFAULT#DEFAULT_GROUP@@mall-order', ip='192.168.1.4', port=8080, weight=1.0, healthy=true, enabled=true, ephemeral=true, clusterName='DEFAULT', serviceName='DEFAULT_GROUP@@mall-order', metadata={preserved.register.source=SPRING_CLOUD, version=v2}}
2022-04-13 01:02:05.971 INFO 18340 --- [nio-9091-exec-7] com.lvhao.controller.UserController : invoke do Res hellOrder
hellOrder
就可以看到日志的不同
我们使用ribbon 的 @LoadBalanced 做到负载均衡 是因为
@LoadBalanced 给RestTemplate中set了一个拦截器 他这个拦截器起来的作用达到了负载均衡~ 的效果