首页 > 编程语言 >Feign与SpringCloud LoadBalancer实现负载均衡源码分析

Feign与SpringCloud LoadBalancer实现负载均衡源码分析

时间:2025-01-16 17:56:59浏览次数:1  
标签:Feign return SpringCloud 负载 1111 instances 源码 Integer

SpringCloud LoadBalancer

众所周知,SpringCloud体系中负载均衡的组件有SpringCloud LoadBalancer和Ribbon,Ribbon也在逐渐的被替代掉,因为SpringCloud LoadBalancer性能更高,支持响应式
下面通过hard-coded体现一下SpringCloud的负载均衡
首先有一个【say-hello】服务,提供"/","/greeting"接口,同时为了后续观测方便,将此服务的端口号也返回

我们的调用方【user】服务,使用webflux,下面为主要依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2023.0.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

想要实现负载均衡,首先需要WebClient发起请求并且添加ReactorLoadBalancerExchangeFilterFunction过滤器,在使用的地方注入ReactorLoadBalancerExchangeFilterFunction即可,

另外还需要提供ServiceInstanceListSupplier,并且注入为bean,稍后再分析原理,,这里serverId为say-hello,我们将服务实例写成固定的单个节点,8081,8082,8082

现在启动say-hello服务的8081,8082,8083服务来体现负载均衡

调整user服务的日志等级为debug

logging:
  level:
    root: debug

访问user服务的/hello接口

可以明显的发现当前的负载策略是依次轮询,并且每次起始的实例并非8081
实现原理也很简单,主要在WebClient调用的时候会首先执行ExchangeFilterFunction的filter方法,ExchangeFilterFunction的andThan可以实现无限套娃,相当于过滤器的链式调用,有点绕

filterFunctions为自己传入的filter,andThan其实相当于next,我们在/hello接口中传入的filter为ReactorLoadBalancerExchangeFilterFunction,其中负载均衡主要在这里面进行开展,也可以传入自定义的过滤器
LoadBalancerLifecycle将获取当前NamedContextFactory,并且它确保负载均衡器可以在整个生命周期正常工作,也可以做一些额外的工作,
比如可以记录日志

也可以使用MicrometerStatsLoadBalancerLifecycle记录一些指标信息
接着调用LoadBalancerLifecycle的start方法
下面会调用主要逻辑choose方法
在LoadBalancerClientConfiguration,如果我们没有定义的话会默认使用RoundRobinLoadBalancer,采用依次轮询的策略,

而像其他的ReactorLoadBalancer都需要ServiceInstanceListSupplier来提供实例的一些信息,像在这个demo中我们使用固定值

根据这些扩展接口,我们很容易的来集成其他的注册发现框架,比如nacos通过DiscoveryClientServiceInstanceListSupplier
获取完ServiceInstance,我们来看RoundRobinLoadBalancer的轮询算法实现

其中position为初始化时产生的[0,999]的随机数,每次调用都会进行+1并且与Integer.MAX_VALUE进行与运算,如果是正整数,因为Integer.MAX_VALUE是2^32 -1 。0111 1111 1111 1111 1111 1111 1111 1111,当执行Integer.MAX_VALUE + 1时
会导致最高位(符号位)发生变化,从而产生溢出,加1后的结果为 1000 0000 0000 0000 0000 0000 0000 0000,表示的是最小的负数,即Integer.MIN_VALUE,次数再和Integer.MAX_VALUE进行与运算,所以pos的取值范围为[0, Integer.MAX_VALUE]
当连续的整数对相同的数取余时,余数也是连续的,所以就实现了依次轮询的策略
SpringCloud LoadBalancer里面内置了RandomLoadBalancer随机算法,实现应该是最简单的,直接使用随机数ThreadLocalRandom.current()
当然,我们也可以自己实现负载均衡的策略,比如下面是按照权重来实现,
修改DemoServiceInstanceListSuppler中的DefaultServiceInstance,在metadata添加额外参数weight,instance1权重为1,instance2权重为2,instance3权重为3

在当前NamedContext添加ReactorLoadBalancer bean

@Bean
    public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(
            ServiceInstanceListSupplier serviceInstanceListSupplier) {
        return new ReactorServiceInstanceLoadBalancer() {
            @Override
            public Mono<Response<ServiceInstance>> choose(Request request) {
                return serviceInstanceListSupplier.get(request).next().map(instances -> {
                    return getInstanceResponse(instances);
                });
            }

            private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
                if (instances.size() == 1) {
                    return new DefaultResponse(instances.get(0));
                }
                int totalWeight = instances.stream()
                        .mapToInt(this::getWeight)
                        .sum();

                Random random = new Random();
                int r = random.nextInt(totalWeight);

                for (ServiceInstance instance : instances) {
                    int weight = getWeight(instance);
                    if (r < weight) {
                        return new DefaultResponse(instance);
                    }
                    r -= weight;
                }
                return new EmptyResponse();
            };

            private int getWeight(ServiceInstance instance) {
                // 从元数据中获取权重,默认权重为 1
                String weightStr = instance.getMetadata().getOrDefault("weight", "1");
                try {
                    return Integer.parseInt(weightStr);
                } catch (NumberFormatException e) {
                    return 1; // 如果解析失败,默认权重为 1
                }
            }
        };
    }


重启项目,可以看到节点大概是按照我们预设的权重来的,instance1 8081端口的几率还是很小的

除了上面手动往WebClient里面添加Filter的方法之外,我们更多的是使用自动配置的方式,以减少程序的复杂性,
新添加bean,并且添加注解@LoadBalancerClient,name为say-hello,

新建接口/hi


其中@LoadBalancerClient会通过LoadBalancerClientConfigurationRegistrar注入为NamedContextFactory.Specification,如果添加了@LoadBalanced注解将在BeanPostProcessor的时候将会添加ExchangeFilterFunction

参考文档
https://spring.io/guides/gs/spring-cloud-loadbalancer

标签:Feign,return,SpringCloud,负载,1111,instances,源码,Integer
From: https://www.cnblogs.com/LiuFqiang/p/18675438

相关文章

  • CyberRT-record-源码查看
    记录数据RosbagROSROS中通过record.cpp调用Recorder类来进行bag的保存rosbagrecord的代码位于ros_comm\tools\rosbag\src\recorder.cpp中。实现的类主要为Recorder。Recorder的接口,一共有4个。总的来说,录制bag包的流程分为2个部分, 一个流程......
  • 计算机毕业设计—92713 Springboot邻家优选超市线上线下购物系统小程序(源码免费领)
    摘 要21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所认识,科学化的管理,使信息存储达到准确、快速、完善,并能提高工作管理效率,促进其发展。论文主要是对邻家优选超......
  • 计算机毕业设计—92968 高校运动会信息管理系统设计与实现(源码免费领)
    摘 要本论文介绍了一个高校运动会信息管理系统的设计和实现过程。首先是高校运动会的需求分析和可行性分析,通过比较运动会的各个工作流程,确定了系统的数据流程和数据库结构,然后介绍了高校运动会信息管理系统开发所使用的软件开发工具,最后描述了系统的详细设计与实现。本......
  • 计算机毕业设计—92767 php 酒店预约管理系统 (源码免费领)
    摘 要随着科学技术的飞速发展,社会的方方面面、各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身的优势,酒店预约管理系统当然也不能排除在外。酒店预约管理系统是以实际运用为开发背景,运用软件工程开发方法,采用Thinkphp技术构建的一个管理系统。整个开发过程......
  • 计算机毕业设计—312059 SSM新时代网咖管理系统(源码免费领)
      摘  要随着互联网时代的到来,同时计算机网络技术高速发展,网络管理运用也变得越来越广泛。因此,建立一个B/S结构的新时代网咖管理系统;新时代网咖管理系统的管理工作系统化、规范化,也会提高平台形象,提高管理效率。本新时代网咖管理系统是针对目前网咖管理系统的实际需......
  • 计算机毕业设计—311017 spring boot酒店预定系统(源码免费领)
    摘 要信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题。针对酒店客房预定等问题,对酒店信息管理进行研究分析,然后开发设计出酒店预订系统以解决问题。......
  • 数字可调控开关电源设计(论文+源码)
    1设计要求在本次数字可调控开关电源设计过程中,对关键参数设定如下:(1)输入电压:DC24-26V,输出电压:12-24(可调);(2)输出电压误差:<0.1V;(3)开关频率:50KHZ;纹波范围:<200mV;最大转化效率:89%;(4)最大输出电流:4.2A;输入过压保护:36V。根据上述要求,在此将整个系统架构设计如图2.1所示,采用STM32F......
  • 基于单片机的书写坐姿规范提醒器的设计(论文+源码)
    1功能设计本课题为基于单片机的书写坐姿规范提醒器的设计,其主要针对学生在进行书写时,经常会出现坐姿不对等现象,这样长期下去会影响学生的身体健康,因此本系统在功能上设计如下:采用超声波传感器检测坐姿,如果距离太近,则通过语音播报“距离过近请注意坐姿”的提示信息;通过光敏电......
  • 【附源码】JAVA大学生竞赛管理系统源码+SpringBoot+VUE+前后端分离
    学弟,学妹好,我是爱学习的学姐,今天带来一款优秀的项目:大学生竞赛管理系统 。本文介绍了系统功能与部署安装步骤,如果您有任何问题,也请联系学姐,偶现在是经验丰富的程序员!一.系统演示系统测试截图   系统视频演示 https://githubs.xyz/show/343.mp4 二.系统概述 ......
  • 【附源码】JAVA课程管理系统源码+SpringBoot+VUE+前后端分离
    学弟,学妹好,我是爱学习的学姐,今天带来一款优秀的项目:课程管理系统 。本文介绍了系统功能与部署安装步骤,如果您有任何问题,也请联系学姐,偶现在是经验丰富的程序员!一.系统演示系统测试截图     系统视频演示 https://githubs.xyz/show/342.mp4 二.系统概述......