朱晔的互联网架构实践心得S1E5:不断耕耘的基础中间件

朱晔的互联网架构实践心得S1E5:不断耕耘的基础中间件git

下载本文PDF进行阅读github

通常而言中间件和框架的区别是,中间件是独立运行的用于处理某项专门业务的CS程序,会有配套的客户端和服务端,框架虽然也是处理某个专门业务的可是它不是独立程序,是寄宿在宿主程序进程内的一套类库。算法

 

图上绿色部分表明了框架,红色部分表明了管理系统,紫色部分表明了中间件。本文会着重介绍管理系统和中间件部分。数据库

配置管理

比较知名的分布式配置服务和管理系统有携程的https://github.com/ctripcorp/apollo(上图)以及https://github.com/knightliao/disconf。对于比较大型的互联网项目来讲,由于业务繁杂,需求多变,每每各类系统都会有大量的配置,覆盖几个方面:后端

  • 针对系统内部技术层面的各类配置,各类池的大小、 队列的大小、日志级别、各类路径、批次大小、处理间隔、重试次数、超时时间等。
  • 针对业务运营层面的各类配置,活动的周期奖励、黑白名单、弹窗、广告位等。
  • 针对运维和发布层面的配置,灰度名单、注册中心地址、数据库地址、缓存地址、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那套(开发符合时候须要符合标准+发布的时候须要有流程)方式来运做。网络

 

全链路监控

开源的实现有https://github.com/dianping/cat以及https://github.com/naver/pinpoint(上图)等等。对于微服务比较多的(主流程涉及8+微服务)系统,若是没有服务的全链路调用跟踪那么排查故障以及性能问题就会很困难了。通常完善的全链路监控体系不只仅覆盖微服务,并且功能也会更丰富,实现下面的功能:架构

  • 以Log、Agent、Proxy或整合进框架的方式实现,尽量少的侵入的状况下实现数据的收集。并且确保数据的收集不会影响到主业务,收集服务端宕机的状况下业务不影响。
  • 调用跟踪。涉及到服务调用、缓存调用、数据库调用,MQ调用,不只仅能够以树的形式呈现每次调用的类型、耗时、结果,还能够呈现完整的根,也就是对于网站请求呈现出请求的完整信息,对于Job任务呈现出Job的信息。
  • JVM的信息(好比对于Java)。呈现每个进程JVM层次的GC、Threads、Memory、CPU的使用状况。能够进行远程Stack的查看和Heap的快照(没有进程的内存信息,不少时候基于服务器层面粗粒度的资源使用状况的监控,基本不可能分析出根本缘由),而且能够设定策略进行按期的快照。虚拟机的信息查看和调用跟踪甚至能够经过快照进行关联,在出现问题的时候可以了解当时虚拟机的状态对于排查问题是很是有好处的。
  • 依赖关系一览。有的时候咱们作架构方案,第一步就是梳理模块和服务之间的依赖关系,只有这样咱们才能肯定影响范围重构范围,对于微服务作的比较复杂的项目来讲,每一个人可能只是关注本身服务的上下游,对于上游的上游和下游的下游彻底不清楚,致使公司也没有人能够说的清楚架构的全貌。这个时候咱们有全链路跟踪系统的话,能够对经过分析过去的调用来绘制出一张依赖关系的架构图。这个图若是对QPS作一些热点的话,还能够帮助咱们作一些运维层面的容量规划。
  • 高级分析建议。好比在全链路压测后定位分析瓶颈所在。定时分析全部组件的执行性能,得出性能衰退的趋势,提前进行问题预警。分析JVM的线程和GC状况,辅助定位High CPU和Memory Leak的问题。退一万步说,即便没有这样的自动化的高级分析,有了调用跟踪的图和组件依赖关系图,至少在出问题的时候咱们人能分析出来咋回事。
  • Dashboard。非必须,只要数据收集足够全面,如以前文章所示,咱们能够用Grafana来进行各类个性化的图表配置。

 

数据访问中间件

开源的实现有C实现的https://github.com/Qihoo360/Atlas以及Go实现的https://github.com/flike/kingshard 等。数据访问中间件是独立部署的数据库的透明代理,自己须要是以集群方式支持高可用,背后还须要对接多套数据库做为一个集群,通常而言会提供以下的功能:

  • 最经常使用的功能就是读写分离。也包括负载均衡和故障转移的功能,自动在多个从库作负载均衡,经过可用性探测,在主库出现故障的时候配合数据库的高可用和复制作主库的切换。
  • 随着数据量的增多须要分片功能。分片也就是Sharding,把数据按照必定的维度均匀分散到不一样的表,而后把表分布在多个物理数据库中,实现压力的分散。这里写入的Sharding通常而言没有太多的差别,可是读取方面由于涉及到归并汇总的过程,若是要实现复杂功能的话仍是比较麻烦的。因为分片的维度每每可能有多个,这方面能够采用多写多个维度的底层表来实现也能够采用维度索引表方式来实现。
  • 其它一些运维方面的功能。好比客户端权限控制,黑白名单,限流,超时熔断,和调用链搭配起来的调用跟踪,全量操做的审计搜索,数据迁移辅助等等。
  • 更高级的话能够实现SQL的优化功能。随时进行SQL的Profiler,而后达到必定阈值后提供索引优化建议。相似https://github.com/Meituan-Dianping/SQLAdvisor
  • 其它。极少的代理实现了分布式事务的功能(XA)。还能够实现代理层面的分布式悲观锁的功能。其实细想一下,SQL由于并非直接扔到数据库执行,这里的可能性就太多了,想干啥均可以。

实现上通常须要作下面几件事情:

  • 有一个高性能的网络模型,通常基于高性能的网络框架实现,毕竟Proxy的网络方面的性能不能成为瓶颈。
  • 有一个MySQL协议的解析器,开源实现不少,拿过来直接用便可。
  • 有一个SQL语法的解析器,Sharding以及读写分离免不了须要解析SQL,通常流程为SQL解析、查询优化、SQL路由、SQL重写,在把SQL提交到多台数据库执行后进行结果归并。
  • Proxy自己最好是无状态节点,以集群方式实现高可用。

这些功能除了Proxy方式的实现还有和数据访问标准结合起来的实现,好比改写JDBC的框架方式实现,两种实现方式各有优缺点。框架方式的实现不局限于数据库类型,性能略高,Proxy方式的实现支持任意的语言更透明,功能也能够作的更强大一些。最近还出现了边车Sidecard方式实现的理念,相似于ServiceMesh的概念,网上有一些资料,可是这种方式到目前为止还没看到成熟的实现。

 

分布式缓存中间件

相似于数据库的Proxy,这里是以缓存服务做为后端,提供一些集群化的功能。好比以Redis为后端的开源的实现有https://github.com/CodisLabs/codis以及饿了么的https://github.com/eleme/corvus 等等。其实不采用Proxy方式作,开发一个缓存客户端在框架层面作也是彻底能够的,可是以前也说了这两种方式各有优劣。代理方式的话更透明,若是有Java、Python、Go都须要连接Redis,咱们无需开发多套客户端了。通常实现下面的功能:

  • 分布式。这是最基本的,经过各类算法把Key分散到各个节点,提供必定的容量规划和容量报警功能。
  • 高可用。配合Redis的一些高可用方案实现必定程度的高可用。
  • 运维方面的功能。好比客户端权限控制,黑白名单,限流,超时熔断,全量操做的审计搜索,数据迁移辅助等等。
  • 跟踪和问题分析。配合全链路监控实现一体化的缓存访问跟踪。以及更智能的分析使用的状况,结合缓存的命中率,Value的大小,压力平衡性提供一些优化建议和报警,尽早发现问题,缓存的崩盘每每是有前兆的。
  • 完善的管理后台,能够一览集群的用量、性能,以及作容量规划和迁移方案。

若是Redis集群特别大的话的确是有一套的本身的Proxy体系会更方便,小型项目通常用不到。

 

任务(Job)管理

以前有提到过,Job是我认为的互联网架构体系中三马车的三分之一,扮演了重要的角色。开源实现有http://elasticjob.io/。Job的管理的实现有两种方式,一种是相似于框架的方式,也就是Job的进程是一直启动着的,由框架在合适的时候调用方法去执行。一种是相似于外部服务的方式,也就是Job的进程是按须要在合适的机器启动的。在本文一开始的图中,我画了一个任务调度的中间件,对于后一种方式的实现,咱们须要有一套中间件或独立的服务来复杂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,开源组件要实现这个理念须要有大量的修改和整合,不少大公司内部都必定程度作了这些事情,可是也由于框架的各类粘连依赖没法完全开源,这块工做要作好须要大量的时间精力,真的须要不断耕耘和沉淀才能发展出适合本身公司技术栈的各类中间件和管理系统体系。

相关文章
相关标签/搜索