微服务架构最佳实践

  • 注:文章来源:极客时间的专栏《从0开始学架构》

方法篇

服务粒度

  • 三个火枪手原则,即一个微服务三我的负责开发
  • 从系统规模来说,3我的负责开发一个系统,系统的复杂度恰好达到每一个人都能全面理解整个系统,又可以进行分工的粒度;2我的,系统的复杂度不够,开发人员可能以为没法体现本身的技术实力;4个及以上,系统复杂度又没法让开发人员对系统的细节都了解很深
  • 从团队管理来讲,3我的能够造成一个稳定的备份,即便一我的休假或者调配到其余系统,剩余2我的还能够支撑;2我的压力太大;一我的就是单点啦

拆分方法

  • 基于“三个火枪手”的理论,能够计算出拆分后合适的服务数量
1. 基于业务逻辑拆分
  • 将系统中的业务模块按照职责范围识别出来,每一个单独的业务模块拆分为一个独立的服务
  • 难点问题在于,对“职责范围”的理解差别很大。例如,一个电商系统,第一种方式是将服务划分为“商品”“交易”“用户”3个服务,第二种方式是划分为“商品”“订单”“支付”“发货”“卖家”“买家”6个服务,哪一种方式更合理?
  • 困惑在于从业务的角度来拆分,规模粗和细都没有问题,由于拆分基础都是业务逻辑,要判断拆分粒度,不能从业务逻辑角度,根据“三个火枪手”原则,计算一下大概的服务范围
  • 例如,有10我的,按以上原则,大约须要划分4个服务,那么“登陆、注册、用户信息管理”均可以划到“用户服务”职责范围内;若是团队规模是100人支撑服务,服务数量能够达到40个,那么“用户登陆”就是一个服务了;若是团队规模达到1000人支撑业务,那“用户链接管理”可能就是一个独立的服务了
2. 基于可扩展拆分
  • 将系统中的业务模块按照稳定性排序,将已经成熟和改动不大的服务拆分为稳定服务,将常常变化和迭代的服务拆分为变更服务
  • 稳定的服务粒度能够粗一些,即便逻辑上没有强关联的服务,也能够放在同一个子系统中,例如将“日志服务”和“升级服务”放在同一个子系统中;不稳定的服务粒度能够细一些,但不要太细,始终记住要控制服务的总数量
  • 这样的拆分主要是为了提高项目快速迭代的效率,避免在开发的时候,不当心影响了已有的成熟功能致使线上问题
3. 基于可靠性拆分
  • 将系统中的业务模块按照优先级排序,将可靠性要求高的核心服务和要求低的非核心服务拆分开来,而后重点保证核心服务的高可用。
好处
  • 避免非核心服务故障影响核心服务

  例如,日志上报通常都属于非核心服务,可是在某些场景下可能有大量的日志上报,若是系统没有拆分,那么日志上报可能致使核心服务故障;拆分后即便日志上报有问题,也不会影响核心服务算法

  • 核心服务高可用方案能够更加单

  核心服务的功能逻辑更加简单,存储的数据可能更少,用到的组件也会更少,设计高可用方案部分状况下要比不拆分简单不少数据库

  • 可以下降高可用成本

  将核心服务拆分出来后,核心服务占用的机器、带宽等资源比不拆分要少不少。所以,只针对核心服务作高可用方案,机器、带宽等成本比不拆分要节省较多缓存

4. 基于性能拆分
  • 将性能要求高或者性能压力大的模块拆分出来,避免性能压力大的服务影响其余服务
  • 常见的拆分方式和具体的性能瓶颈有关,能够拆分Web服务、数据库、缓存等
  • 例如,电商的抢购,性能压力最大的是入口的排队功能,能够将排队功能独立为一个服务
以上拆分,能够根据实际状况自由排列组合

基础设施

  • “automated”是重要一环,若是其相关的基础设施不健全,那微服务就是焦油坑,让研发,测试,运维陷入各类陷阱中
  • 微服务基础设施以下图所示

实施微服务
  • 有开源的微服务基础设施全家桶,例如,Spring Cloud项目,涵盖了服务发现、服务路由、网关、配置中心等功能
  • 若是微服务的数量并非不少的话,并非每一个基础设施都是必须的
按优先级来搭建基础设施
    1. 服务发现、服务路由、服务容错:这是最基本的微服务基础设施
    1. 接口框架、API网关:主要是为了提高开发效率,接口框架是提高内部服务的开发效率,API网关是为了提高与外部服务对接的效率
    1. 自动化部署、自动化测试、配置中心:主要是为了提高测试和运维效率
    1. 服务监控、服务跟踪、服务安全:主要是为了进一步提高运维效率
  • 以上3和4两类基础设施,其重要性会随着微服务节点数量增长而愈来愈重要,但在微服务节点数量较少的时候,能够经过人工的方式支撑,虽然效率不高,但也基本可以顶住

基础设施

自动化测试

  • 微服务将本来大一统的系统拆分为多个独立运行的“微”服务,微服务之间的接口数量大大增长,而且微服务提倡快速交付,版本周期短,版本更新频繁
  • 若是每次更新都靠人工回归整个系统,则工做量大,效率低下,达不到“快速交付”的目的,所以必须经过自动化测试系统来完成绝大部分测试回归的工做中
  • 自动化测试涵盖的范围包括代码级的单元测试、单个系统级的集成测试、系统间的接口测试,理想状况是每类测试都是自动化
  • 由于团队规模和人力的缘由没法全面覆盖,至少要作到接口测试自动化

自动化部署

  • 相比大一统的系统,微服务须要部署的节点增长了几倍甚至十几倍,微服务部署的频率也会大幅提高(例如,咱们的业务系统70%的工做日都部署操做),综合计算下来,微服务部署的次数是大一统系统部署次数的几十倍
  • 这么大的部署操做,若是继续采用人工手工处理,须要投入大量的人力,且容易出错,所以须要自动化部署的系统来完成部署操做
  • 自动化部署系统包括版本管理、资源管理(例如,机器管理、虚拟机管理)、部署操做、回退操做等功能

配置中心

  • 微服务的节点数量很是多,经过人工登陆每台机器手工修改,效率低,容易出错
  • 特别是部署或者排障时,须要快速增删改查配置,人工操做的方式显然是不行的
  • 有的运行期配置须要动态修改而且全部节点即时生效,人工操做是没法作到的
  • 综上,微服务须要一个统一的配置中心来管理全部微服务节点的配置
  • 配置中心包括配置版本管理(例如,一样的微服务,有10个节点是给移动用户服务的,有20个节点给联通用户服务的,配置项都同样,配置值不同)、增删改查配置、节点配置、配置同步、配置推送等功能

接口框架

  • 微服务提倡轻量级的通讯方式,通常采用HTTP/REST或者RPC方式统一接口协议
  • 但在实践过程当中,光统一接口协议还不够,还须要统一接口传递的数据格式
  • 例如,咱们须要指定接口协议为HTTP/REST,但这还不够,还须要指定HTTP/REST的数据格式采用JSON,而且JSON的数据都遵循以下规范。

  • 若是咱们只是简单指定了HTTP/REST协议,而不指定JSON和JSON的数据规范,那么就会出现这样混乱的状况:有的微服务采用XML,有的采用JSON,有的采用键值对;即便一样都是JSON,JSON数据格式也不同。这样每一个微服务都要适配几套甚至几十套接口协议,至关于把曾经由ESB作的事情转交给微服务本身作了,这样作的效率显然是没法接受的,所以须要统一接口框架
  • 接口框架不是一个可运行的系统,通常以库或者包的形式提供给全部微服务调用。例如,针对上面的JSON样例,能够由某个基础技术团队提供多种不一样语言的解析包(Java包、Python包 、C库等)

API网关

  • 系统拆分为微服务后,内部的微服务之间是互联互通的,相互之间的访问都是点对点的
  • 若是外部系统想调用系统的某个功能,也采起点对点的方式,则外部系统会很是“头大”
  • 由于在外部系统看来,它不须要也没办法理解这么多微服务的职责分工和边界,它只会关注它须要的能力,而不会关注这个能力应该由哪一个微服务提供
  • 外部系统访问系统还涉及安全和权限相关的限制,若是外部系统直接访问某个微服务,则意味着每一个微服务都要本身实现安全和权限的功能,这样作不但工做量大,并且都是重复工做
  • 综合上面的分析,微服务须要一个统一的API网关,负责外部系统的访问操做
  • API网关是外部系统访问的接口,全部的外部系统接入系统都须要经过API网关,主要包括接入鉴权(是否容许接入)、权限控制(能够访问哪些功能)、传输加密、请求路由、流量控制等功能

服务发现

  • 微服务种类和数量不少,若是这些信息所有经过手工配置的方式写入各个微服务节点,首先配置工做量大,配置文件可能要配几百上千行,几十个节点加起来后配置项就是几万几十万行了,人工维护这么大数量的配置项是一项灾难
  • 其次是微服务节点常常变化,多是因为扩容致使节点增长,也多是故障处理时隔离掉一部分节点,还多是采用灰度升级,先将一部分节点升级到新版本,而后让新老版本同时运行
  • 无论哪一种状况,咱们都但愿节点的变化可以及时同步到全部其余依赖的微服务。若是采用手工配置,是不可能作到实时更改生效的
  • 所以,须要一套服务发现的系统来支撑微服务的自动注册和发现
服务发现主要有两种实现方式:自理式和代理式
1. 自理式
  • 自理式结构以下:

  • 自理式结构就是指每一个微服务本身完成服务发现。例如,图中SERVICE INSTANCE A访问SERVICE REGISTRY获取服务注册信息,而后直接访问SERVICE INSTANCE B
  • 自理式服务发现实现比较简单,由于这部分的功能通常经过统一的程序库或者程序包提供给各个微服务调用,而不会每一个微服务都本身来重复实现一遍;而且因为每一个微服务都承担了服务发现的功能,访问压力分散到了各个微服务节点,性能和可用性上不存在明显的压力和风险
2. 代理式
  • 代理式结构以下:

  • 代理式结构就是指微服务之间有一个负载均衡系统,由负载均衡系统来完成微服务之间的服务发现
  • 代理式的方式看起来更加清晰,微服务自己的实现也简单了不少,但实际上这个方案风险较大
  • 第一个风险是可用性风险,一旦LOAD BALANCER系统故障,就会影响全部微服务之间的调用
  • 第二个风险是性能风险,全部的微服务之间的调用流量都要通过LOAD BALANCER系统,性能压力会随着微服务数量和流量增长而不断增长,最后成为性能瓶颈
  • 所以LOAD BALANCER系统须要设计成集群的模式,但LOAD BALANCER集群的实现自己又增长了复杂性
  • 无论是自理式仍是代理式,服务发现的核心功能就是服务注册表,注册表记录了全部的服务节点的配置和状态,每一个微服务启动后都须要将本身的信息注册到服务注册表,而后由微服务或者LOAD BALANCER系统到服务注册表查询可用服务

服务路由

  • 有了服务发现以后,微服务之间可以方便地获取相关配置信息,但具体进行某次调用请求时,咱们还须要从全部符合条件的可用微服务节点中挑选出一个具体的节点发起请求,这就是服务路由须要完成的功能
  • 服务路由和服务发现紧密相关,服务路由通常不会设计成一个独立运行的系统,一般状况下是和服务发现放在一块儿实现的
  • 对于自理式服务发现,服务路由是微服务内部实现的;对于代理式服务发现,服务路由是由LOAD BALANCER系统实现的
  • 不管放在哪里实现,服务路由核心的功能就是路由算法。常见的路由算法有:随机路由、轮询路由、最小压力路由、最小链接数路由等

服务容错

  • 系统拆分为微服务后,单个微服务故障的几率变小,故障影响的范围也减小,可是微服务的节点数量大大增长
  • 从总体上来看,系统中某个微服务出故障的几率会大大增长
  • 微服务具备故障扩散的特色,若是不及时处理故障,故障扩散开来就会致使看起来系统中不少服务节点都故障了, 所以须要微服务可以自动应对这种出错场景,及时进行处理。不然,若是节点一故障就须要人工处理,投入人力大,处理速度慢;而一旦处理速度慢,则故障就很快扩散,因此咱们须要服务容错的能力
  • 常见的服务容错包括请求重试、流控和服务隔离
  • 一般状况下,服务容错会集成在服务发现和服务路由系统中

服务监控

  • 系统拆分微服务后,节点数量大大增长,致使须要监控的机器、网络、进程、接口调用数等控制对象的数量大大增长;同时,一旦发生故障,咱们须要快速根据各种信息来定位故障。靠人力去完成是不现实的
  • 例如,咱们收到用户投诉说业务有问题,若是此时采用人工的方式去搜集、分析信息,可能把几十个节点的日志打开一遍就须要十几分钟了,所以须要服务监控系统来完成微服务节点的监控
做用
  • 实施搜集信息并进行分析,避免故障后再来分析,减小了处理时间
  • 服务监控能够在实时分析的基础上进行预警,在问题萌芽的阶段发觉并预警,下降了问题影响的范围和时间

  一般状况下,服务监控须要搜集分析大量的数据,所以建议作成独立的系统,而不要集成到服务发现、API网关等系统中安全

服务跟踪

  • 服务监控能够作到微服务节点级的监控和信息收集,但若是咱们须要跟踪某一个请求在微服务中的完整路径,服务监控是难以实现的。若是每一个服务的完整请求链信息都实时发送给服务监控系统中,数据量会大到没法处理
  • 服务监控和服务跟踪的区别能够简单归纳为宏观和微观的区别。例如,A服务经过HTTP协议请求B服务10次,B经过HTTP返回JSON对象,服务监控会记录请求次数、响应时间、响应错误码、请求参数、返回的JOSN对象等信息
  • 目前不管是分布式跟踪仍是微服务的服务跟踪,绝大部分请求跟踪的实现技术都基于Google的Dapper论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》

服务安全

  • 系统拆分为微服务后,数据分散在各个微服务节点上
  • 从系统链接的角度来讲,任意微服务均可以访问全部其余微服务节点
  • 从业务的角度来讲,部分敏感数据或者操做,只能部分微服务能够访问,而不是全部的微服务均可以访问,所以须要设计服务安全机制来保证业务和数据的安全性
  • 服务安全主要分为三部分:接入安全、数据安全、传输安全
  • 一般状况下,服务安全能够集成到配置中心系统中进行实现,即配置中心配置微服务的接入安全策略和数据安全策略,微服务节点从配置中心获取这些配置信息,而后在处理具体的微服务调用请求时根据安全策略进行处理。
  • 因为这些策略是通用的,通常会把策略封装通用的库提供给各个微服务调用。
  • 基本架构以下:

注:有兴趣了解极客时间专栏的同窗,能够查看极客时间专栏—可提供返现服务网络