简易RPC框架-SPI

2023-11-09

案例

我们所熟悉的jbdc是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,提供了一种基准,据此可以构建更高级的工具和接口。

如上图所示,任意的一个数据库厂商只要去实现jdbc的接口,就可以轻松的对接jbdc从而为应用开发人员所服务。

SPI

上面的jdbc的设计理念叫SPI,它的全名是Service Provider Interface。它的理念是对某类功能进行抽象,确保应用程序依赖抽象而不是具体的某种实现,通过配置服务实现者的方式来达到面向接口编程以及扩展的目的。比如我们项目中需要用到日志组件,有的项目喜欢logback,有的喜欢log4j,有的喜欢common-log等等,如果在项目中直接依赖这些日志接口,那么后续如果需要对日志组件重新选型,对现有项目的影响会非常大,所有后来就有了slfj,它抽象了日志接口但不包含任何的实现,具体实现全部依赖于不同的厂商。

传统的java spi一般做法是在resources/META-INF/services/目录下面创建一个以服务接口命名的文件,该文件内容就是实现该服务接口的具体实现类的完全限定名。当程序加载的时候,就能通过resources/META-INF/services/里的配置文件找到具体的实现类名,并加载实例化。 通过这个机制就能找到服务接口的实现类,而不需要再代码里写死。

java.util.ServiceLoader这个就是java spi中用来加载服务实现类的工具,本文不对它的具体用法做过多介绍。

主题:限流策略如何扩展

本文要讨论的问题是,rpc框架中的限流过滤器扩展问题(可参考之前的文章 :简易RPC框架-客户端限流配置),之前介绍的限流实现是采用了guava提供的RateLimit,当时客户端限流的实现是在框架中写好的不允许修改,不同项目如果需要不同的限流策略那么就需要针对原有方案进行扩展,如果扩展呢?

Spring-boot 中的SPI

我在spring-boot项目中按传统的spi方式配置后,发现ServiceLoader加载指定接口找不到具体的实现类,后来发现spring-boot有自己的spi实现。它是在resources/META-INF/spring.factories中配置相关的接口,而且这个类的配置方式与传统的spi也有所不同,它采用了key=value方式,这点有点类似dubbo的spi机制。文件目录如下:

下面给出我调整之后的方案:

客户端限流接口

定义一个限流的接口,因为限流会有些参数控制,所以就增加RpcInvocation来协助完成。

public interface AccessLimitService {

    void acquire(RpcInvocation invocation);
}

客户端限流接口实现

本文只是为了简单实现,所以直接将原有写在rpc框架中的限流方式抽取出来,并没有重新采用一种新的限流策略。

public class AccessLimitServiceImpl implements AccessLimitService {

    @Override
    public void acquire(RpcInvocation invocation) {
        AccessLimitManager.acquire(invocation);
    }


    static class AccessLimitManager{

        private final static Object lock=new Object();

        private final static Map<String,RateLimiter> rateLimiterMap= Maps.newHashMap();

        public static void acquire(RpcInvocation invocation){
            if(!rateLimiterMap.containsKey(invocation.getClassName())) {
                synchronized (lock) {
                    if(!rateLimiterMap.containsKey(invocation.getClassName())) {
                        final RateLimiter rateLimiter = RateLimiter.create(invocation.getMaxExecutesCount());
                        rateLimiterMap.put(invocation.getClassName(), rateLimiter);
                    }
                }
            }
            else {
                RateLimiter rateLimiter=rateLimiterMap.get(invocation.getClassName());
                rateLimiter.acquire();
            }
        }
    }
}

客户端限流过滤器调整

既然限流的实现抽取成了接口,所以此处的具体实现调整为从服务提供者中找对应的实现。

@Override
public Object invoke(RpcInvoker invoker, RpcInvocation invocation) {
    logger.info("before acquire,"+new Date());
    List<AccessLimitService> accessLimitServiceLoader = SpringFactoriesLoader.loadFactories(AccessLimitService.class, null);
    if(!CollectionUtils.isEmpty(accessLimitServiceLoader)){
        AccessLimitService accessLimitService=accessLimitServiceLoader.get(0);
        accessLimitService.acquire(invocation);
    }

    Object rpcResponse=invoker.invoke(invocation);
    logger.info("after acquire,"+new Date());
    return rpcResponse;
}
    

目前还不支持同一个项目中多种限流策略,目前版本只允许存在一种,如果配置了多种实现,也只会选择第一个。如果需要支持也是可以的,通过配置一个名称来指定即可,但感觉价值并不大。

SpringFactoriesLoader就是spring-boot实现的类似java.util.ServiceLoader的一种服务加载工具,它负责从resources/META-INF/spring.factories中读取相应的配置,并对其加载实例化。总共包含两个核心方法:

  • loadFactoryNames

    这个方法就是加载某个接口的所有指定实现类名,它可以服务于下面的loadFactories方法。

  • loadFactories 首先通过loadFactoryNames方法从配置文件中获取接口与实现类的关系,然后一个一个实例化服务实现类。

经过以上几步的调整,就基本实现了一个简单的基于SPI思想的组件扩展机制。客户端可以扩展任意的限流机制去替换。

本文源码

文中代码是依赖上述项目的,如果有不明白的可下载源码

转载于:https://www.cnblogs.com/ASPNET2008/p/9062341.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

简易RPC框架-SPI 的相关文章

随机推荐

  • git rebase小计

    http www cnblogs com kym archive 2010 08 12 1797937 html git rebase 顾名思义 就是重新定义 re 起点 base 的作用 即重新定义分支的版本库状态 要搞清楚这个东西 要先
  • 74160同步置数法解析(以接成同步八进制计数器为例)

    我们先来看一下电路逻辑图 从中提取核心信息 将QD QC QB QA接成0010是为了配合LOAD引脚使用 以将74160的状态置为0010 计数器的最大状态为1001 当74160到达1001时 通过7400N与非门将LOAD引脚置为0
  • Linux 下安装zabbix

    一 下载安装zabbix 1 下载zabbix root localhost wget https mirrors tuna tsinghua edu cn zabbix zabbix 4 0 rhel 7 x86 64 zabbix re
  • OFFICE2016用过一段时间后正版密钥显示未激活问题

    转自百度知道 用户 kay陈健 侵删 一般出现这种问题是你电脑中先安装了一个版本的office 但是没有激活 没有将其卸载干净 然后又安装了另外一个版本的office 后者激活了 我也遇到了这个问题 目前已按如下方法解决 1 运行下面的命令
  • Arcgis 重装 的 license 问题

    1 卸载不干净 C arcgis安装目录 C ProgramData ESRI C Program Files x86 Common Files 2 注册表 删除有关Arcgis和ESRI的注册表 我是一条分割线 最后 我学会了重装系统
  • if...else; 嵌套if...else

    if else结构 如果怎么样 否则怎么样 if 分支条件 当条件满足时执行 else 当条件不满足时执行 注意 分支条件返回的一定是一个布尔类型 当分支中有且只有一行代码的时候 是可以省略大括号 gt 不推荐 和 之间不能添加任何符号 i
  • Java中 类名+方法名(){}的意思

    public class GetVersion public VersionBean get version code Context context String packagename if getPackageInfo context
  • pandoc 使用方法

    我是使用的 typora mac 版本 在下载 pandoc 完成后使用 pandoc 导出word时出现闪退情况 导致不能成功导出 然后就搜索到 pandoc 的命令行方法 以此记录下来 1 进入https pandoc org 下载 p
  • 代码片段

    以下代码是从Stack Overflow上看到的 对于C virtual的特性挺有参考意义的 于是记录下来 class A public void f std cout lt lt A lt lt std endl class B publ
  • 安全知识试题

    安全知识试题 一 单选题 共计30分 1 1分 在 QQ 群里有一朋友称 他们的学校近期有国外某知名大学教授来上课 上完课程后就可以拿到该大学文凭 但要交 26800 元的学费 正确的做法是 A 判定为诈骗信息 2 1分 在校园里东西丢失该
  • Android OpenGLES 学习笔记

    GL10 纹理问题 贴纹理的时候最好是要 2 n 字节对齐 这里说的是最后绑定到 GL 的那个图片 如果这个图片是由别的图片组合的 则组合的小图片没有这个要求 还有纹理的大小不能超过 GL 最大纹理大小的限制 查询方法 这里是 GL 标准的
  • vue+element表格使用vue-json-viewer实现查看JSON数据效果

    效果图 功能 在element弹窗中根据表格行查看当前行的JSON数据 高亮 可折叠 可复制 这里需要先安装vue json viewer插件 官网地址 https www npmjs com package vue json viewer
  • 神经网络编程基础

    目录 1 二分类 Binary Classification 2 逻辑回归 Logistic Regression 3 逻辑回归的代价函数 Logistic Regression Cost Function 4 梯度下降法 Gradient
  • nodejs调整版本问题

    因为接触到的项目渐渐增多 前端项目所需的nodejs版本也出现了分歧 之前一直用的14 16版本需要调换成八点几的版本 因为不会调整 所以多走了很多弯路 记录下来 以备不时之需 根据网络上所说可以使用nvm进行调整 所以在卸载nodejs之
  • C#进行MapX二次开发之MapX基础知识

    C 进行MapX二次开发之MapX基础知识 MapX的主要技术特点 1 以表 Table 的形式组织信息 每一个表都是一组MapInfo文件 这些文件组成了地图文件和数据库文件 为使用MapInfo 就需要有组成表的用户数据和地图文件 这些
  • 从LXMERT到VLMO:多模态预训练模型的演变史

    作者 吉雅太 单位 清华大学 研究方向 多模态研究 自从 2018 年 BERT 在 NLP 领域声名鹊起 通过预训练在 n 多 NLP 任务中刷榜 成功发掘出了 transformer 的潜力 众多研究者就看到了多模态发展的新的机会 使用
  • C/C++ 打印三角形

    打印三角形是C语言的经典例题 首先我们先看看效果图 一 直角三角形 ok 现在从最简单的打印直角三角形开始 通过以上效果图你会发现规律 行数 的个数 1 1 2 2 3 3 4 4 5 5 n n 根据以上规律写出以下代码 include
  • 查缺补漏:集和与非平凡属性

    查缺补漏 集和与非平凡属性 前面的习题和知识点补充 Conjunctive normal form CNF 是布尔逻辑的一种方法 它将公式表示为带有AND或or的子句的连词 由连词or AND连接的每个子句必须是文字或包含析取or运算符 子
  • selenium-XPATH定位

    XPATH定位 相对路径 相对定位是两个杠表示 相对路径易维护 例 div 1 div 2 绝对路径 绝对定位用一个杠 绝对路径一个层级变化所有空间都有变化 例 html body div 1 section section main di
  • 简易RPC框架-SPI

    案例 我们所熟悉的jbdc是一种用于执行SQL语句的Java API 可以为多种关系数据库提供统一访问 提供了一种基准 据此可以构建更高级的工具和接口 如上图所示 任意的一个数据库厂商只要去实现jdbc的接口 就可以轻松的对接jbdc从而为