SCF:58集团自研的一套RPC远程服务通信框架(Service Communication Framework),支持跨平台,具有高并发,高性能,高可靠性,并提供异步、多协议、事件驱动的中间层服务框架,与阿里的dubbo属于相同一类。
WF:WF是Web Framework的缩写,它是58集团自研的一款基于JAVA语言的MVC开发框架,主要应用于基于JAVA的Web项目。
插件器与插件仓库切入点
什么是面向切面的插件器所谓的“面向切面的插件器”实际上就是使用面向切面的思想开发一套插件器,并提供一系列现成完整的插件仓库给使用者,并支持根据业务需要开发自定义插件并无侵入性地嵌入到插件器中。
专业一点,插件器是一个仅依赖于cglib和slf4j的轻量级aop框架,支持同步和异步模式。插件器附带scf和wf的对接api,可以直接在scf和wf中使用。
发展历程1. 背景及设计初衷
之前一直负责租房、二手房、商业地产、小区、经纪人等四端(pc、m、app、小程序)的web层与底层的基础微服务层等较多分散业务,日常工作避免不了业务需求频繁迭代。
如此多的业务系统分布在了不同的子部门及更细化的开发小组中,其中大家都有一些共性的技术型需求,如接口监控、报警、容灾(灾备)、服务降级、过载保护等等,虽然架构部给我们提供了强大的服务管理平台,但更细致更自定义化的处理还是必不可少,基本每个系统都要有类似的强需求。
一次我们做新项目时想要复用这些功能,发现所有这些都会对现有系统或多或少会有部分侵入性,还要稍加改造,大部分情况都存在“水土不服”的情况,为我们的项目帮助没有预想的那么大,因此我就想,有没有可能做到一次开发到处使用呢,从那以后就开始了“插件器”的初步设想与实验开发。
2. 原始方式为何不满足业务需求
原始过滤器实现的两种切入功能
首先就是要分析痛点,看看原有方式为何给我们造成了困惑,或为什么没有达到我们预想的那种效果呢,多方了解沟通,总结下来有以下几点:
a) 移植难&侵入性强:针对现有系统开发,大部分参数都写死在代码中;接入过程中,需要很多额外的理解与开发量,没办法做到无脑接入;
b) 无法满足所有框架:往往支持web框架的无法支持微服务框架,支持了微服务框架的又不支持内部方法的直接调用;
c) 影响性能:接入过多此类需求,很多都是串行累加,时间久了对核心的业务功能会造成性能上的大幅降低,最终演变成了系统重构;
原理与设计1. 设计理念与痛点解决
a) 首先,定义“插件器”和“插件仓库”两个概念,绝对不能将两个东西混为一谈或设计开发过程中揉到一起。
b) 其次,采用面向切面的方式包装业务模块,通俗一点就是给需要处理的代码层加上代理。主要包装的目标为wf工程、scf工程、普通类调用等。
c) 再次,开发插件时直接继承标准插件抽象类,使得大家在开发插件时无需关注其未来的运行框架与位置,保证独立可运行即可。
d) 插件的配置采用注解方式,实际调研标识,这种方式可大大降低开发者的接入与使用成本。
e) 支持到参数级别,除类和方法外,最小粒度可作用于方法中的某个参数,
f) 采用异步扫描预加载 本地缓存方式,保证插件器的接入对原有工程启动及代理方法调用时的影响降低到最低(实际测试后证明其影响小到可忽略不计)。
g) 插件器尽量少的依赖外部开源jar包(只依赖cglib和slf4j),避免出现包冲突导致系统接入时遇到各种问题。
2. 功能对比
框架类型优势劣势spring-aop功能强大
1.太重,依赖很多jar
2.配置比较繁琐aspectj
1.功能强大
2.依赖少
1.编译期植入
2.额外DSL学习成本插件器
1.依赖少,仅依赖cglib和slf4j
2.使用简单
3.整合scf和wf功能没有spring-aop和aspectj强大
与市面AOP组件优劣对比
3. 实现原理
插件器整体上可以分为如下几个模块(附模块图):
a) 业务类加载模块:该模块目的就是扫描所需业务的所有类文件,将标记为需支持插件的类及方法放到内存map中,也就是为了在代理层调用到该方法时可以实时从内存中找到该标记并切入适当的插件功能。主要为公共代理层做基本物料的准备。
b) 插件仓库加载模块:插件器的主要功能执行者,通过注解实现,便于维护及运行时加载。采用了“规约大于配置”的思想,只要是实现了指定接口的注解就将其注入到内存插件仓库中。同样也是为了公共代理层做基本物料的准备。
c) 对目标框架的切入模块:其实插件器及插件都缺少一个核心点,就是如何无缝切入到现有业务框架中,而不同的项目工程使用的业务框架也都不同,比如scf、wf、dubbo、spring等,需要针对不同的目标框架采用不同的切入方案。只有切入成功了才能让公共代理层发挥其真正的作用。而当需要接入一个新框架时,只需要开发针对这个框架的“切入器”,而其他模块完全都不需要改动,实现了模块间的解耦。
d) 公共代理层模块:该模块是插件器的核心模块,包括同步、异步的处理,插件与插件之前的上下文数据流转等。其本质也比较简单,就是通过反射的方式实现业务类的动态代理,将需要运行插件的执行前、执行后整体包装切入到业务方法的正确位置。
e) 异步线程池模块:因为考虑到不同插件的特点,有些插件(如日志收集)无需跟着业务方法实时运行的时候,可以考虑将插件的具体执行放到独立的线程池中,哪怕10秒之后运行完该功能也不影响,但业务方法不会因为耗时10秒的插件而导致超时。所以维护好该线程池也就有很大的必要性了。
f) 插件仓库模块:所有插件开发、提交、维护的一个虚拟仓库,每个插件的本质可以理解为”实现了指定接口的注解及注解实现类”,由于需要支持同步和异步两种插件执行方式,因此在定义开发某个新插件的时候,就要根据不同的定位来实现不同的接口,即当需要串行执行时,需实现一个IFPluginAnnotationSerial接口即可,而当插件器加载扫描到该插件的时候就知道该用什么方式来运行它了。
插件器模块划分