一个微服务框架的细节
KingWorks微服务系列文章:
(一)一个微服务框架的故事
(二)一个微服务框架的情节
从KingWorks-0.0.0版本(想象版本)开始,我就知道我踏上了一条“不归的自主研发路线”,到目前的KingWorks-3.0.0,我始终坚守着这一份执着。曾经想过放弃,因为它不是“开源主流”;曾经想过放弃,因为它很费力;曾经想过放弃,因为为了让它生命力不断,我需要付出的精力比专研几个新框架还要累得累。我坚守了,是因为它的简单;我坚守了,是因为同事们的喜欢;我坚守了,是因为它让我的心血在许多一线系统中滚滚流动。
微服务初心
我们微服务是为了把“大事化小小事化无”,但问题往往是从“小事作大”,如果我们以“微服务”理念从事小捉起,虽不可避免未来不确定性,但至少可以抵御历史风险。从过去经验可以知道,一个系统的成长基本要经历“单体-集群-分布式”的发展模式,前提是你开发的系统生命力足够强大。我们都知道,“微服务”是一种分布式架构,一个小小的应用系统直接“分布式”感觉未免有点“关公刀削苹果”,但如何避免一百个“小生命”当中有那么其中一两个可以粗壮成长所带来的痛楚呢?也就是说,如何用一个“微服务”理念的架构更简单、便捷与高效地去孵化一个个从0到N的关键机会。如果一个系统的每一个发展阶段都需要大动干戈,除了资金成本外,最重要的还是时间成本,综合来说,这都是试错成本。这已经不是一个“如何把一个大问题解决”的问题,而是一个“如何解决由一个小问题发展到大问题等一些列问题”的问题。我相信没有天生的大问题,每一个创(chuang)新(ye)都是一个从小到大的试错过程,如何把试错成本降到最低,是我最为关心的问题,因为机会是留个有准备的人。
微服务容器
最简单、便捷以及高效的理想状态就是系统发展过程的一个“趋0变动”。也就是系统从“单体”到“集群”,再从“集群”到“微服务”的发展过程中无须改动(过多)代码即可简单达到各种支撑模式的切换。为了达到这种效果,我想到了“容器”的概念。例如Docker容器或JVM容器,它们提供的只是“单元”运行的环境,轻松聚合和分离单例的运行。从“微服务”理念看,“服务”作为我们业务的“执行单元”,同样可以存在一个微服务应用容器,给个“服务单元”提供轻量级的运行环境,从而达到轻松聚合和分离服务的可能性。
KingWorks-MSAC(微服务应用容器,以下简称“微容器”)是我们KingWorks微服务框架的核心组件,它主要有六大功能特点,分别是映射、驱动、代理、管控、管理以及适配。
映射:按照规则可以根据URI访问路径信息分发到指定的服务处理当中;
驱动:激活每个框架约定下的服务线程执行单元;
代理:代理执行应用六大生命周期模块组件,如初始化模块组件、销毁模块组件、业务前置模块组件、业务逻辑模块组件、业务后置模块组件以及定时器模块组件;
管控:对服务生命周期内的状态进行统一管控,如服务注册、服务注销、服务发现、服务心跳、服务访问限制、服务并发限制以及远程配置同步等状态行为;
管理:对服务信息的统一管理,如服务单元信息、统一配置信息、服务列表信息以及各种服务状态信息;
适配器:服务与“外界”联系的中间件统称“适配器”,如数据量连接组件、Redis连接组件、远程访问。
(微)服务结构
以上单元服务工程的构建目录结构主要是基于KingWorks微服务框架的高约束和高标准的体现,具体说明如下:
服务目录:一个服务的身份和标准定义(com.kingworks.xxxsop/smp),其中xxx为服务标识,如以上例子,test就是本演示服务的标识,smp/sop为服务类型,smp代表面向信息管理服务、sop代表面向业务支撑服务;
模块组件目录:是服务初始化、销毁以及业务前、后置模块组件的父目录;
销毁模块组件目录:是服务进程销毁时执行的模块单元;
初始化模块组件目录:是服务进程初始化时执行的模块单元;
业务后置模块组件目录:是服务线程在执行业务逻辑模块组件后执行的模块单元;
业务前置模块组件目录:是服务线程在执行业务逻辑模块组件前执行的模块单元;
业务处理模块组件目录:是服务业务处理的核心逻辑单元;
定时器模块组件目录:是服务整个生命周期内运行的定时器执行模块单元;
微容器会根据以上约定分别在容器内扫描服务单元以及服务单元内各模块执行的组件单元。此外,微容器可以通过两种方式识别出服务单元,一种就是以上的“明文工程”模式,也就是源码工程;而另一种是jar包形式,可以对服务源码工程打包成“kingworks-xxxsmp/sop-v.jar”格式的jar引入容器工程里,其中xxx就是服务标识,而v就是服务版本号,这也就是所谓的微服务插件化架构模式。
(微)服务样例:service
testsop为本(微)服务演示样例,其中kingwors-msac-3.0.0就是我们的微容器,Talk is cheap,show the code:
启动微容器(服务),通过以下URI即可访问:
(微)服务样例:serviceBefore
serviceBefore模块组件顾名思义也就是service业务处理模块执行前的一个前置执行组件单元,有点类似于Filter的角色,Demo如下所示:
启动微容器(服务),通过以下URI即可访问:
(微)服务样例:serviceAfter
serviceBefore模块组件顾名思义也就是service业务处理模块执行后的一个后置执行组件单元,如果结合业务前置模块来使用,这就是一种约定的AOP模式,Demo如下所示:
启动微容器(服务),通过以下URI即可访问:
(微)服务样例:contextInitialized
contextInitialized是服务单元初始化时容器会扫描执行的模块组件单元,Demo如下所示:
启动微容器(服务):
(微)服务样例:contextDestroyed
contextDestroyed是服务单元销毁时容器会扫描执行的模块组件单元,Demo如下所示:
启动微容器(服务):
(微)服务样例:timer
timer是服务单元在启动时会扫描执行的模块组件单元,在整个服务生命周期内执行,Demo如下所示:
启动微容器(服务):
(微)服务管控
其实“微容器”本身也是一个拥有标准服务单元目录结构的服务,如下所示:
微容器的所有管控行为都是一样通过服务标准六大模块组件单元执行管控的,例如“服务的注册”是在容器服务的初始化模块组件中执行,“服务的注销”是在容器服务的销毁模块组件中执行,“服务的心跳”、“服务的发现”以及“远程配置同步”都是在容器服务的定时器模块组件中执行,“服务的访问限制”和“服务的并发限制”都是在服务前置模块组件中执行的。此外,容器还拥有自身的service业务处理单元(“^KINGWORKS_000”业务标识归属于容器服务所有),这些业务处理单元暴露了容器的一些基本管控和信息输出,如远程配置实时更新通知、容器服务状态信息查询等,如下Demo所示:
(微)服务管理
微容器除了对基本的服务属性、状态信息等基本服务信息管理外,还充当了统一信息配置与统一RPC服务信息管理两大“功能”。
【服务属性信息】
namespace:命名空间,容器内置默认值为default
serverCode:服务标识
IP:协议地址,当IP为空时,容器动态获取本地IP
port:服务端口,容器内置默认值为80
scope:服务范围(private:自己可见|default:同命名空间可见|protect:同命名空间或子命名空间可见|public:所有服务可见),容器内置默认值为default
zone:服务领域(私域标识),当[zone=public]时,表示该服务为公域信息配置(非服务实例,不能注册,混合云关键点所在),容器内置默认值为default
weight:服务权重(0~9),容器内置默认值为2
【服务状态信息】
服务总体并发以及各业务处理并发信息
服务网关调用状态信息(如RPC连接池信息、服务熔断次数、RPC状态信息等)
数据库连接池实体状态信息
Redis连接池实例状态信息
Kafka连接池实体状态信息
......
【统一信息配置】
微容器规范约束的配置文件有:
kingworks.properties:微容器服务基本信息管理与控制
rdc.yml:服务注册发现中心静态配置信息
datasource.properties:关系数据库配置信息
redis.properties:Redis配置信息
kafka.properties:Kafka配置信息
以上五大配置文件的配置规范都是微容器有一定的要规范配置格式,如关系数据库配置信息文件,poolName就是数据库连接池实例名称,如果没有特别指定,微容器的数据适配器组件(后续会介绍)默认会先选择poolName=serverCode的数据库连接池实例,如果不存在则匹配寻找poolName=default的数据库连接池实例。
统一信息配置除了统一静态解析之外,还可以通过配置conf服务进行动态解析,无论是静态解析还是远程动态解析,对服务内部调用来说完全是透明的。服务内部实现还可以通过对文件变动的动态监听,以便实时做出处理。
【统一RPC服务信息管理】
rdc.yml(服务注册发现中心信息配置文件)就是RPC匹配的一个服务信息中心,当系统初期阶段为“单体模式”的时候,可以通过走“静态服务解析”模式,也就是把所有的服务信息填写(注册)在这个文件下即可达到本地静态服务注册发现中心的效果。就算后续服务模式扩展改成动态加载模式,只需要配置rdc服务即可,对服务业务调用逻辑来说完全是透明的,无须改动任何一行代码。
【统一日志信息输出管理】
微容器对日志输出一共有三种规范化约定模式,分别是标准输出、文件输出、流输出(Kafka)三种模式,可通过kingworks.properties文件进行配置:
微容器的日志输出管理是通过logback引擎自定义以上三种日志模式Appender,有着高约束和规范的日志属性和输出格式(JSON):
(微)服务适配
微容器把服务以外的对接中间件工具组件统称“适配器”,例如数据库连接适配器,Redis连接适配器组件,远程过程调用适配器组件等。
【数据库连接适配器】
数据库连接适配器组件是微容器高约束规则并基于标准化原生JDBC的API进行封装的,具体使用如下Demo所示:
以上Demo是通过testsop服务的KINGWORKS_001_001_002业务实现单元实现的,通过URI访问即可删除相关查询信息:
【远程过程调用适配器组件】
微服务跟传统系统最大的一个差别之一就是服务的信息交互已经多样化,例如数据库、内存数据库、服务与服务之间交付等,已经弱化了传统数据源的概念,这也是“适配器”统称的来源。
只要微容器存在服务信息(无论是本地静态解析还是远程动态解析),RPCClient即可根据serverCode、serviceCode以及请求参数即可进行服务远程过程调用,其中调用过程的一些列动作如负载、路由、降级、服务访问权限、以及夸私有云访问等过程都已经被RPCClient封装起来,我们可以通过kingworks.properties配置文件对服务远程过程调用进行个性化配置。
微服务切面服务:BASESMP/SOP
basesop是所有sop服务的基础切面服务,basesmp是所有smp服务的基础切面服务,何为基础切面服务,请看下图所示:
通过上述介绍可以知道,微容器本身也内置了一个框架(Framework)级别的服务,其实框架服务、基础切面服务以及业务服务的各个模块的执行过程和顺序就如上图所示。严格来说,基础SMP/SOP切面服务是所有SMP/SOP的切面服务,那么微容器框架服务是所有服务的最顶层切面服务。基础切面服务跟框架服务一样,不是一个独立的业务服务,更多是所有业务服务的上一层抽象。例如所有SMP服务都会有一个公共的前置操作(如会话验证),可以通过BASESMP的serviceBefore实现即可达到所有SMP服务的公共前置操作。其它组件一样道理,但有一点跟微容器框架服务有点相似,基础切面服务的service如果实现了某业务逻辑实现,那么此业务逻辑实现的标识不会在其它业务服务上生效,因为为容器先会在基础切面服务检测才到业务服务检测。以下测试是在testsop服务的基础上新增一个basesop服务的引用,basesop服务的所有组件实现跟testsop服务一样,具体演示过程如下:
启动服务后,我们通过console的打印信息观察testsop服务与basessp服务之间的关系:
微服务模式
KingWorks微容器抽象了服务单元的实现,让应用系统架构的模式切换对服务来说完全是透明的,当系统在“单体”模式的时候,更多是处于一种静态服务解析模式:
当应用系统规模到一定程度时,可以对原单体应用模式进行扩展,进入分布式模式:
由于KingWorks微服务“zone”和“scope”属性的定义,可以让不同私有域的服务通过公有域的入口进行远程相互调用。在微容器的支撑下,最为突出和困难反而是对服务业务领域边界的定义与抽象的设计。
微服务总结
以上的总结,仅仅只是微容器的一些基本玩法,受限于自己的文章篇幅控制,更多好玩的玩法和思路以后会慢慢分享。同样由于诸多因素的受限和考虑,暂时无法公开源码,但我更愿意分享和记录整个解决问题的思考过程。其实,KingWorks已经不仅仅只是一个框架,它承载更多地是我解决问题的一种思维方式的体现,它的内涵也不仅仅只是通过KingWorks微容器的数千行代码就能体现,基于KingWorks之上的,还有一整套基于微服务(应用)云上运用“云开发”模式的一个“应用云容器” ......