微服务的负载均衡Ribbon

本文详细介绍了如何使用Spring Cloud Ribbon进行负载均衡,包括通过Nacos获取服务列表、自定义负载均衡策略如轮询和随机,以及实现同集群优先和版本优先的灰度发布。在灰度发布中,通过服务元数据设置版本,确保相同版本的服务互相调用,实现了新老版本的平滑过渡。
摘要由CSDN通过智能技术生成

在这里插入图片描述

我现在启动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了一个拦截器  他这个拦截器起来的作用达到了负载均衡~ 的效果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值