聊聊如何独立使用ribbon实现业务客户端负载均衡

news2024/11/25 19:15:36

前言

ribbon是Netflix开源的客户端负载均衡工具,ribbon实现一系列的负载均衡算法,通过这些负载均衡算法去查找相应的服务。ribbon被大家所熟知,可能是来源于spring cloud,今天就来聊聊如何单独使用ribbon来实现业务客户端负载均衡

实现关键

springcloud ribbon获取服务列表是通过注册中心,而单独使用ribbon,因为没有注册中心加持,就得单独配置服务列表

示例

1、在业务项目中pom引入ribbon GAV

<dependency>
    <groupId>com.netflix.ribbon</groupId>
    <artifactId>ribbon</artifactId>
    <version>2.2.2</version>
</dependency>

不过引进去,发现如果引入netfiix的相关的类,比如IPing,会发现引不进去,原因是因为这个GAV里面依赖的jar的生命周期是runtime,即在运行期或者测试阶段才生效,在编译阶段是不生效的。如果我们为了方便,可以直接单独引入
spring cloud ribbon

    <dependency>
           <groupId>org.springframework.cloud</groupId>-->
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>-->
           <version>2.2.2.RELEASE</version>
    </dependency>

本文我们是想脱离springcloud,直接使用ribbon,因此我们可以直接 引入如下GAV

   <!--  核心的通用性代码-->
        <dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-core</artifactId>
            <version>${ribbon.version}</version>
        </dependency>
        <!-- 基于apache httpClient封装的rest客户端,集成了负载均衡模块,内嵌http心跳检测-->
        <dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-httpclient</artifactId>
            <version>${ribbon.version}</version>
        </dependency>
        <!-- 负载均衡模块-->
        <dependency>
            <groupId>com.netflix.ribbon</groupId>
            <artifactId>ribbon-loadbalancer</artifactId>
            <version>${ribbon.version}</version>
        </dependency>

        <!-- IClientConfig配置相关-->
        <dependency>
            <groupId>com.netflix.archaius</groupId>
            <artifactId>archaius-core</artifactId>
            <version>0.7.6</version>
        </dependency>

        <!-- IClientConfig配置相关-->
        <dependency>
            <groupId>commons-configuration</groupId>
            <artifactId>commons-configuration</artifactId>
            <version>1.8</version>
        </dependency>

2、创建ribbon元数据配置类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class RuleDefinition {

    /**
     * 服务名称
     */
    private String serviceName;

    /**
     * 命名空间,当服务名相同时,可以通过namesapce来进行隔离区分
     * 未指定默认为public
     */
    @Builder.Default
    private String namespace = DEFAULT_NAMESPACE;

    /**
     * 自定义负载均衡策略,未指定默认为轮询
     */
    @Builder.Default
    private String loadBalancerRuleClassName = RoundRobin;

    /**
     * 自定义心跳检测,未指定不检测
     */
    @Builder.Default
    private String loadBalancerPingClassName = DummyPing;

    /**
     * 服务列表,多个用英文逗号隔开
     */
    private String listOfServers;


    /**
     * 该优先级大于loadBalancerPingClassName
     */
    private IPing ping;

    /**
     * 心跳间隔,不配置默认是10秒,单位秒
     */
    private int pingIntervalSeconds;


    /**
     * 该优先级大于loadBalancerRuleClassName
     */
    private IRule rule;



}

@Data
@AllArgsConstructor
@NoArgsConstructor
@ConfigurationProperties(prefix = PREFIX)
public class LoadBalanceProperty {

    public static final String PREFIX = "lybgeek.loadbalance";

    private List<RuleDefinition> rules;

    public Map<String,RuleDefinition> getRuleMap(){
        if(CollectionUtils.isEmpty(rules)){
            return Collections.emptyMap();
        }

        Map<String,RuleDefinition> ruleDefinitionMap = new LinkedHashMap<>();
        for (RuleDefinition rule : rules) {
            String key = rule.getServiceName() + RULE_JOIN + rule.getNamespace();
            ruleDefinitionMap.put(key,rule);
        }

        return Collections.unmodifiableMap(ruleDefinitionMap);
    }
}

3、创建负载均衡工厂【核心实现】

 private final LoadBalanceProperty loadBalanceProperty;

    // key:serviceName + nameSpace
    private static final Map<String, ILoadBalancer> loadBalancerMap = new ConcurrentHashMap<>();

    public ILoadBalancer getLoadBalancer(String serviceName,String namespace){
        String key = serviceName + RULE_JOIN + namespace;
        if(loadBalancerMap.containsKey(key)){
            return loadBalancerMap.get(key);
        }
        RuleDefinition ruleDefinition = getAvailableRuleDefinition(serviceName,namespace);
        IPing ping = ruleDefinition.getPing();
        if(ObjectUtils.isEmpty(ping)){
            // 无法通过ConfigurationManager.getConfigInstance().setProperty(serviceName + DOT + namespace + DOT + PING_CLASS_NAME, ruleDefinition.getLoadBalancerPingClassName());
            //LoadBalancerBuilder没提供通过ClientConfig配置ping方法,只能通过withPing修改
            ping = getPing(serviceName,namespace);
        }

        IRule rule = ruleDefinition.getRule();
        if(ObjectUtils.isEmpty(rule)){
            // 也可以通过ConfigurationManager.getConfigInstance().setProperty(serviceName + DOT + namespace + DOT + RULE_CLASS_NAME, ruleDefinition.getLoadBalancerRuleClassName());
            rule = getRule(serviceName,namespace);
        }


        // 配置服务列表
        ConfigurationManager.getConfigInstance().setProperty(serviceName + DOT + namespace + DOT + SERVER_LIST, ruleDefinition.getListOfServers());
        // 因为服务列表目前是配置写死,因此关闭列表更新,否则当触发定时更新时,会重新将服务列表状态恢复原样,这样会导致server的isLive状态不准确
        // 不设置默认采用com.netflix.loadbalancer.PollingServerListUpdater
        ConfigurationManager.getConfigInstance().setProperty(serviceName + DOT + namespace + DOT + SERVERLIST_UPDATER_CLASS_NAME, EmptyServerListUpdater.class.getName());
        IClientConfig config = new DefaultClientConfigImpl(namespace);
        config.loadProperties(serviceName);
        ZoneAwareLoadBalancer<Server> loadBalancer = getLoadBalancer(config, ping, rule);
        loadBalancerMap.put(key,loadBalancer);
        if(ruleDefinition.getPingIntervalSeconds() > 0){
            // 默认每隔10秒进行心跳检测
            loadBalancer.setPingInterval(ruleDefinition.getPingIntervalSeconds());
        }

        return loadBalancer;


    }



    public ZoneAwareLoadBalancer<Server> getLoadBalancer(IClientConfig config, IPing ping, IRule rule){
        ZoneAwareLoadBalancer<Server> serverZoneAwareLoadBalancer = LoadBalancerBuilder.newBuilder()
                .withClientConfig(config)
                .withPing(ping)
                .withRule(rule)
                .buildDynamicServerListLoadBalancerWithUpdater();
        return serverZoneAwareLoadBalancer;
    }






    /**
     * 获取 iping
     * @param serviceName
     * @param namespace
     * @return
     */
    @SneakyThrows
    public IPing getPing(String serviceName, String namespace){
        RuleDefinition ruleDefinition = getAvailableRuleDefinition(serviceName,namespace);
        Class<?> loadBalancerPingClass = ClassUtils.forName(ruleDefinition.getLoadBalancerPingClassName(), Thread.currentThread().getContextClassLoader());
        Assert.isTrue(IPing.class.isAssignableFrom(loadBalancerPingClass),String.format("loadBalancerPingClassName : [%s] is not Iping class type",ruleDefinition.getLoadBalancerPingClassName()));
        return (IPing) BeanUtils.instantiateClass(loadBalancerPingClass);


    }

    /**
     * 获取 loadbalanceRule
     * @param serviceName
     * @param namespace
     * @return
     */
    @SneakyThrows
    public IRule getRule(String serviceName, String namespace){
        RuleDefinition ruleDefinition = getAvailableRuleDefinition(serviceName,namespace);
        Class<?> loadBalancerRuleClass = ClassUtils.forName(ruleDefinition.getLoadBalancerRuleClassName(), Thread.currentThread().getContextClassLoader());
        Assert.isTrue(IRule.class.isAssignableFrom(loadBalancerRuleClass),String.format("loadBalancerRuleClassName : [%s] is not Irule class type",ruleDefinition.getLoadBalancerRuleClassName()));
        return (IRule) BeanUtils.instantiateClass(loadBalancerRuleClass);

    }

    private RuleDefinition getAvailableRuleDefinition(String serviceName,String namespace){
        Map<String, RuleDefinition> ruleMap = loadBalanceProperty.getRuleMap();
        Assert.notEmpty(ruleMap,"ruleDefinition is empty");
        String key = serviceName + RULE_JOIN + namespace;
        RuleDefinition ruleDefinition = ruleMap.get(key);
        Assert.notNull(ruleDefinition,String.format("NOT FOUND AvailableRuleDefinition with serviceName : [{}] in namespace:[{}]",serviceName,namespace));
        return ruleDefinition;
    }


核心实现类:com.netflix.loadbalancer.LoadBalancerBuilder 利用该类创建相应的负载均衡

4、测试

a、 新起2个服务提供者占用6666端口、和6667端口

b、 在消费端的application.yml配置如下内容

lybgeek:
  loadbalance:
    rules:
      - serviceName: provider
        namespace: test
        loadBalancerPingClassName: com.github.lybgeek.loadbalance.ping.TelnetPing
        pingIntervalSeconds: 3
     #   loadBalancerRuleClassName: com.github.lybgeek.loadbalance.rule.CustomRoundRobinRule
        listOfServers: 127.0.0.1:6666,127.0.0.1:6667

c、 测试类

  @Override
    public void run(ApplicationArguments args) throws Exception {

        ServerChooser serverChooser = ServerChooser.builder()
                .loadBalancer(loadbalanceFactory.getLoadBalancer("provider", "test"))
                .build();

        while(true){
            Server reachableServer = serverChooser.getServer("provider");
            if(reachableServer != null){
                System.out.println(reachableServer.getHostPort());
            }
            TimeUnit.SECONDS.sleep(1);

        }


    }

当服务提供者都正常提供服务时,观察控制台


可以观察以轮询的方式调用服务提供者,当断掉其中一台服务提供者时,再观察控制台


会发现只调用服务正常的那台

总结

独立使用ribbon其实不会很难,主要对LoadBalancerBuilder这个API熟悉就可以定制自己想要的负载均衡器。springcloud从2020版本就将ribbon废弃了,转而要扶持其亲儿子loadbalancer,就目前功能而言,loadbalancer还没ribbon丰富,通过本文纪念一下被springcloud遗弃的ribbon

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-ribbon-loadbalance

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/666069.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

我心中的编程语言之王:Python

我心中的编程语言之王&#xff1a;Python 在当今日益发展的信息技术领域&#xff0c;编程语言的地位愈发重要。它们是构建现代软件和应用的基石&#xff0c;也是实现科技进步的关键工具。在众多编程语言中&#xff0c;Python 以其简单、易用、高效等诸多优点&#xff0c;成为了…

Dubbo架构分层总结

进来闲来无事看了些有关dubbo源码的书籍和《极客时间》何辉老师的课程&#xff0c;由于知识点比较碎&#xff0c;遂以笔记的方式纪录&#xff0c;毕竟好记性不如烂笔头&#xff0c;也希望对感情趣的同学提供点帮助 假设你是个新手开发者&#xff0c;可能只是简单使用过dubbo框…

数字孪生世界建设核心能力:数据治理能力

随着世界经济由工业经济向数字经济转型&#xff0c;数据逐步成为关键的生产要素&#xff0c;企业开始将数据作为一种战略资产进行管理。数据从业务中产生&#xff0c;在IT系统中承载&#xff0c;要对数据进行有效治理&#xff0c;需要业务充分参与&#xff0c;IT系统确保遵从&a…

前端开发必须要知道的package.json文件

前言 package.json 文件是一个 Node.js 项目的配置文件&#xff0c;用于描述项目的元数据信息&#xff08;如名称、版本、作者、依赖等&#xff09;&#xff0c;以及运行和构建该项目所需的脚本命令。 在项目开发过程中&#xff0c; package.json 文件的维护和更新是非常重要…

Axure8 零基础操作入门

参考&#xff1a;黑马产品经理课程 视频资源&#xff1a;day1&day2&#xff0c;Axure部分 Axure8常用功能 选择/缩放 选择 包含选中&#xff1a;全部选中才有效&#xff08;避免误操作&#xff0c;建议使用这个&#xff09;相交选中&#xff1a;相交即全选中 缩放 元件等…

PHP开发工具22-PHP中安装和使用xdebug

文章目录 前言配置详解总结 前言 本文已收录于PHP全栈系列专栏&#xff1a;PHP快速入门与实战 作为一个程序员&#xff0c;千万不要说你没有用过debug工具&#xff0c;不然有点说不过去。xdebug是PHP语言一个强大的利器&#xff0c;用他可以做很多事情。 xdebug是PHP开发者常…

提升编程效率:你不能错过的18款VS Code扩展

微信搜索 【大迁世界】, 我会第一时间和你分享前端行业趋势&#xff0c;学习途径等等。 本文 GitHub https://github.com/qq449245884/xiaozhi 已收录&#xff0c;有一线大厂面试完整考点、资料以及我的系列文章。 快来免费体验ChatGpt plus版本的&#xff0c;我们出的钱 体验地…

LTV-5341-ASEMI代理台湾光宝高速光耦LTV-5341

编辑&#xff1a;ll LTV-5341-ASEMI代理台湾光宝高速光耦LTV-5341 型号&#xff1a;LTV-155E 品牌&#xff1a;光宝 封装&#xff1a;LSOP-5 引脚数量&#xff1a;5 类型&#xff1a;光耦 特性&#xff1a;台湾光宝、IGBT驱动器、储能专用光耦&#xff3c;高速光耦 封装…

pnpm项目运行启动以及如何迁移到内网

1.迁移前的准备 首先看对node版本和pnpm版本的要求是什么&#xff0c;我的是自己电脑&#xff08;windows系统&#xff09;和内网电脑&#xff08;windows系统&#xff09;上的环境一致的 我的项目要求是 1.node版本 16.20.0 2.pnpm版本 8.6.2 需要先将node 和 pnpm 安装好相应…

今年前改BUG,下午就被通知在改进优化了

内卷可以说是 2023 年最火的一个词了。2022 年刚开始&#xff0c;在很多程序员网站看到很多 Java 程序员的 2023 年度总结都是&#xff1a;Java 越来越卷了&#xff08;手动狗头&#xff09;&#xff0c;2023 年是被卷的一年。前有几百万毕业生虎视眈眈&#xff0c;后有在职人员…

slam十四讲 03 Eigen实践之三维空间刚体运动

目录 1 初始化 2 旋转空间中的向量 3 欧拉角 4 变换矩阵 5 四元素 完整程序 1 初始化 旋转的两种办法&#xff1a; &#xff08;1&#xff09;旋转矩阵&#xff1a;a Ra, a R^T a, 旋转矩阵的特性&#xff1a;是一个行列式为1的正交矩阵. 三维空间的旋转是3x3矩阵&am…

基于SpringBoot的校园请假管理系统

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 校园请假信息管理系统…

php质量检查工具 phpmd的使用

PHPMD简介 团队在使用php-cs-fixer 代码格式自动式化工具之后,在格式,代码错误等方面带来了很大便利,不过在命名,代码质量,代码复杂度,缺少一些检查,在网上搜索后,发现PHPMD 一个PHP代码静态分析工具. 安装 官方网站 github 你可以直接到下载页面封装好的 phar 包&#xff1…

云原生数据库受到青睐,亚马逊云科技数据库提供多项功能

小小的改变&#xff0c;标志一个新时代的全面开启&#xff0c;一个数据库的云原生时代。前不久&#xff0c;Gartner公布了一组数据&#xff0c;引起了不小的讨论度。在2022年全球数据库管理系统的市场份额排名中&#xff0c;作为纯云厂商的亚马逊云科技&#xff0c;超越了老牌传…

一种基于目标的可解释的自动驾驶预测和规划策略

摘要&#xff1a; 本文介绍了一种通过理性逆向规划进行目标识别和多模态轨迹预测的方法。通过将目标识别与MCTS 计划相结合&#xff0c;为自车生成优化计划。 最近炒得比较火的影子模式实际就是在通过数据收集的方式不断模拟自动驾驶系统按照人类驾驶习惯实现人之间的交互过程…

QML 快速上手3 - QuickControl2

目录 QuickControl2简介风格设置control 配置文件图像浏览器案例component 组件报错问题StackViewSwipeView QuickControl2 简介 quickcontrol 用于快速构建风格化的用户界面 它包括了以下几个预制的组件风格 Default QT 默认风格Universal windows 桌面风格Material 谷歌推…

【FPGA】译码器、计数器及数码管显示

写在前面 万万没想到最后去了FPGA岗位&#xff0c;但是FPGA只在研一学过&#xff0c;确实忘得差不多了&#xff0c;因此从头把东西过亿边 这是某本书上的第一章节&#xff0c;感觉写的还是挺不错的&#xff0c;大概看了一下让我回想起很多知识&#xff0c;个人感觉比较适合学习…

《effective java》中关于解决构造函数/方法签名包含大量参数的解决方法

针对构造方法 重叠构造器模式 重叠构造器模式是一种编程中的反模式&#xff0c;指的是一个类有多个构造函数&#xff0c;每个构造函数都有不同数量的参数&#xff0c;从而可以根据不同的情况创建对象。这种方式会导致代码可读性和可维护性降低&#xff0c;因为构造函数过多&…

echarts柱状图数据太多设置滚动条

当你的项目中因数据量太大&#xff0c;导致柱状图数据全部叠在一起不便于看的时候&#xff0c;你们是怎么处理的&#xff1f; 大部分同学可能第一想法就是裁剪一部分数据&#xff0c;仅展示页面最大限度能够展示的数据&#xff0c;这确实是一个好办法&#xff0c;简单快速。但…

【分布式】分布式存储架构

文章目录 一、集中存储结构二、分布式存储2.1 、分布式存储的兴起2.2 、分布式存储的重要性2.3 、分布式存储的种类和比较 三、分布式理论浅析3.1 、一致性和可用性3.2 、数据分布哈希分布&#xff08; Swift &#xff09;顺序分布&#xff08; Bigtable &#xff09;CRUSH 分布…