文章内容
一、微服务架构
微服务架构在某种程度上是面向服务的架构SOA继续发展的下一步,它更加强调服务的”彻底拆分”。
1、优点
- 更好的开发规模
- 更快的开发速度
- 支持迭代开发或现代化增量开发
- 充分利用现代软件开发生态系统的优势(云、容器、 DevOps、Serverless)
- 支持水平缩放和细粒度缩放
- 小体量,降低了开发人员的认知复杂性
2、缺点
- 更高数量级的活动组件(服务、数据库、进程、容器、框架)
- 复杂性从代码转移到基础设施
- RPC 调用和网络通信的大量增加
- 整个系统的安全性管理更具有挑战性
- 整个系统的设计变得更加困难
- 引入了分布式系统的复杂性
二、微服务定义
1、一组小的服务
原来的单块服务都是业务能力大而全的打包在一个单块中,微服务主张把这些单块服务进行拆分,形成一个个小的独立的服务。这里有个最大的特点是“小”,那么纠结要小到什么程度才为之小,很多同学都会纠结这个小的点,因为这个小并没有特别和明确的规定,所以这也就引申出了现在很多DDD领域驱动设计来指引微服务的拆分,但基本上一个微服务能让一个开发人员能够独立的理解,基本上就称为一个微服务,具体有多少行代码并不是很关键。
2、独立的进程
微服务是运行在独立的进程当中,例如java程序部署在tomcat,也可以部署在容器docker中,容易本身也是一种进程,所以微服务可以以进程的方式去扩展。
3、轻量级的通讯
微服务主张使用轻量级去构建通讯机制,例如http,固定消息格式和减少消息格式,服务之间不耦合,让通讯尽量轻量。
4、基于业务能力
微服务是基于业务能力进行构建,例如有用户服务,登陆服务,商品服务,基于这些业务能力去构建这些微服务。
5、独立部署
微服务被拆分开后,每个团队独立维护自己的微服务,开发,迭代自己的微服务,是可以独立的去部署,团队之间是不需要特别的去协调,这些对业务开发维护可以做到更加的敏捷,轻量,快速。
6、无集中式管理
原来单体服务是需要整个技术团队是需要独立的架构团队去管理,统一架构,统一技术栈,统一存储,微服务就不太一样,微服务主张每个团队根据自己的技术需要,选择自己最熟悉,最高效解决问题的技术栈,甚至选择不同存储方式。
三、康威定律
康威法则设计系统的组织,其产生的设计和架构,等价组织的组织架构
1、单块应用时代
几个团队共同去对一个单块应用去开发和维护时,如果一个团队对这个单块应用进行改造引入一些新的功能或技术的时候,往往需要其他的团队协作和配合,连同做集成测试才能交付这个应用,这个时候,不仅仅是沟通协调成本高,团队和团队之间往往容易产生摩擦。也就是说,多团队之间和单应用产生不匹配,违反康威法则。
2、微服务时代
怎么解决,微服务是一个解决的手段:
我们把单块的应用拆分成若干个独立的应用,每个团队负责自己的服务,相互之间不干扰,当团队A服务的服务进行修改不需要其他的团队来配合,或者说这种配合沟通成本比较少,一般只发生在双方边界交集的地方,那么这个时候发现多团队和微服务之间架构的关系可以映射起来,它符合了康威法则,整体研发效率更高效。
四、微服务利弊
1、利
1)强模块化边界
我们知道做软件架构,软件设计,模块化是非常重要的一点,一开始我们写程序做软件,我们采用类的方式来做模块化,后面开始采用组件或类库的方式做模块化,可以做到工程上的重用和分享给其他团队来使用。微服务在组件的层次上面又高了一层,以服务的方式来做模块化,每个团队独立开始和维护自己的服务,有明显的一个边界,开发完一个服务其他团队可以直接调用这个服务,不需要像组件通过jar或源码的方式去进行分享,所以微服务的边界是比较清晰的。
2)可独立部署
可独立部署是微服务最显著的一个特性,每个团队可以根据自己的业务需求,当产品经理或业务方把需求提过来,可以根据需要独立开发和部署服务,一般来说不需要太过依赖其他团队去协同,这个对比单块应用,单块引用在这个方面需求很多团队来协助和帮忙。
3)技术多样性
微服务是分散式治理,没有集中治理,每个团队可以根据团队自己的实际情况和业务的实际情况去选择适合自己的技术栈,有些团队可能擅长Java开发,有些团队可能更偏向前端,更适合用nodejs去开发服务,不过这个不是越多越好,技术栈的引入也是有成本
2、弊
1)分布式复杂性
在原来单块应用就是一个应用,一个对单块应用的架构比较熟悉的人可以对整个单块应用有一个很好的把控。但是到了分布式系统,微服务化了以后可能涉及到的服务有好几十个,一些大公司可能涉及到的服务上百个,服务与服务之间是通过相互沟通来实现业务,那么这个时候整个系统就变成非常复杂,一般的开发人员或一个团队都无法理解整个系统是如何工作的,这个就是分布式带来的复杂性。
2)最终一致性
微服务的数据是分散式治理的,每个团队都有自己的数据源和数据拷贝,比方说团队A有订单数据,B团队也有订单数据,团队A修改了订单数据是否应该同步给团队B的数据呢,这里就涉及到数据一致性问题,如果没有很好的解决一致性问题,就可能造成数据的不一致,这个在业务上是不可以接受的。
3)运维复杂性
以往的运维需要管理的是机器+单块的应用,分布式系统和单块应用不一样的是,分布式系统需要很多的服务,服务与服务之间相互协同,那么对分布式系统的资源,容量规划,对监控,对整个系统的可靠性稳定性都非常具备挑战的。
4)测试复杂性
对测试人员来说,在单块应用上,一个测试团队只需要测试一个单块应用就可以了,到了分布式系统,各个服务是分布在各个团队的,这个对测试团队来说要求就很好,做集成测试的时候需要很多的团队相互配合去联合做集成测试。
五、微服务分层
1、BFF是什么?
BFF即 Backend For Frontend(服务于前端的后端),也就是服务器设计 API 时会考虑前端的使用,并在服务端直接进行业务逻辑的处理,又称为用户体验适配器。BFF 只是一种逻辑分层,而非一种技术,虽然 BFF 是一个新名词,但它的理念由来已久。
2、微服务整个体系大的方向
一般将微服务整个体系大的方向划分为2层,见下图:
在最上层不属于微服务有很多的连接方式,有PC,有H5,有APP等等,在下层,包含了2个层,它们一起组成我们的微服务或SOA,微服务加单的划分为2层,最底下的基础服务层。
1)微服务基础服务层
基础服务一般属于互联网平台基础性的支撑服务,比方说,电商网站的基础服务有订单服务,商品服务,用户服务等,这些都属于比较基础和原子性,下沉一个公司的基础设施的低层,向下承接存储,向上提供业务能力,有些公司叫(基础服务,中间层服务,公共服务),netflix成为中间层服务。我们暂且统称为基础服务。
2)微服务聚合服务层
已经有了基础服务能提供业务能力,为什么还需要聚合服务,因为我们有不同的接入端,如app和H5,pc等等,它们看似调用大致相同的数据,但其实存在很多差异,例如PC需要展示更多信息,APP需要做信息裁剪等等。一般低层服务都是比较通用的,基础服务应该对外输出相对统一的服务,在抽象上做得比较好。但是对不同的外界app和pc的接入,我们需要作出不同的适配,这个时候需要有一个层去做出聚合裁剪的工作。例如一个商品详情在pc端展示和app端的展示,pc可能会展示更多的信息,而app则需要对信息作出一些裁剪,如果基础服务直接开放接口给到pc和app,那么基础服务也需要去做成各种设配,这个很不利于基础服务的抽象,所以我们在基础层之上加入聚合服务层,这个层可以针对pc和app做成适当的设配进行相应的裁剪。
除了裁剪,还有一个更重要的原因,假定PC端想获取“我的订单”的一个列表,那么Pc端必须知道和调用基础服务的几个模块(用户服务,订单服务,商品服务),pc端发起几次请求之后,在这个几个请求的数据进行汇总,这样不仅性能低下网络开销也比较大,还有pc端要承接数据聚合的这么一个工作,会让pc端变成更为复杂。如果在中间加多一个层为聚合服务层,即对网络开销进行减少,因为微服务内部是通过内网进行数据传输,也让pc端的业务变得比较简单。
这个层在各个公司有不同叫法,有叫 聚合服务,边界服务,设配服务 netflix叫边界服务,因为它处在公司微服务和外部的边界之上。这个的划分只是一个逻辑划分,在物理上或者在微服务上这两个层级其实在部署和调用没有差别。
3、代码分层设计
六、技术架构体系
1、接入层
接入层负责把外部的流量接入到内部平台系统中来,涉及到更多是基础设施,由运维团队进行负责。
2、基础设施层
基础设施层主要是由运维团队来进行维护,设计由计算,也计算资源的分配,网络,存储,监控,安全等等
3、网关层
流量接进来之后,会先经过一个网关层,网关在微服务起到举足轻重的作用,主要起到反向路由,限流熔断,安全,鉴权等等的跨横切面的功能,这个层在微服务中起到核心的层次。
4、业务服务层
整个业务服务按照逻辑划分为两次,分别是聚合层和基础服务层,聚合层对基础层进行聚合和裁剪,对外部提供业务能力。当然这个视每个公司不同情况而定,此层再划分两层是属于逻辑划分。
5、支撑服务层
微服务并不是单单把业务服务启动起来就完毕,微服务在治理的过程中还需要更多的支持,所以由了支撑服务层。支持服务包含了注册发现,集中配置,容错限流,日志聚合,监控告警,后台服务,后台服务涉及到例如MQ,Job,数据访问,这些都是后台服务的内容。
6、平台服务层
在微服务逐步完善的过程中,各个团队的都引入一个新的平台服务,例如由容器,镜像的管理,容器的服务编排等等,很多公司先后的引入容器化和容器编排来解决运维管理和发布微服务的难题,docker + keburnetes,确实是被微服务越来越接受。另外,通过CICD支撑起来的devops也是构建在平台服务层的这个能力。由于这个平台服务层的逐步完善也在慢慢解放运维人员一开始对微服务各种治理的不适。
七、服务发现机制
1、第一种:传统Lb模式
这个模式有一个独立的Lb,例如可以硬件F5做负载均衡器,也可以用软件,例如nginx来做负载均衡器,一般来说生产者上线后,会想运维申请一个域名,将域名配置到负载均衡器上,生产者的服务会部署多份,Lb具有负载均衡的功能。消费方想要去进行消费,会通过dns做域名解析,dns会解析到Lb上面,Lb会负载均衡到后台的生产者服务。这种做法是最传统的做法,也是最简单,消费者接入成本低,但是生产者发布服务需要运维的介入。还有一点问题就是Lb成为整个服务中转中心,如何确保这个Lb为高可用,另外还有一点,就是消费者调用生产者必须穿透Lb,这当中可能会有一些性能开销。
2、第二种:进程内Lb模式
这种做法把传统的Lb转移到进程内,生产者会把自己的信息注册到一个注册中心,并且定期发送心跳建立生产者和注册中心的连接。消费者去监听注册中心,从注册中心获取生产者的列表,Lb存在消费者的进程内,消费者直接使用内部Lb去调用生产者,消费者的Lb会定期去同步注册中心的服务信息。这种做法的好处是没有中间的一跳,不存在集中式Lb的性能短板,也不存在Lb可能存在的单点问题。但是在多语言中,必须每个语言都维护自己的一个Lb,我们熟知的Dubbo就是采用这种进程内Lb模式。
3、第三种:主机独立Lb模式
主机独立Lb模式是在前面传统Lb和进程内Lb的模式上做了折中,它把一个Lb以一个独立进程的方式部署在一台独立主机上,既不是集中式Lb也不是进程内Lb,这种方式跟第二种有一些类似,生产者一样注册到注册中心,主机上的Lb也会定时同步注册中心的注册信息,把注册信息放在本地进程中进行负载均衡,这种方式Lb既不存在消费者的进程内,可以让消费者更专注于业务,还可以免去集中式Lb每次调用都必须进行中转一跳的网络开销,并且也可以支持多语言跟消费者语言脱离关系。不过这种模式可能在运维的成本会比较高,运维需要关注每一台机器的LB。
八、API网关
1、为什么要使用网关?
让客户看公司的服务会认为是一个整体的服务。这个就是我们的网关所起到的作用,能够屏蔽我们内部的细节,统一输出对外的接口。
2、微服务架构的四个层次
从这张图上看到有四个层次,最上层是我们用户层,第二层是我们的负载均衡器,第三层就是我们的网关,最后是我们内部的微服务,在接入网关的时候为什么需要在上面需要一个负载均衡器,因为我们想让网关无状态,无状态的网关有一个好处,可以部署很多,不会有单点,即使挂了一台,其他的网关还在,这个对整个系统的稳定性起来非常重要的作用。一般的系统会有一个LB,然后对应多个网关。
3、网关能起到的作用
网关能起到的作用很多:
- 1. 最重要的一个重要反向路由,当外面的请求进来之后,怎么找到内部具体的微服务,这个是网关起来重要职责,将外部的调用转化为内部的服务服务,这个就是反向路由
- 2. 第二个是认证安全,网关像是一个门卫,有一些访问是正常的访问,有一些是恶意的访问,例如说爬虫,甚至是一些黑客行为,网关需要将其拦截在外部- 第三个重要职责是限流熔断,比方说,有一个门,外面有流量进来,正常来说流量是比较稳定的,但也有可能有突发流量,有可能网站在搞促销,这个时候可能就有流量的洪峰闯进来,如果说内部没有好的限流熔断措施,可能造成内部整个服务的服务器瘫痪,网关就要承担限流熔断的职责。
- 3. 最后一个功能,网关要承担日志监控的职责,外部的访问所有的流量都要经过网关,那么可以在网关上可以对所有的流量做访问的审计,把它作为日志保存起来,另外可以通过分析日志,知道性能的调用情况,能够对整个流量情况进行监控
九、路由发现体系
1、Netflix的两个重要支撑服务
在Netflix的微服务架构中,有两个非常重要的支撑服务
- Netflix的大名鼎鼎的注册中心组件叫Eureka
- 另一个Netflix也是大名鼎鼎的网关组件叫Zuul
2、Netflix内部的两层逻辑划分
Netflix在内部微服务上也是两层的逻辑划分,低层是基础服务(Netflix叫中间层服务),上一层叫聚合服务层,(Netflix叫边界服务),内部服务的发现也是通过注册中心Eureka,基础服务向Eureka进行服务注册,聚合服务通过Eureka进行服务发现,并把聚合层生产者缓存在本身,就可以进行直接的服务调用。
网关层是处在外部调用和聚合服务之间的层,网关层可以看作是一个超级的客户端,它一样可以作为微服务的一个组件,也会同步Eureka注册中心的路由表,外部服务请求进来后,网关根据路由表找到对应的聚合服务进行调用。
另外注册中心和网关还可以对整个调用进行治理,比方说对服务的调用进行安全管控,哪些服务是是有严格的安全要求,不允许随便进行调用,哪些服务可以通过网关放出去,这些能力可以通过网关和注册中心进行实现。这些就是服务治理相关的能力。
十、配置中心
配置中心可以简单的理解为一个服务模块,开发人员或运维人员可以通过界面对配种中心进行配置,下面相关的微服务连接到配置中心上面就可以实时连接获取到配置中心上面修改的参数。
1、配置中心的两种更新方式
更新的方式一般有两种
- pull模式,服务定时去拉取配置中心的数据
- push模式,服务一直连接到配置中心上,一旦配置有变成,配种中心将把变更的参数推送到对应的微服务上
2、配置中心更新方式的利弊
这两种做法其实各有利弊
- pull可以保证一定可以拉取得到数据,pull一般采用定时拉取的方式,即使某一次出现网络没有拉取得到数据,那在下一次定时器也将可以拉取得到数据,最终保证能更新得到配置
- push也有好处,避免pull定时器获取存在时延,基本可以做到准实时的更新,但push也存在问题,如果有网络抖动,某一次push没有推送成功,将丢失这次配置的更新
3、目前比较流行的配置中心开源组件
目前比较流行的配种中心开源组件有springcloud-Config,百度的disconf,阿里的diamond,还有携程的apollo,杨波老师之前是在携程工作过,所以主推的是apollo这套系统。
也是一个服务器,也带有对应的客户端,它的特色在于客户端,客户端有一个缓存机制,每次拉取成功后,会把数据保存在缓存机制中,甚至爬客户端的缓存丢失,客户端还可以将缓存sync在本地文件缓存,这样的设计非常的巧妙,就算apollo的配置中心挂掉了,或者客户端的服务重启了,但是因为本地缓存还存在,还可以使用本地缓存继续对外提供服务,从这点来看apollo的配置中心在高可用上考虑还是比较周到的。
4、配置中心两种获取配置数据的方式
另外一点,配置中心有两种获取配置数据的方式,一种pull,一种push,两者各有有点,apollo把两者的优点进行了结合,开发或运维人员在配置中心进行修改,配置中心服务将实时将修改推送push到apollo的客户端,但考虑到可能由于某些网络抖动没有推送成功,客户端还具备了定时向apollo服务端拉取pull数据的功能,就算推送没成功,但是只要一定时间周期,客户端还是会主动去拉取同步数据,保证能把最终配置同步到服务中。这个也是apollo在高可用方面上非常有特色的设计。
十一、微服务治理
1、服务注册发现
微服务当中有很多服务,几十个上百个,它们当中有错中复杂的依赖关系,这个时候就存在服务的消费者怎么发现生产者,这个就是服务注册发现需要解决的问题。
2、服务的负载均衡
为了应对大的流量,我们的服务提供方一般都是大规模部署,这个时候就存在服务的负载均衡的问题,另外我们的服务需要路由,这个能力非常重要,如果我们需要灰度发布或者蓝绿发布的机制,那么需要考虑软路由的机制。
3、监控-日志
日志对于我们后期排错找出问题,定位问题是非常关键,我们一套好的监控治理框架需要集成日志服务
4、监控-metrics
当我们需要对服务的调用量进行监控,对服务延迟出错数有一个好的监控手段,这就是me监控环境
5、监控-调用链埋点
微服务有错综复杂的调用关系,就像一个网状一般,如果没有好的调用链监控,开发人员很容易迷失在当中,出问题很难定位,有了好的调用链监控会帮助我们快速定位问题,更好的理解整套微服务系统。
6、限流熔断
微服务是一个分布式系统,如果没有好的限流熔断措施,当一个服务出现故障或者出现延迟,会造成整个系统瘫痪。
7、安全-访问控制
有些服务并不希望所有的人都能去调用到,涉及到一些敏感信息,比例跟钱相关的信息,那么我们需要安全和访问的控制策略,来限制对这些服务的访问。
8、rpc & rest
rpc和rest根据之前的对比,两者各有优劣,如果一个微服务框架中能支持这两个调用,能兼容更多的技术栈,会更加的灵活
9、序列化
序列化中,有高性能但对开发人员不优化的二进制序列化,也有对开发人员相当友好的但性能未必最佳的文本式序列化,这个需要根据场景进行灵活的配置,消息系列化协议
10、代码生成
现在在大规模开发的情况下,比较推从一个契约驱动开发的方法,开发人员先定立契约,代码自动生成的方式生成对应的代码脚手架,这个在大规模开发的时候更能确保代码的一致和规整
11、统一异常处理
我们希望服务治理的环节能集成统一的服务异常处理的能力,这样的化异常能够达到更加标准化,出现问题能更好定位好属于什么类型的问题。如果说没有这样的一个环节,大家各自的玩法不一样,抛的异常各异,出现问题难以定位和无法标准化友好输出。
12、文档
微服务最终是要给消费者去使用,暴露出去的API如果没有好的文档,只提供出一些代码,接入方接入的成本会变成比较高,好的文档体系是各方协调和效率的保证。
13、集中配置中心
微服务框架需要集成集中式的配置能力,避免各个服务间各自配置,增快参数调整的速度和规范统一格式。
14、后台集成MQ,Cache,DB
微服务治理的核心思路就是把上面讲到的各个环节沉淀下来,变成平台和框架的一部分,开发人员可以更加专注业务逻辑的实现,在实现业务逻辑的时候不需要去关注外部环节的,从而提升开发的效率,治理环节沉淀在框架之中有专门的平台架构团队去进行管控。
十二、微服务分层监控
1、分层监控
1)基础设施监控
一般是由运维人员进行负责,涉及到的方面比较接近硬件体系,例如网络,交换机,路由器等低层设备,这些设备的可靠性稳定性就直接影响到上层服务应用的稳定性,所以需要对网络的流量,丢包情况,错包情况,连接数等等这些基础设施的核心指标进行监控。
2)系统层监控
涵盖了物理机,虚拟机,操作系统这些都是属于系统级别监控的方面,对几个核心指标监控,如cpu使用率,内存占用率,磁盘IO和网络带宽情况。
3)应用层监控
涉及到方面就跟服务紧密相关,例如对url访问的性能,访问的调用数,访问的延迟,还有对服务提供性能进行监控,服务的错误率,对sql也需要进行监控,查看是否有慢sql,对与cache来说,需要监控缓存的命中率和性能,每个服务的响应时间和qps等等。
4)业务监控
比方说一个典型的交易网站,需要关注它的用户登录情况,注册情况,下单情况,支付情况,这些直接影响到实际触发的业务交易情况,这个监控可以提供给运营和公司高管他们需需要关注的数据,直接可能对公司战略产生影响。
5)端用户体验监控
一个应用程序可能通过app,h5,pc端的方式交付到用户的手上,用户通过浏览器,客户端打开练到到我们的服务,那么在用户端用户的体验是怎么样,用户端的性能是怎么样,有没有产生错误,这些信息也是需要进行监控并记录下来,如果没有监控,有可能用户的因为某些原因出错或者性能问题造成体验非常的差,而我们并没有感知,这里面包括了,监控用户端的使用性能,返回码,在哪些城市地区他们的使用情况是怎么样,还有运营商的情况,包括电信,联通用户的连接情况。我们需要进一步去知道是否有哪些渠道哪些用户接入的时候存在着问题,包括我们还需要知道客户端使用的操作系统浏览器的版本。
2、监控点
可以通过以下几点进行监控:
- 日志监控
- Metrics监控
- 调用链监控
- 报警系统
- 健康检查
3、典型主流的监控架构
- 在微服务运行的体系下,我们一般把监控的agent分散到各个服务身边,agent分别是收集机器和服务的metrics,发送到后台监控系统,一般来说,我们的服务量非常大,在收集的过程中,会加入队列,一般来说用kafka,用消息队列有个好处就是两边可以进行解耦,还好就是可以起到庞大的日志进行一个缓存的地带,并在mq可以做到高可用,保证消息不会丢失。
- 日志收集目前比较流行的是ELK的一套解决方案,(Elasticsearch,Logstash,Kibana),Elasticsearch 分布式搜索引擎,Logstash 是一个日志收集的agent,Kibana 是一个查询的日志界面。
- metrice会采用一个时间序列的数据库,influxDB是最近比较主流时间数据库。
- 微服务的agent例如springboot也提供了健康检查的端点,可以检查cpu使用情况,内存使用情况,jvm使用情况,这些需要一个健康检查机制,能够定期对服务的健康和机器的健康进行check,比较常见的是nagios,zabbix等,这些开源平台能够定期去检查到各个微服务的检查程序并能够进行告警给相关人员,在服务未奔溃之前就可以进行提前的预先接入。
十三、调用链
微服务是一个分布式非常复杂系统,如果没有一套调用链监控,如果服务之间依赖出现问题就很难进行调位。
目前个大主流互联网公司中,ali有非常出名的鹰眼系统,点评也有一套很出名的调用链监控系统CAT。调用链监控其实最早是google提出来的,2010年google发表了一篇调用链的论文,论文以它内部的调用链系统dapper命名,这个论文中讲解调用链在google使用的经验和原理,大致的原理如下图:
在外界对微服务进行一个请求开始进入我们的微服务体系时,会生成一个Root Span,当web服务去调用后面的服务svc1时又会生成一个span,调用DB也会生成一个span,每一个应用调度都会生成一个新的span,这个是span是整个调用链形成的关键,span中有一些关键的信息,有traceId,spanId。RootSpan是比较特殊的,在启动的时候会生成spanId还会生成TraceId,其他的span会生成自己的spanId,为了维护好调用链上下文的调用关系,span会去记录调用它的链路,以parent spanId记录下来,这样的话父子之间的关系就可以记录下来,每个调用都会把第一个链路traceId也记录下来,这样,当我们把这些span都存在起来,就可以通过分析手段,把整个调用链的关系还原出来。
这里可以采用ELK的方式去记录和展示调用链监控日志,当我们一条调用为一行记录存储下来:
通过traceId 和 parentSpanId 就可以串联起来为一个整体的链路,并可以从这个链路去分析错误或者调用延时和调用次数等等。
目前市面主流的调用链选型有 zipkin,pinpoint,cat,skywalking,他们之间各有一些偏重点,值得一说的是skywalking国人出品的一款新的调用链工具,采用开源的基于字节码注入的调用链分析,接入段无代码入侵,而且开源支持多种插件,UI在几款工具来说比较功能比较强大,而且ui也比较赏心悦目,目前已经加入了apache孵化器。
1、skywalking优势
- 首先在实现方式上,skywalking基本对于代码做到了无入侵,采用java探针和字节码增强的方式,而在cat还采用了代码埋点,而zipkin采用了拦截请求,pinpoint也是使用java探针和字节码增强
- 其次在分析的颗粒度上,skywaling是方法级,而zipkin是接口级,其他两款也是方法级
- 在数据存储上,skywalking可以采用日志体系中比较出名的ES,其他几款,zipkin也可以使用ES,pinpoint使用Hbase,cat使用mysql或HDFS,相对复杂,如果公司对ES熟悉的人才比较有保证,选择熟悉存储方案也是考虑技术选型的重点
- 还有就是性能影响,根据网上的一些性能报告,虽然未必百分百准备,但也具备参考价值,skywalking的探针对吞吐量的影响在4者中间是最小的,经过对skywalking的一些压测也大致证明
2、几款调用链选型对比
下面是网上摘录的几款调用链选型对比:
1)基本原理
类别 | Zipkin | Pinpoint | SkyWalking CAT |
实现方式 | 拦截请求,发送(HTTP,mq)数据至zipkin服务 | java探针,字节码增强 | java探针,字节码增强 |
2)接入
类别 | Zipkin | Pinpoint | SkyWalking | CAT |
接入方式 | 基于linkerd或者sleuth方式,引入配置即可 | javaagent字节码」 | javaagent字节码 | 代码侵入 |
agent到collector的协议 | http,MQ | thrift | gRPC | http/tcp |
OpenTracing | √ | × | √ | × |
3)分析
类别 | Zipkin | Pinpoint | SkyWalking | CAT |
颗粒度 | 接口级 | 方法级 | 方法级 | 代码级 |
全局调用统计 | × | √ | √ | √ |
traceid查询 | √ | × | √ | × |
报警 | × | √ | √ | √ |
JVM监控 | × | × | √ | √ |
4)页面UI展示
类别 | Zipkin | Pinpoint | SkyWalking | CAT |
健壮度 | ** | ***** | **** | ***** |
5)数据存储
类别 | Zipkin | Pinpoint | SkyWalking | CAT |
数据存储 | ES,mysql,Cassandra,内存 | Hbase | ES,H2 | mysql,hdfs |
十四、流量治理
1、熔断
如果说房子里面安装了电路熔断器,当你使用超大功率的电路时,有熔断设配帮你保护不至于出问题的时候把问题扩大化。
2、隔离
我们知道计算资源都是有限的,cpu,内存,队列,线程池都是资源,他们都是限定的资源数,如果不进行隔离,一个服务的调用可能要消耗很多的线程资源,把其他服务的资源都给占用了,那么可能出现应为一个服务的问题连带效应造成其他服务不能进行访问。
3、限流
让大流量的访问冲进去我们的服务时,我们需要一定的限流措施,比方说我们规则一定时间内只允许一定的访问数从我们的资源过,如果再大的化系统会出现问题,那么就需要限流保护。
4、降级
如果说系统后题无法提供足够的支撑能力,那么需要一个降级能力,保护系统不会被进一步恶化,而且可以对用户提供比较友好的柔性方案,例如告知用户暂时无法访问,请在一段时候后重试等等。
5、Hystrix
Hystrix就把上面说的 熔断,隔离,限流,降级封装在这么一个组件里面
1)Hystrix内部设计和调用流程
下图是Hystrix内部设计和调用流程
2)Hystrix大致的工作流
大致的工作流如下:
- 构建一个HystrixCommand对象,用于封装请求,并在构造方法配置请求被执行需要的参数
- 执行命令,Hystrix提供了几种执行命令的方法,比较常用到的是synchrous和asynchrous
- 判断电路是否被打开,如果被打开,直接进入fallback方法
- 判断线程池/队列/信号量是否已经满,如果满了,直接进入fallback方法
- 执行run方法,一般是HystrixCommand.run(),进入实际的业务调用,执行超时或者执行失败抛出未提前预计的异常时,直接进入fallback方法
- 无论中间走到哪一步都会进行上报metrics,统计出熔断器的监控指标
- fallback方法也分实现和备用的环节
- 最后是返回请求响应
十五、设计模式
1、独享数据库(Database per Microservice)
当一家公司将大型单体系统替换成一组微服务,首先要面临的最重要决策是关于数据库。单体架构会使用大型中央数据库。即使转移到微服务架构许多架构师仍倾向于保持数据库不变。虽然有一些短期收益,但它却是反模式的,特别是在大规模系统中,微服务将在数据库层严重耦合,整个迁移到微服务的目标都将面临失败(例如,团队授权、独立开发等问题)。
更好的方法是为每个微服务提供自己的数据存储,这样服务之间在数据库层就不存在强耦合。这里我使用数据库这一术语来表示逻辑上的数据隔离,也就是说微服务可以共享物理数据库,但应该使用分开的数据结构、集合或者表,这还将有助于确保微服务是按照领域驱动设计的方法正确拆分的。
1)优点
- 数据由服务完全所有
- 服务的开发团队之间耦合度降低
2)缺点
- 服务间的数据共享变得更有挑战性
- 在应用范围的保证 ACID 事务变得困难许多
- 细心设计如何拆分单体数据库是一项极具挑战的任务
3)何时使用独享数据库
- 在大型企业应用程序中
- 当团队需要完全把控微服务以实现开发规模扩展和速度提升
4)何时不宜使用独享数据库
- 在小规模应用中
- 如果是单个团队开发所有微服务
5)可用技术示例
所有 SQL、 NoSQL 数据库都提供数据的逻辑分离(例如,单独的表、集合、结构、数据库)。
2、事件源(Event Sourcing)
在微服务架构中,特别使用独享数据库时,微服务之间需要进行数据交换。对于弹性高可伸缩的和可容错的系统,它们应该通过交换事件进行异步通信。在这种情况,您可能希望进行类似更新数据库并发送消息这样的原子操作,如果在大数据量的分布式场景使用关系数据库,您将无法使用两阶段锁协议(2PL),因为它无法伸缩。而 NoSQL 数据库因为大多不支持两阶段锁协议甚至无法实现分布式事务。
在这些场景,可以基于事件的架构使用事件源模式。在传统数据库中,直接存储的是业务实体的当前“状态”,而在事件源中任何“状态”更新事件或其他重要事件都会被存储起来,而不是直接存储实体本身。这意味着业务实体的所有更改将被保存为一系列不可变的事件。因为数据是作为一系列事件存储的,而非直接更新存储,所以各项服务可以通过重放事件存储中的事件来计算出所需的数据状态。
1)优点
- 为高可伸缩系统提供原子性操作。
- 自动记录实体变更历史,包括时序回溯功能。
- 松耦合和事件驱动的微服务。
2)缺点
- 从事件存储中读取实体成为新的挑战,通常需要额外的数据存储(CQRS 模式)
- 系统整体复杂性增加了,通常需要领域驱动设计
- 系统需要处理事件重复(幂等)或丢失
- 变更事件结构成为新的挑战
3)何时使用事件源
- 使用关系数据库的、高可伸缩的事务型系统
- 使用 NoSQL 数据库的事务型系统
- 弹性高可伸缩微服务架构
- 典型的消息驱动或事件驱动系统(电子商务、预订和预约系统)
4)何时不宜使用事件源
- 使用 SQL 数据库的低可伸缩性事务型系统
- 在服务可以同步交换数据(例如,通过 API)的简单微服务架构中
3、命令和查询职责分离(CQRS)
如果我们使用事件源,那么从事件存储中读取数据就变得困难了。要从数据存储中获取实体,我们需要处理所有的实体事件。有时我们对读写操作还会有不同的一致性和吞吐量要求。
这种情况,我们可以使用 CQRS 模式。在该模式中,系统的数据修改部分(命令)与数据读取部分(查询)是分离的。而 CQRS 模式有两种容易令人混淆的模式,分别是简单的和高级的。
在其简单形式中,不同实体或 ORM 模型被用于读写操作,如下所示:
它有助于强化单一职责原则和分离关注点,从而实现更简洁的设计。
在其高级形式中,会有不同的数据存储用于读写操作。高级的 CQRS 通常结合事件源模式。根据不同情况,会使用不同类型的写数据存储和读数据存储。写数据存储是“记录的系统”,也就是整个系统的核心源头。
对于读频繁的应用程序或微服务架构,OLTP 数据库(任何提供 ACID 事务保证的关系或非关系数据库)或分布式消息系统都可以被用作写存储。对于写频繁的应用程序(写操作高可伸缩性和大吞吐量),需要使用写可水平伸缩的数据库(如全球托管的公共云数据库)。标准化的数据则保存在写数据存储中。
对搜索(例如 Apache Solr、Elasticsearch)或读操作(KV 数据库、文档数据库)进行优化的非关系数据库常被用作读存储。许多情况会在需要 SQL 查询的地方使用读可伸缩的关系数据库。非标准化和特殊优化过的数据则保存在读存储中。
数据是从写存储异步复制到读存储中的,所以读存储和写存储之间会有延迟,但最终是一致的。
1)优点
- 在事件驱动的微服务中数据读取速度更快
- 数据的高可用性
- 读写系统可独立扩展
2)缺点
- 读数据存储是弱一致性的(最终一致性)
- 整个系统的复杂性增加了,混乱的 CQRS 会显着危害整个项目
3)何时使用CQRS
- 在高可扩展的微服务架构中使用事件源
- 在复杂领域模型中,读操作需要同时查询多个数据存储
- 在读写操作负载差异明显的系统中
4)何时不宜使用CQRS
- 在没有必要存储大量事件的微服务架构中,用事件存储快照来计算实体状态是一个更好的选择
- 在读写操作负载相近的系统中
4、Saga
如果微服务使用独享数据库,那么通过分布式事务管理一致性是一个巨大的挑战。你无法使用传统的两阶段提交协议,因为它要么不可伸缩(关系数据库),要么不被支持(多数非关系数据库)。
但您还是可以在微服务架构中使用 Saga 模式实现分布式事务。Saga 是 1987 年开发的一种古老模式,是关系数据库中关于大事务的一个替代概念。但这种模式的一种现代变种对分布式事务也非常有效。Saga 模式是一个本地事务序列,其每个事务在一个单独的微服务内更新数据存储并发布一个事件或消息。Saga 中的首个事务是由外部请求(事件或动作)初始化的,一旦本地事务完成(数据已保存在数据存储且消息或事件已发布),那么发布消息或事件则会触发 Saga 中的下一个本地事务。
如果本地事务失败,Saga 将执行一系列补偿事务来回滚前面本地事务的更改。
1)Saga事务协调管理的两种形式
Saga 事务协调管理主要有两种形式:
- 事件编排 Choreography:分散协调,每个微服务生产并监听其他微服务的事件或消息然后决定是否执行某个动作
- 命令编排 Orchestration:集中协调,由一个协调器告诉参与的微服务哪个本地事务需要执行
2)优点
- 为高可伸缩或松耦合的、事件驱动的微服务架构提供一致性事务
- 为使用了不支持 2PC 的非关系数据库的微服务架构提供一致性事务
3)缺点
- 需要处理瞬时故障,并且提供等幂性
- 难以调试,而且复杂性随着微服务数量增加而增加
4)何时使用Saga
- 在使用了事件源的高可伸缩、松耦合的微服务中
- 在使用了分布式非关系数据库的系统中
5)何时不宜使用Saga
- 使用关系数据库的低可伸缩性事务型系统
- 在服务间存在循环依赖的系统中
5、面向前端的后端 (BFF)
在现代商业应用开发,特别是微服务架构中,前后端应用是分离和独立的服务,它们通过 API 或 GraphQL 连接。如果应用程序还有移动 App 客户端,那么 Web 端和移动客户端使用相同的后端微服务就会出现问题。因为移动客户端和 Web 客户端有不同的屏幕尺寸、显示屏、性能、能耗和网络带宽,它们的 API 需求不同。
面向前端的后端模式适用于需要为特殊 UI 定制单独后端的场景。它还提供了其他优势,比如作为下游微服务的封装,从而减少 UI 和下游微服务之间的频繁通信。此外,在高安全要求的场景中,BFF 为部署在 DMZ 网络中的下游微服务提供了更高的安全性。
1)优点
- 分离 BFF 之间的关注点,使得我们可以为具体的 UI 优化他们
- 提供更高的安全性
- 减少 UI 和下游微服务之间频繁的通信
2)缺点
- BFF 之间代码重复
- 大量的 BFF 用于其他用户界面(例如,智能电视,Web,移动端,PC 桌面版)
- 需要仔细的设计和实现,BFF 不应该包含任何业务逻辑,而应只包含特定客户端逻辑和行为
3)何时使用BFF
- 如果应用程序有多个含不同 API 需求的 UI
- 出于安全需要,UI 和下游微服务之间需要额外的层
- 如果在 UI 开发中使用微前端
4)何时不宜使用BFF
- 如果应用程序虽有多个 UI,但使用的 API 相同
- 如果核心微服务不是部署在 DMZ 网络中
5)可用技术示例
任何后端框架(Node.js,Spring,Django,Laravel,Flask,Play,…)都能支持。
6、API网关
在微服务架构中,UI 通常连接多个微服务。如果微服务是细粒度的(FaaS) ,那么客户端可能需要连接非常多的微服务,这将变得繁杂和具有挑战性。此外,这些服务包括它们的 API 还将不断进化。大型企业还希望能有其他横切关注点(SSL 终止、身份验证、授权、节流、日志记录等)。
一个解决这些问题的可行方法是使用 API 网关。API 网关位于客户端 APP 和后端微服务之间充当 facade,它可以是反向代理,将客户端请求路由到适当的后端微服务。它还支持将客户端请求扇出到多个微服务,然后将响应聚合后返回给客户端。它还支持必要的横切关注点。
1)优点
- 在前端和后端服务之间提供松耦合
- 减少客户端和微服务之间的调用次数
- 通过 SSL 终端、身份验证和授权实现高安全性
- 集中管理的横切关注点,例如,日志记录和监视、节流、负载平衡
2)缺点
- 可能导致微服务架构中的单点故障
- 额外的网络调用带来的延迟增加
- 如果不进行扩展,它们很容易成为整个企业应用的瓶颈
- 额外的维护和开发费用
3)何时使用API网关
- 在复杂的微服务架构中,它几乎是必须的
- 在大型企业中,API 网关是中心化安全性和横切关注点的必要工具
4)何时不宜使用API网关
- 在安全和集中管理不是最优先要素的私人项目或小公司中
- 如果微服务的数量相当少
7、Strangler
如果想在运行中的项目中使用微服务架构,我们需要将遗留的或现有的单体应用迁移到微服务。将现有的大型在线单体应用程序迁移到微服务是相当有挑战性的,因为这可能破坏应用程序的可用性。
一个解决方案是使用 Strangler 模式。Strangler 模式意味着通过使用新的微服务逐步替换特定功能,将单体应用程序增量地迁移到微服务架构。此外,新功能只在微服务中添加,而不再添加到遗留的单体应用中。然后配置一个 Facade (API 网关)来路由遗留单体应用和微服务间的请求。当某个功能从单体应用迁移到微服务,Facade 就会拦截客户端请求并路由到新的微服务。一旦迁移了所有的功能,遗留单体应用程序就会被“扼杀(Strangler)”,即退役。
1)优点
- 安全的迁移单体应用程序到微服务
- 可以并行地迁移已有功能和开发新功能
- 迁移过程可以更好把控节奏
2)缺点
- 在现有的单体应用服务和新的微服务之间共享数据存储变得具有挑战性
- 添加 Facade (API 网关)将增加系统延迟
- 端到端测试变得困难
3)何时使用Strangler
- 将大型后端单体应用程序的增量迁移到微服务
4)何时不宜使用Strangler
- 如果后端单体应用很小,那么全量替换会更好
- 如果无法拦截客户端对遗留的单体应用程序的请求
8、断路器
在微服务架构中,微服务通过同步调用其他服务来满足业务需求。服务调用会由于瞬时故障(网络连接缓慢、超时或暂时不可用) 导致失败,这种情况重试可以解决问题。然而,如果出现了严重问题(微服务完全失败),那么微服务将长时间不可用,这时重试没有意义且浪费宝贵的资源(线程被阻塞,CPU 周期被浪费)。此外,一个服务的故障还会引发整个应用系统的级联故障。这时快速失败是一种更好的方法。
在这种情况,可以使用断路器模式挽救。一个微服务通过代理请求另一个微服务,其工作原理类似于电气断路器,代理通过统计最近发生的故障数量,并使用它来决定是继续请求还是简单的直接返回异常。
1)断路器的三种状态
断路器可以有以下三种状态:
- 关闭:断路器将请求路由到微服务,并统计给定时段内的故障数量,如果超过阈值,它就会触发并进入打开状态
- 打开:来自微服务的请求会快速失败并返回异常。在超时后,断路器进入半开启状态
- 半开:只有有限数量的微服务请求被允许通过并进行调用。如果这些请求成功,断路器将进入闭合状态。如果任何请求失败,断路器则会进入开启状态
2)优点
- 提高微服务架构的容错性和弹性
- 阻止引发其他微服务的级联故障
3)缺点
- 需要复杂的异常处理
- 日志和监控
- 应该支持人工复位
4)何时使用断路器
- 在微服务间使用同步通信的紧耦合的微服务架构中
- 如果微服务依赖多个其他微服务
5)何时不宜使用断路器
- 松耦合、事件驱动的微服务架构
- 如果微服务不依赖于其他微服务
9、外部化配置
每个业务应用都有许多用于各种基础设施的配置参数(例如,数据库、网络、连接的服务地址、凭据、证书路径)。此外在企业应用程序通常部署在各种运行环境(Local、 Dev、 Prod)中,实现这些的一个方法是通过内部配置。这是一个致命糟糕实践,它会导致严重的安全风险,因为生产凭证很容易遭到破坏。此外,配置参数的任何更改都需要重新构建应用程序,这在在微服务架构中会更加严峻,因为我们可能拥有数百个服务。
更好的方法是将所有配置外部化,使得构建过程与运行环境分离,生产的配置文件只在运行时或通过环境变量使用,从而最小化了安全风险。
1)优点
- 生产配置不属于代码库,因而最小化了安全漏洞
- 修改配置参数不需要重新构建应用程序
2)缺点
- 我们需要选择一个支持外部化配置的框架
3)何时使用外部化配置
- 任何重要的生产应用程序都必须使用外部化配置
4)何时不宜使用外部化配置
- 在验证概念的开发中
10、消费端驱动的契约测试
在微服务架构中,通常有许多有不同团队开发的微服务。这些微型服务协同工作来满足业务需求(例如,客户请求),并相互进行同步或异步通信。消费端微服务的集成测试具有挑战性,通常用 TestDouble 以获得更快、更低成本的测试运行。但是 TestDouble 通常并不能代表真正的微服务提供者,而且如果微服务提供者更改了它的 API 或 消息,那么 TestDouble 将无法确认这些。另一种选择是进行端到端测试,尽管它在生产之前是强制性的,但却是脆弱的、缓慢的、昂贵的且不能替代集成测试(Test Pyramid)。
在这方面消费端驱动的契约测试可以帮助我们。在这里,负责消费端微服务的团队针对特定的服务端微服务,编写一套包含了其请求和预期响应(同步)或消息(异步)的测试套件,这些测试套件称为显式的约定。对于微服务服务端,将其消费端所有约定的测试套件都添加到其自动化测试中。当特定服务端微服务的自动化测试执行时,它将一起运行自己的测试和约定的测试并进行验证。通过这种方式,契约测试可以自动的帮助维护微服务通信的完整性。
1)优点
- 如果提供程序意外更改 API 或消息,可以被快速的自动发现
- 更少意外、更健壮,特别是包含大量微服务的企业应用程序
- 改善团队自主性
2)缺点
- 需要额外的工作来开发和集成微服务服务端的契约测试,因为他们可能使用完全不同的测试工具
- 如果契约测试与真实服务情况不匹配,将可能导致生产故障
3)何时使用需求驱动的契约测试
- 在大型企业业务应用程序中,通常由不同的团队开发不同服务
4)何时不宜使用消费端驱动的契约测试
- 所有微服务由同一团队负责开发的小型简单的应用程序
- 如果服务端微服务是相对稳定的,并且不处在活跃的开发状态
十六、关键设计
1、监控-发现故障的征兆
2、定位问题-链路跟踪
要实现链路跟踪,每次服务调用会在HTTP的HEADERS中记录至少记录四项数据:
- traceId:traceId标识一个用户请求的调用链路。具有相同traceId的调用属于同一条链路
- spanId:标识一次服务调用的ID,即链路跟踪的节点ID
- parentId:父节点的spanId
- requestTime & responseTime:请求时间和响应时间
3、分析问题-日志分析
一般使用ELK日志分析组件。ELK是Elasticsearch、Logstash和Kibana三个组件的缩写。
- Elasticsearch:搜索引擎,同时也是日志的存储。
- Logstash:日志采集器,它接收日志输入,对日志进行一些预处理,然后输出到Elasticsearch。
- Kibana:UI组件,通过Elasticsearch的API查找数据并展示给用户。
最后还有一个小问题是如何将日志发送到Logstash。一种方案是在日志输出的时候直接调用Logstash接口将日志发送过去。这样一来又(咦,为啥要用“又”)要修改代码……于是小明选用了另一种方案:日志仍然输出到文件,每个服务里再部署个Agent扫描日志文件然后输出给Logstash。
4、网关-权限控制,服务治理
5、服务注册于发现-动态扩容
6、熔断、服务降级、限流
1)熔断
当一个服务因为各种原因停止响应时,调用方通常会等待一段时间,然后超时或者收到错误返回。如果调用链路比较长,可能会导致请求堆积,整条链路占用大量资源一直在等待下游响应。所以当多次访问一个服务失败时,应熔断,标记该服务已停止工作,直接返回错误。直至该服务恢复正常后再重新建立连接。
2)服务降级
当下游服务停止工作后,如果该服务并非核心业务,则上游服务应该降级,以保证核心业务不中断。比如网上超市下单界面有一个推荐商品凑单的功能,当推荐模块挂了后,下单功能不能一起挂掉,只需要暂时关闭推荐功能即可。
3)限流
一个服务挂掉后,上游服务或者用户一般会习惯性地重试访问。这导致一旦服务恢复正常,很可能因为瞬间网络流量过大又立刻挂掉,在棺材里重复着仰卧起坐。因此服务需要能够自我保护——限流。限流策略有很多,最简单的比如当单位时间内请求数过多时,丢弃多余的请求。另外,也可以考虑分区限流。仅拒绝来自产生大量请求的服务的请求。例如商品服务和订单服务都需要访问促销服务,商品服务由于代码问题发起了大量请求,促销服务则只限制来自商品服务的请求,来自订单服务的请求则正常响应。
7、测试
8、无服务器架构(Serverless)
Serverless的基础是云技术,它是云技术发展到一定阶段而出现的一种革命性的高端架构。Serverless并不是说不需要服务器,而是指不需要开发者去关心底层服务器的状态、资源和扩容等,开发者只需要关注于业务逻辑实现。架构上,我们可以把serverless分为FaaS和BaaS:
- FaaS(函数即服务):用于创建、运行、管理函数服务的计算平台,它支持多种开发语言,比如java、nodejs、dart等,这有利于不同端测的开发同学介入开发。FaaS是基于事件驱动的思想,只有当一个函数被事件触发时才会占用服务器资源执行,不然都是无需占用服务器资源的
- BaaS(后端即服务):提供了用于函数调用的第三方基础服务,比如身份校验、日志、数据库等,它是有服务商直接提供,开发者无需关系实现,直接调用即可
1)优点
- 降低创业公司启动成本
- 减少运营成本
- 降低开发成本
- 实现快速上线
- 系统安全性更高
- 能适应微服务架构和扩展性能力强
2)缺点
- 不适合长时间运行应用
- 完全会依赖于第三方服务
- 缺乏调式和开发工具,排查问题困难
- 无法用于高并发运用
3)应用场景
- 发送通知
- WebHook
- 数据统计分析
- Trigger及定时任务
- Chat 机器人
十七、服务网格(Service Mesh)
1、三种服务发现模式
服务发现和负载均衡并不是新问题,业界其实已经探索和总结出一些常用的模式,这些模式的核心其实是代理 (Proxy,如下图所示),以及代理在架构中所处的位置。
在服务消费方和服务提供方之间增加一层代理,由代理负责服务发现和负载均衡功能,消费方通过代理间接访问目标服务。根据代理在架构上所处的位置不同,当前业界主要有三种不同的服务发现模式。
1)模式一:传统集中式代理
这是最简单和传统做法,在服务消费者和生产者之间,代理作为独立一层集中部署,由独立团队 (一般是运维或框架) 负责治理和运维。常用的集中式代理有硬件负载均衡器 (如 F5),或者软件负载均衡器 (如 Nginx),F5(4 层负载)+Nginx(7 层负载) 这种软硬结合两层代理也是业内常见做法,兼顾配置的灵活性 (Nginx 比 F5 易于配置)。
这种方式通常在 DNS 域名服务器的配合下实现服务发现,服务注册 (建立服务域名和 IP 地址之间的映射关系) 一般由运维人员在代理上手工配置,服务消费方仅依赖服务域名,这个域名指向代理,由代理解析目标地址并做负载均衡和调用。
国外知名电商网站 eBay,虽然体量巨大,但其内部的服务发现机制仍然是基于这种传统的集中代理模式,国内公司如携程,也是采用这种模式。
2)模式二:客户端嵌入式代理
这是很多互联网公司比较流行的一种做法,代理 (包括服务发现和负载均衡逻辑) 以客户库的形式嵌入在应用程序中。这种模式一般需要独立的服务注册中心组件配合,服务启动时自动注册到注册中心并定期报心跳,客户端代理则发现服务并做负载均衡。
Netflix 开源的 Eureka(注册中心)[附录 1] 和 Ribbon(客户端代理)[附录 2] 是这种模式的典型案例,国内阿里开源的 Dubbo 也是采用这种模式。
3)模式三:主机独立进程代理
这种做法是上面两种模式的一个折中,代理既不是独立集中部署,也不嵌入在客户应用程序中,而是作为独立进程部署在每一个主机上,一个主机上的多个消费者应用可以共用这个代理,实现服务发现和负载均衡,如下图所示。这个模式一般也需要独立的服务注册中心组件配合,作用同模式二。
2、服务网格Service Mesh
所谓的 Service Mesh,其实本质上就是上面提到的模式三:主机独立进程模式。
1)Service Mesh特点
- 是一个基础设施
- 轻量级网络代理,应用程序间通讯的中间层
- 应用程序无感知,对应用程序透明无侵入
- 解耦应用程序的重试/超时、监控、追踪和服务发现等控制层面的东西
2)Service Mesh开源实现
- 第一代Service MeshLinkerd:使用Scala编写,是业界第一个开源的Service Mesh方案Envoy:基于C++ 11编写,无论是理论上还是实际上,Envoy 性能都比 Linkderd 更好
- 第二代Service Mesh:主要改进集中在更加强大的控制面功能(与之对应的 sidecar proxy 被称之为数据面)Istio:是 Google 和 IBM 两位巨人联合 Lyft 的合作开源项目。是当前最主流的Service Mesh方案Conduit:各方面的设计理念与 Istio 非常类似。但是作者抛弃了 Linkerd, 使用Rust重新编写了sidecar, 叫做 Conduit Data Plane, 控制面则由Go编写的 Conduit Control Plane接管