手写RPC框架02-路由模块设计与实现

news2024/9/28 19:39:19

源码地址:https://github.com/lhj502819/IRpc/tree/v3

系列文章:

  • 注册中心模块实现
  • 路由模块实现
  • 序列化模块实现
  • 过滤器模块实现

为什么需要路由模块?

在当今互联网日益发展的情况下,我们一个服务一般都会部署多个,一方面可以均摊压力,另一方面也可以增加容错性,提高我们系统的稳定性。
但这种情况无疑会提升系统的复杂性,这里我们只讨论在进行RPC远程调用的时候我们需要考虑的事情。如果只有一个服务提供者Provider的情况下,直接根据ip + port请求即可,如果有多个Provider的话,那么就需要一套合适的负载均衡算法去选择一个合适的Provider。
如果没有路由模块的话,我们也可以很简单的实现,比如在上一版本中我们通过jdk自带的Random函数进行的随机选择。
在这里插入图片描述

但这样做有以下几个弊端:

  • 假设目标机器的性能不一致,如何对机器进行权重分配?
  • 每次都要执行Random函数,在高并发情况下对CPU的消耗较高;
  • 如何基于路由策略做ABTest?

因此我们单独抽象出一个模块来做这些工作,也就是路由模块。

jdk Random随机函数的缺点

通过查看Random函数的源码我们就能知道,由于Random函数底层会调用System.nanTome(),此函数会发起一次系统调用,而系统调用就涉及到CPU的状态切换,对性能的消耗是极大的。因此我们如果需要用到随机算法的话,最好自己实现一套。
在这里插入图片描述

路由抽象

public interface IRouter {

    /**
     * 刷新路由数组
     * @param selector
     */
    void refreshRouterArr(Selector selector);

    /**
     * 获取对应provider的连接通道
     * @param selector
     * @return
     */
    ChannelFutureWrapper select(Selector selector);

    /**
     * 更新权重值
     */
    void updateWeight(URL url);

}

负载均衡算法

随机算法

对应源代码中的cn.onenine.irpc.framework.core.router.RandomRouterImpl
实现思想:提前将所有的连接打乱顺序,随机放到数组中,也能达到随机访问的效果,但访问的顺序是不变的。当Client连接完成后,则调用此方法打乱顺序。

public void refreshRouterArr(Selector selector) {
    List<ChannelFutureWrapper> channelFutureWrappers = CONNECT_MAP.get(selector.getProviderServiceName());
    ChannelFutureWrapper[] arr = new ChannelFutureWrapper[channelFutureWrappers.size()];
    //提权生成调用先后顺序的随机数组
    int[] result = createRandomIndex(arr.length);
    //按照随机数组中的数字顺序,将所有的provider channel放入新的Channel数组中
    for (int i = 0; i < result.length; i++) {
        arr[i] = channelFutureWrappers.get(result[i]);
    }
    SERVICE_ROUTER_MAP.put(selector.getProviderServiceName(), arr);
}

/**
 * 创建随机乱序数组
 */
public static Integer[] createRandomArr(Integer[] arr) {
    int total = arr.length;
    Random ra = new Random();
    for (int i = 0; i < total; i++) {
        int j = ra.nextInt(total);
        if (i == j) {
            continue;
        }
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    return arr;
}

权重算法

每个Provider在向注册中心注册的时候,都会设置自身的权重值为100,Client会在与Provider建立连接之后开启一个NodeData Watcher,当监听到Provider节点数据发生变化时,则会发起一个自定义的事件IRpcNodeChangeEvent,通知我们的路由策略进行权重刷新(updateWeight)。
在这里插入图片描述

如下为核心实现逻辑:

@Override
public void updateWeight(URL url) {
    List<ChannelFutureWrapper> channelFutureWrappers = CONNECT_MAP.get(url.getServiceName());
    //创建根据权重值创建对应的数组,权重大的其index在数组中占比大
	//比如channelFutureWrappers的第3个weight占比为50%,其他的4个总共占比50%
	//那么weightArr中则大概长这样:3,3,3,3,0,1,2,4
    Integer[] weightArr = createWeightArr(channelFutureWrappers);
    Integer[] randomArr = createRandomArr(weightArr);
    ChannelFutureWrapper[] finalChannelFutureWrappers = new ChannelFutureWrapper[randomArr.length];
    for (int i = 0; i < randomArr.length; i++) {
        finalChannelFutureWrappers[i] = channelFutureWrappers.get(randomArr[i]);
    }
    SERVICE_ROUTER_MAP.put(url.getServiceName(),finalChannelFutureWrappers);
}
public static Integer[] createWeightArr(List<ChannelFutureWrapper> channelFutureWrappers) {
    List<Integer> weightArr = new ArrayList<>();
    for (int k = 0; k < channelFutureWrappers.size(); k++) {
        Integer weight = channelFutureWrappers.get(k).getWeight();
        int c = weight / 100;
        for (int i = 0; i < c; i++) {
            weightArr.add(k);
        }
    }
    Integer[] arr = new Integer[weightArr.size()];
    return weightArr.toArray(arr);
}
/**
 * 创建随机乱序数组
 */
public static Integer[] createRandomArr(Integer[] arr) {
    int total = arr.length;
    Random ra = new Random();
    for (int i = 0; i < total; i++) {
        int j = ra.nextInt(total);
        if (i == j) {
            continue;
        }
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    return arr;
}

轮询算法

通过自增计数,对数组长度取余的方式进行轮询访问。

public class ChannelFuturePollingRef {

    private AtomicLong referenceTimes = new AtomicLong(0);

    /**
     * 对Providers实现轮询访问
     */
    public ChannelFutureWrapper getChannelFutureWrapper(String serviceName) {
        ChannelFutureWrapper[] wrappers = SERVICE_ROUTER_MAP.get(serviceName);
        //自增取余,顺序访问
        //0 % 10 = 0; 1 % 10 = 1; 2 % 10 = 2 ;....;11 % 10 = 1
        long i = referenceTimes.getAndIncrement();
        int index = (int) (i % wrappers.length);
        return wrappers[index];
    }

}

其他路由算法

  • 最小连接数

需要记录每个应用服务器正在处理的连接数,然后将新来的请求转发到最少的那台上。

  • 分布式哈希一致性算法

分布式哈希一致性算法在实际使用时可能会出现“哈希倾斜”问题,为了解决这类问题,通常在算法的内部会设计一些虚拟节点,从而平衡请求的均匀性。

  • ip的hash算法

通过将源地址通过hash计算,定位到具体的一台机器上,但是如果一旦某台机器崩溃的话,该IP的请求就会直接失败,容错性不强。

路由策略配置化

将具体的路由策略通过配置的方式,使用起来更加灵活。在Client初始化的时候,会根据不同的配置选择对应的路由策略实现。

private void initConfig() {
    //初始化路由策略
    String routeStrategy = clientConfig.getRouteStrategy();
    if (RANDOM_ROUTER_TYPE.equals(routeStrategy)) {
        IROUTER = new RandomRouterImpl();
    } else if (ROTATE_ROUTER_TYPE.equals(routeStrategy)) {
        IROUTER = new RotateRouterImpl();
    }
}

总结

本次我们完成了RPC框架中路由层的设计与实现,并实现了随机路由算法、根据权重进行访问和轮询算法。

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

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

相关文章

Python绘制表白代码,又是一个表白神器

前言 嗨呀&#xff0c;又是我&#xff0c;又给你们带来了表白的代码 之前发了那些 照片里面加文字的…还有烟花…还有跳动爱心…emm你们也可以去看看哦 今天带来的这个&#xff0c;也是很不错哦 只不过它出来的有些慢&#xff0c;我这里先给你们看看这个效果图吧 效果展示…

大数据基础平台搭建-(三)Hadoop集群HA+Zookeeper搭建

大数据基础平台搭建-&#xff08;三&#xff09;Hadoop集群HAZookeeper搭建 大数据平台系列文章&#xff1a; 1、大数据基础平台搭建-&#xff08;一&#xff09;基础环境准备 2、大数据基础平台搭建-&#xff08;二&#xff09;Hadoop集群搭建 3、大数据基础平台搭建-&#xf…

如何让小型云台机械手实现按颜色分拣物品?

1. 功能说明 在小型云台机械手附近设置一个工作台&#xff0c;并安装一个TCS3200颜色识别传感器。将红色、蓝色工件分别放置在传感器上&#xff0c;如果检测的物料的颜色为红色&#xff0c;机械臂将物体放在机械臂的左侧&#xff0c;如果检测的物料的颜色为蓝色&#xff0c;机械…

数据结构与算法-希尔排序、归并排序

目录​​​​​​​ 希尔排序 1.算法描述 2.算法的实现 归并排序 4.1算法描述 2.算法实现 希尔排序 1.算法描述 1959年shell发明&#xff0c;第一批突破O&#xff08;n2&#xff09;时间复杂度的排序算法&#xff0c;是简单插入排序的改进版。它与插入之处在于&#xff0…

Android 深入系统完全讲解(二)

操作系统 操作系统是一套软件&#xff0c;它的任务就是为上层开发的用户&#xff0c;提供一个更方便的开发环境&#xff0c;同时 让硬件连接到系统中&#xff0c;能够非常方便&#xff0c;从而提高开发速度&#xff0c;以及稳定可靠。 操作系统就是这么存在的。 我们理解它&am…

基于低代码平台构筑金融行业IT运维服务体系

我今天分享题目是《基于低代码平台&#xff0c;构筑金融行业的IT运维服务体系》。这是一个大家不太熟悉的领域&#xff0c;首先它的行业是金融&#xff0c;其次它做的事情是IT运维。 关于金科信息 我先介绍一下金科信息。金科信息在1993年成立&#xff0c;到明年我们就整整30…

前端开发技术栈(插件篇):datatables

一、Datatables介绍 官网地址&#xff1a;https://datatables.net/ DataTables中文网&#xff1a;http://datatables.club/ 1、Datatables是一款jquery表格插件。它是一个高度灵活的工具&#xff0c;可以将任何HTML表格添加高级的交互功能。 2、分页&#xff0c;即时搜索和排序…

【Bootstrap】可复用的组件

目录 一、字体图标 二、下拉菜单 1. 步骤 2. 对齐 3. 分割线 4. 禁用的菜单项 三、按钮组 1. 按钮组 2. 按钮工具栏 3. 尺寸 4. 嵌套 5. 垂直排列 四、输入框组 1. 输入框组 2. 尺寸 3. 作为额外元素的按钮 4. 作为额外元素的按钮式下拉菜单 一、字体图标 组件…

55、MySOL数据库

目录 一、MySQL安装和配置 二、数据库 三、表 四、数据库的C [create] R [read] U [update] D [delete] 语句 1、insert语句 2、update语句 3、delete语句 4、select语句 五、Mysql常用数据类型&#xff08;列类型&#xff09;&#xff1a;​编辑 六、函数 *合计 / 统计…

kettle - 清洗 mongodb 数据案例

文章目录前言kettle - 清洗 mongodb 数据案例一、需求二、kettle开发1、新建mongodb数据查询2、配置kettleTest集合与清洗后kettleTestClear集合字段映射3、根据_id进行排序4、使用java脚本将日期格式化5、进行字段选择6、将delete字段进行值映射7、mongo输出8、最后加一个写日…

详解哨兵之间是如何通信的

基于 pub/sub 机制的哨兵集群组成 哨兵实例之间可以相互发现&#xff0c;要归功于 Redis 提供的 pub/sub 机制&#xff0c;也就是发布 / 订阅机制。 哨兵只要和主库建立起了连接&#xff0c;就可以在主库上发布消息了&#xff0c;比如说发布它自己的连接信息&#xff08;IP 和…

PDF如何转换成PPT?教你们几个简单方法

我们在工作经常用PDF文件进行传输&#xff0c;因为PDF体积小&#xff0c;传输速度很快&#xff0c;还不会不同设备上出现不兼容的问题&#xff0c;可以很好的保持文件的排版&#xff0c;不过我们有时候需要进行文件的展示&#xff0c;所以需要将PDF文件转换为PPT文件&#xff0…

2023河南/北京/重庆/南京DAMA-CDGA/CDGP数据治理工程师认证报名

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…

论文阅读——Recognizing Emotion Cause in Conversations

文章目录摘要引言相关工作任务定义构造RECCON数据集情绪原因的类型实验任务1&#xff1a;Causal Span Extraction模型任务2&#xff1a;Causal Emotion Entailment模型面临的挑战摘要 识别文本中情绪背后的原因是NLP中一个未被探索的研究领域。这个领域的发展具有着改善情感模…

Docker基础1-3

Docker基础1-3 时间:2023-01-02 https://www.bilibili.com/video/BV1gr4y1U7CY/ xmind文档&#xff1a;https://www.aliyundrive.com/s/6iaQt9zLDVm 一、Docker简介 1、Docker解决了什么问题 Docker打破了过去「程序即应用」的观念。透过镜像(images)将作业系统核心除外&am…

LeetCode:15. 三数之和

15. 三数之和1&#xff09;题目2&#xff09;思路3&#xff09;代码4&#xff09;结果1&#xff09;题目 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] …

【Nginx】核心配置文件结构

文章目录Nginx核心配置文件结构全局块user指令work process指令其他指令events块events指令配置实例http块定义MIME-Type自定义服务日志其他配置指令server块和location块Nginx服务器基础配置实例Nginx核心配置文件结构 从前面的内容学习中&#xff0c;我们知道Nginx的核心配置…

给力!低代码开发平台广州流辰信息科技助您增辉创价值!

低代码平台开发公司流辰信息深耕行业多年&#xff0c;一直以市场为导向&#xff0c;凭借敏锐的市场洞察力砥砺前行、拼搏进取&#xff0c;提升研发创新能力&#xff0c;广州流辰信息科技与各新老客户朋友风雨同舟&#xff0c;携手共创宏伟新蓝图&#xff01; 一、熔铸前沿科技 …

kettle的安装以及注意(迭代中....)

1、下载 kettle的官网下载地址&#xff1a;Pentaho from Hitachi Vantara - Browse Files at SourceForge.net 如果需要下载其他版本&#xff1a; 直接点击对应的版本Name&#xff08;8.0以下的实在Data Integration文件夹里面&#xff09;进去&#xff0c;再选择client-too…

【C++】代码调试的学习笔记

1. IO输出调试&#xff1a;输出重定向 在《第八期-C基础与深度解析》课程中&#xff0c;老师使用了“输出重定向”的语句来查看cout和cerr的结果&#xff1a; ./HelloWorld >txt1 2>txt2 代码含义&#xff1a;将程序HelloWorld的标准输出stdout重定向至文件txt1&#xf…