互联⽹架构:常⽤基础中间件介绍
⼀般⽽⾔中间件和框架的区别是,中间件是独⽴运⾏的⽤于处理某项专门业务的CS程序,会有配套的客户端和服务端,框架虽然也是处理某个专门业务的但是它不是独⽴程序,是寄宿在宿主程序进程内的⼀套类库。
图上绿⾊部分代表了框架,红⾊部分代表了管理系统,紫⾊部分代表了中间件。本⽂会着重介绍管理系统和中间件部分。分布式和微服务的关系
⼀、配置管理
针对系统内部技术层⾯的各种配置,各种池的⼤⼩、 队列的⼤⼩、⽇志级别、各种路径、批次⼤⼩、处理间隔、重试次数、超时时间等。
针对业务运营层⾯的各种配置,活动的周期奖励、⿊⽩名单、弹窗、⼴告位等。
针对运维和发布层⾯的配置,灰度名单、注册中⼼地址、数据库地址、缓存地址、MQ地址等。
因为⼀些基础组件⽐如SOA框架和发布系统也会⽤到配置,这个时候就会可能会有鸡⽣蛋的问题,这⾥我⽐较建议把配置系统作为最最底层的系统,其它服务都可以依赖配置系统。⼀般⽽⾔配置管理除了实现最基本的Key-Value的配置读取和配置之外,还会有下⾯的⼀些特性和功能:
⾼性能。配置服务的压⼒会是⾮常吓⼈的,在⼀次服务调⽤中可能就会有⼏⼗次的配置调⽤,如果服务的整体QPS在500那么配置服务的压⼒可能在1万的QPS,这样的QPS不⾛缓存基本是不可能的。好在即使是1万甚⾄是5万的QPS也不算⼀个很夸张的⽆法解决的压⼒。
⾼可⽤。现在各种开源的配置服务是所谓的分布式配置服务,由可扩展的配置服务集来承担负载均衡和⾼可⽤功能,配置服务⼀旦挂了可能会让系统瘫痪。你可能会说配置服务⼀般本地会有缓存,会有本地的配置⽂件作为后备,会有默认值,但是因为配置是运营运维在实时修改的,如果某个业务的配置没有使⽤最新的配置⾛的是错误的默认值的话,系统会处于完全混乱的状态,所以配置服务的稳定性太重要了。
树形的配置体系。如果只是把所有配置堆在⼀个列表⾥,加上项⽬和分类的话,当配置多达⼏千项的时候还是会有点多。可以⽀持树形的层级配置,不拘泥于项⽬和分类这两个条件。项⽬下可以有模块,模块下可以有分类,分类下可以有⼩类,根据⾃⼰的需求动态构建配置树。
好⽤的客户端。⽐如可以和SpringBoot以及@Value注解结合起来,⾮侵⼊整合配置系统,⽆需任何代码的改动。
毫秒级粒度的修改实时⽣效。可以使⽤长连接推的⽅式实现,也可以实现缓存失效的⽅式实现。
配置的分层隔离。包括按照环境、集和项⽬来提供多套配置相互独⽴不影响,包括可以以层级的⽅式做配置继承。
配置的权限控制。不同类型、环境、集、项⽬的配置具有不同的管理权限,⽐如脱敏只读、只读、读写、导出。
配置的版本管理。配置的每⼀次修改都是⼀个版本,可以为单独的配置或项⽬进⾏直接版本回滚。
丰富的Value形式。配置的Value如果要保存列表的话,保存⼀个JSON阅读和修改都不⽅便,可以直接提供List⽅式的Value,在后台可以单独增删改⾥⾯的⼀项。在⽐如⿊名单的引⽤上这种⽅式⽐较⾼效,否则更新⼀个名单每次都要修改整个⿊名单。这个功能可以和Redis结合在⼀起进⾏实现。Value
除了⽀持字符串可以是JSON和XML形式,系统可以对格式进⾏格式化,对格式进⾏校验。Value 也可以是⾮字符串类型的各种数字格式,系统也会根据类型进⾏校验。
丰富的配置发布⽣效形式。⽐如可以⾃然⽣效、⽴即⽣效以及定时⽣效。定时⽣效的功能适合于在某个时间点需要开启某个配置,⽐如⽤于⾯向⽤户的推送、活动业务。还有⽀持灰度⾃动发布,以⼀定的时间间隔来对集⾥的实例进⾏发布,避免⼈⼯去定期逐⼀发布单台的⿇烦。
审核审计功能。配置的修改可以由管理员进⾏审核(也就是修改和发布的权限⽀持分离),避免配置错误修改。所有配置的修改记录可以查询到谁什么时候因为什么原因修改了什么配置,事后可以审计审查。
配置⽣效跟踪和使⽤率跟踪。可以看到每⼀个配置项现在哪些客户端在使⽤,⽣效的值的版本是哪个。通过这个功能还可以排查现在系统中过去⼀段时间从没有⽤过的配置,删除⽆⽤的配置。
动态配置。在API设计的时候我们引⼊上下⽂的概念,通过传⼊⼀个Map字典作为上下⽂,⽐如某个配置按照不同的⽤户类型、城市需要有不同的值,这个逻辑我们可以不需要在代码⾥⾯⼿⼯编写,直接通过在后台配置上下⽂的匹配策略来动态读取到不同的配置值。
本地快照。对配置进⾏快照本地保存,在出现故障⽆法连接服务端的时候使⽤本地的配置。
这⾥可以看到要实现⼀个功能完善的配置系统⼯作量还是相当⼤的,⼀个优秀的功能强⼤的配置系统可以节省很多开发的⼯作量,因为可配置部分的功能基本就是由配置系统直接实现了,⽆需在数据库中在搞⼤量的XXConfig表(不夸张的说,很多业务系统40%的⼯作量在这个上⾯,不但需要做这些配置表还需要配以配置后台)。
⼆、服务管理
微服务的建设中实现远程调⽤只是实现了20%的⼯作量(但是确实满⾜了80%的需求)。服务管理治理这块有⼤量的⼯作要做。这也就是实现⾃⼰RPC框架的好处,这是第⼀步,有了这第⼀步让数据流过我们⾃⼰的框架以后我们接可以做更多的事情,⽐如:
调⽤链跟踪。能否记录整个调⽤的情况,并且查看这个调⽤链。下⾯⼀节会再说⼀下这点。
注册管理。查看服务的注册情况,服务⼿动上线下线,集切换,压⼒分配⼲预。
配置管理。配置服务端客户端线程池和队列的配置,超时配置等等。当然,这个也可以在配置系统中进⾏。
运维层⾯的管理。查看和管理⽅法熔断,进⾏并发限流配置,服务权限⿊⽩名单配置,安全⽅⾯的配置(信息加密,⽇志脱敏等)。
Service Store的概念。服务发布需要满⾜⼀定要求,有⽂档(⽐如可以通过注解⽅式在代码注释⾥提供),有信息(开发负责⼈、运维负责⼈,服务类型,提供的能⼒),满⾜要求后就可以以类似于苹果App Store发布程序的⽅式发布服务,这样我们就可以在统⼀的平台上查看服务的维护信息和⽂档。
版本控制调⽤统计。对服务进⾏灰度升级,按版本路由,不同版本调⽤分析等等。类似于⼀些应⽤统计平台提供的功能(友盟、TalkingData)。
这⾥我想说的理念是,服务能调⽤通是第⼀步,随着服务数量变多,部署⽅式的复杂化,依赖关系复杂化,版本的迭代,API的变更,开发⼈员和架构师其实急需有⼀套地图能够对服务能⼒的全貌进⾏整体的了解,运维也需要有系统能对服务进⾏观察和调配。服务治理的部分完全可以以iOS那套(开发符合时候需要符合标准+发布的时候需要有流程)⽅式来运作。
三、全链路监控
以Log、Agent、Proxy或整合进框架的⽅式实现,尽可能少的侵⼊的情况下实现数据的收集。⽽且确保数据的收集不会影响到主业务,收集服务端宕机的情况下业务不影响。
调⽤跟踪。涉及到服务调⽤、缓存调⽤、数据库调⽤,MQ调⽤,不仅仅可以以树的形式呈现每次调⽤
的类型、耗时、结果,还可以呈现完整的根,也就是对于⽹站请求呈现出请求的完整信息,对于Job任务呈现出Job的信息。
JVM的信息(⽐如对于Java)。呈现每⼀个进程JVM层次的GC、Threads、Memory、CPU的使⽤情况。可以进⾏远程Stack的查看和Heap的快照(没有进程的内存信息,很多时候基于服务器层⾯粗粒度的资源使⽤情况的监控,基本不可能分析出根本原因),并且可以设定策略进⾏定期的快照。虚拟机的信息查看和调⽤跟踪甚⾄可以通过快照进⾏关联,在出现问题的时候能够了解当时虚拟机的状态对于排查问题是⾮常有好处的。
依赖关系⼀览。有的时候我们做架构⽅案,第⼀步就是梳理模块和服务之间的依赖关系,只有这样我们才能确定影响范围重构范围,对于微服务做的⽐较复杂的项⽬来说,每个⼈可能只是关注⾃⼰服务的上下游,对于上游的上游和下游的下游完全不清楚,导致公司也没有⼈可以说的清楚架构的全貌。这个时候我们有全链路跟踪系统的话,可以对通过分析过去的调⽤来绘制出⼀张依赖关系的架构图。这个图如果对QPS做⼀些热点的话,还可以帮助我们做⼀些运维层⾯的容量规划。
⾼级分析建议。⽐如在全链路压测后定位分析瓶颈所在。定时分析所有组件的执⾏性能,得出性能衰退的趋势,提早进⾏问题预警。分析JVM的线程和GC情况,辅助定位High CPU和Memory Leak的问题。退⼀万步说,即使没有这样的⾃动化的⾼级分析,有了调⽤跟踪的图和组件依赖关系图,⾄少在出问题的时候我们⼈能分析出来咋回事。
Dashboard。⾮必须,只要数据收集⾜够全⾯,如之前⽂章所⽰,我们可以⽤Grafana来进⾏各种个性化的图表配置。
四、数据访问中间件
最常⽤的功能就是读写分离。也包括负载均衡和故障转移的功能,⾃动在多个从库做负载均衡,通过可⽤性探测,在主库出现故障的时候配合数据库的⾼可⽤和复制做主库的切换。
随着数据量的增多需要分⽚功能。分⽚也就是Sharding,把数据按照⼀定的维度均匀分散到不同的表,然后把表分布在多个物理数据库中,实现压⼒的分散。这⾥写⼊的Sharding⼀般⽽⾔没有太多的差异,但是读取⽅⾯因为涉及到归并汇总的过程,如果要实现复杂
功能的话还是⽐较⿇烦的。由于分⽚的维度往往可能有多个,这⽅⾯可以采⽤多写多个维度的底层表来实现也可以采⽤维度索引表⽅式来实现。
其它⼀些运维⽅⾯的功能。⽐如客户端权限控制,⿊⽩名单,限流,超时熔断,和调⽤链搭配起来的调⽤跟踪,全量操作的审计搜索,数据迁移辅助等等。
更⾼级的话可以实现SQL的优化功能。随时进⾏SQL的Profiler,然后达到⼀定阈值后提供索引优化建议。类似。
其它。极少的代理实现了分布式事务的功能(XA)。还可以实现代理层⾯的分布式悲观锁的功能。其实细想⼀下,SQL因为并不是直接扔到数据库执⾏,这⾥的可能性就太多了,想⼲啥都可以。
实现上⼀般需要做下⾯⼏件事情:
有⼀个⾼性能的⽹络模型,⼀般基于⾼性能的⽹络框架实现,毕竟Proxy的⽹络⽅⾯的性能不能成为瓶颈。
有⼀个MySQL协议的解析器,开源实现很多,拿过来直接⽤即可。
有⼀个SQL语法的解析器,Sharding以及读写分离免不了需要解析SQL,⼀般流程为SQL解析、查询优化、SQL路由、SQL重写,在把SQL提交到多台数据库执⾏后进⾏结果归并。
Proxy本⾝最好是⽆状态节点,以集⽅式实现⾼可⽤。
这些功能除了Proxy⽅式的实现还有和数据访问标准结合起来的实现,⽐如改写JDBC的框架⽅式实现,两种实现⽅式各有优缺点。框架⽅式的实现不局限于数据库类型,性能略⾼,Proxy⽅式的实现⽀持任意的语⾔更透明,功能也可以做的更强⼤⼀些。最近还出现了边车Sidecard⽅式实现的理念,类似于ServiceMesh的概念,⽹上有⼀些资料,但是这种⽅式到⽬前为⽌还没看到成熟的实现。
五、分布式缓存中间件
分布式。这是最基本的,通过各种算法把Key分散到各个节点,提供⼀定的容量规划和容量报警功能。
⾼可⽤。配合Redis的⼀些⾼可⽤⽅案实现⼀定程度的⾼可⽤。
运维⽅⾯的功能。⽐如客户端权限控制,⿊⽩名单,限流,超时熔断,全量操作的审计搜索,数据迁移辅助等等。
跟踪和问题分析。配合全链路监控实现⼀体化的缓存访问跟踪。以及更智能的分析使⽤的情况,结合缓存的命中率,Value的⼤⼩,压⼒平衡性提供⼀些优化建议和报警,尽早发现问题,缓存的崩盘往往是有前兆的。
完善的管理后台,可以⼀览集的⽤量、性能,以及做容量规划和迁移⽅案。
如果Redis集特别⼤的话的确是有⼀套的⾃⼰的Proxy体系会更⽅便,⼩型项⽬⼀般⽤不到。
六、任务(Job)管理
⼀些机器加⼊集作为我们的底层服务器资源。
Job编译后打包部署到统⼀的地⽅。Job可以是各个语⾔实现的,这没有关系。可以是裸程序,也可以使⽤Docker来实现。
在允许Job前我们需要对资源进⾏分配,估算⼀下Job⼤概需要怎么样的资源,然后根据执⾏频次统⼀计算得出⼀个合适的资源分配。
由中间件根据每⼀个Job的时间配置在合适的时候把进程(或Docker)拉起执⾏,执⾏前根据当前的情况计算分配⼀个合适的机器,完成后释放资源,下⼀次执⾏不⼀定在同⼀台机器执⾏。
这样的中间件是更底层的⼀套服务,⼀般⽽⾔任务框架会提供如下的功能:
分布式。Job不会受限于单机,可以由集来提供运⾏⽀持,可以随着压⼒的上升进⾏集扩容,任何⼀台机器的宕机不会成为问题。
如果我们采⽤中间件⽅式的话,这个功能由底层的中间件来⽀持了。
API层⾯提供丰富的Job执⾏⽅式。⽐如任务式的Job,拉数据和处理分开的Job。拉数据和处理分开的话,我们可以对数据的处理进⾏分⽚执⾏,实现类似Map-Reduce的效果。
执⾏依赖。我们可以配置Job的依赖关系实现⾃动化的Job执⾏流程分析。业务只管实现拆散的业务Job,Job的编排通过规则由框架分析出来。
整合到全链路监控体系的监控跟踪。
丰富的管理后台,提供统⼀的执⾏时间、数据取量配置,提供Job执⾏状态和依赖分析⼀览,查看执⾏历史,运⾏、暂停、停⽌Job等等管理功能。
七、发布管理
发布管理其实和开发没有太⼤的关联,但是我觉得这也是整个体系闭环中的⼀个环节。发布管理可以使⽤Jenkins等开源实现,在后期可能还是需要有⾃⼰的发布系统。可以基于Jenkins再包⼀层,也可以如最开始的图所⽰,直接基于通⽤的任务调度中间件实现底层的部署。⼀般⽽⾔,发布管理有下⾯的功能:
丰富的任务类型和插件,⽀持各种语⾔程序的构建和发布。有最基本的发布、回滚、重启、停⽌功能。
⽀持项⽬的依赖关系设置,实现⾃动化的依赖路径上的程序⾃动发布。
⼀些运维层⾯的控制。⽐如和CMDB结合做权限控制,做发布窗⼝控制。
⽤于集的发布流程。⽐如可以⼀览集的分组,设置⾃动的灰度发布⽅案。
适合⾃⼰公司的发布流程。⽐如在流程控制上,我们是Dev环境到QA到Stage到Live。其中,QA环境经过QA的确认后可以进⼊Stage 环境,经过开发主管的确认后可以到Stage环境,经过产品经理的确认后可以进⼊Live环境进⾏发布。在发布系统上我们可以结合OA 做好这个流程的控制。
在构建的时候,集成单元测试,集成编码规范检查等等,在后台可以⽅便的看到每⼀次发布的代码变更,测试执⾏情况以及代码规范违例。
Jenkins等系统在对于1和2做的⽐较好,对于和公司层⾯其它系统的结合⽆能⼒为,往往处于这个原因我们需要在Jenkins之上包装出来⾃⼰的发布系统。
总结⼀下,之所以标题说不断耕耘的基础中间件,是指中间件也好框架也好,往往也需要⼀个⼩团队来独⽴维护,⽽且功能是不断迭代增加,这套体系如果结合的好,就不仅仅是实现功能这个最基本的标准了,⽽是:
运维⾃动化API化和AI化的很重要的构成。把控是因为我们掌握了数据流,数据都是从我们的中间件穿越过去到达底层的服务、数据库、缓存,有了把控就有了⾃动化的可能,有了智能监控⼀体化报警的可能。
也因为数据流的经过,通过对数据进⾏分析,我们可以给到开发很多建议,我们可以在这上⾯做很多标准。这些事情都可以由框架架构团队默默去做,不需要业务研发的配合。
因为底层数据源的屏蔽,加上服务框架⼀起,我们实现的是业务系统被框架包围⽽不是业务系统在使⽤框架和中间件这么⼀个形态,那么对于公司层⾯的⼀些⼤型架构改造,⽐如多活架构,我们可以实现业务系统的改造最⼩。数据+服务+流程都已经被中间件所包围和感知,业务系统只是在实现业务功能⽽已,我们可以在业务系统⽆感知的情况下对数据做动态路由,对服务做动态调⽤,对流程做动态控制。如下图,是不是有点Mesh的意思?
本⽂很多地⽅基于思考和YY,开源组件要实现这个理念需要有⼤量的修改和整合,很多⼤公司内部都⼀定程度做了这些事情,但是也因为框架的各种粘连依赖⽆法彻底开源,这块⼯作要做好需要⼤量的时间精⼒,真的需要不断耕耘和沉淀才能发展出适合⾃⼰公司技术栈的各种中间件和管理系统体系。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。