微服务架构的优势与不足

  • 单一庞大(Monolithic)应用不足

    • 逐渐变大, 几年后变成巨大怪物, 各个业务模块犬牙交错,重复代码随处可见,补丁代码越打越多
    • 开发团队痛苦, 敏捷开发和部署举步维艰, 任何单一开发都不可能搞懂, 团队士气走下坡
    • 降低开发速度, 启动时间加长
    • 复杂而巨大的单体式应用也不利于持续性开发, 任何一个改动都需要一次全量发布,哪怕是修改一句文案
    • 单体式应用在不同模块发生资源冲突时,扩展将会非常困难 (比如数据都只用一种, 版本也只能一致)
    • 可靠性降低, bug(如内存泄露)将相互影响
    • 单体式应用使得采用新架构和语言非常困难 (比如一个组件, 在一个小应用升级替换非常容易, 但是在大应用上就很难, 要避免影响所有现有功能变得非常困难)

    一开始你有一个很成功的关键业务应用,后来就变成了一个巨大的,无法理解的怪物。因为采用过时的,效率低的技术,使得雇佣有潜力的开发者很困难。应用无法扩展,可靠性很低,最终,敏捷性开发和部署变的无法完成

  • 微服务在3d模型中的定位

    • Y轴代表将应用分解为微服务 (不同服务拆开)
    • X轴代表运行多个隐藏在负载均衡器之后的实例 (负载均衡+集群)
    • Z轴将服务分区 (相同服务分区, 拆小)
  • 不像传统多个服务共享一个数据库,微服务架构每个服务都有自己的数据库 (好难)

  • 微服务的好处

    • 单个服务很容易开发、理解和维护
    • 每个服务都可以有专门开发团队来开发。开发者可以自由选择开发技术,提供API服务; 重写重构,技术迭代变得不是很困难
    • 易于独立部署, AB测试,持续化部署
    • 每个服务独立扩展
  • 微服务架构的不足

    • there are no silver bullets
    • 微服务应用是分布式系统,由此会带来固有的复杂性
    • 因为数据分区, 也因为CAP理论, 微服务不得不使用最终一致性方案, 因此对开发要求更高
    • 单体应用容易测试, 微服务测试复杂度提高, 需要各个微服务的stubs
    • 改动的影响将波及很多应用, 不过这是面向服务架构的特点
    • 服务拆分后, 服务增多, 部署难道和工作量增加

API Gateway

微服务调用方案一: 客户端到多个微服务直接通信

不足:

  • 每个微服务暴露的细粒度API数量的不匹配, 客户端(移动端或者PC浏览器)需要发起多个(甚至数百个)服务请求, 在公网上效率非常低, 客户端代码非常复杂
  • 微服务的协议可能并不是web友好型。一个服务可能是用Thrift的RPC协议,而另一个服务可能是用AMQP消息协议。它们都不是浏览或防火墙友好的,并且最好是内部使用。应用应该在防火墙外采用类似HTTP或者WEBSocket协议
  • 很难重构微服务: 随着时间推移, 服务可能合并或者拆分, 如果客户端直接与微服务交互,那么这种重构就很难实施.

微服务调用方案二: API Gateway

API Gateway 的职责:

  • 封装内部系统架构, 提供API给各个客户端

    API Gateway可以提供给客户端一个定制化的API, API Gateway通过调用多个服务来处理这一个请求并返回结果

    涉及: 请求转发, 合成和协议转换(在web协议与内部使用的非Web友好型协议间进行转换)

  • 授权, 监控, 负载均衡, 缓存, 请求分片, 管理, 静态响应处理

优点:

  • 封装应用内部结构, 减少了客户端与服务器端的通信次数,也简化了客户端代码

缺点:

  • 必须要高可用, 必须开发,部署和管理 (额外成本)

  • 可能成为开发的一个瓶颈

  • 开发者必须更新API Gateway来提供新服务提供点来支持新暴露的微服务 (类似第一条)

实现(需要考虑的事情):

  • 性能和可扩展性

  • 采用反应性编程模型: Gateway 调用后端服务尽量并行, 事件驱动, 比如JavaScript的Promise

    利用传统的同步回调方法来实现API合并的代码会使得你进入回调函数的噩梦中

  • API Gateway 需要支持多种通信方式

    微服务是分布式系统, 微服务之间的通信方式有两种:

    • 异步: 基于消息, 如AMQP(Advanced Message Queuing Protocol)
    • 同步: HTTP, Thrift等
  • 服务发现:

    服务数量众多, 服务的实例也会动态的改变, API Gateway需要采用系统的服务发现机制, 两种方案:

    • 服务端发现
    • 客户端发现
  • 处理部分失败

    分布式系统中服务超时或者不可用时, API Gateway不应该被阻断并处于无限期等待下游服务的状态。但是,如何处理这种失败依赖于特定的场景和具体服务

    API Gateway来确保系统错误不影响到用户体验

    方案: 服务降级, 返回信息降级, 利用缓存旧数据


进程间通信

IPC交互模式:

一对一

  • 同步:

    • 请求/响应: REST(HTTP), Thrift
  • 异步:

    • 通知: 使用消息系统, channel只有一个消费者, 以实现点对点的通知
    • 请求/异步响应: TODO

一对多

  • 异步:

    • 发布/订阅: 用消息系统, channel有多个消费者
    • 发布/异步响应 TODO 如何实现

微服务架构有两类IPC机制可选,异步消息机制和同步请求/响应机制

API 设计

  • 小的api改动, 设计客户端和服务端时候应该遵循健壮性原理, 使得客户端能和新旧版本兼容
  • 大规模改动, 不可能强制让所有客户端立即升级, 可以考虑把版本号嵌入URL, 每个服务可以处理多个版本的API, 或者不同服务实例处理不同版本

  • 处理失败

    分布式系统中部分失败是普遍存在的问题, 某些服务过载或者反应很慢也是要考虑的问题

    需要考虑的应对措施:

    • 网络超时
    • 限制请求次数
    • Circuit Breaker Pattern: 记录成功和失败请求的数量。如果失效率超过一个阈值,触发断路器使得后续的请求立刻失败。如果大量的请求失败,就可能是这个服务不可用,再发请求也无意义。在一个失效期后,客户端可以再试,如果成功,关闭此断路器
    • 提供回滚: 请求失败后的回滚逻辑, 比如使用备份缓存数据

服务发现

  • Why:

    服务实例网络位置动态分配, 服务伸缩, 失效, 升级造成服务实例动态变化

  • 客户端发现模式

    服务实例启动时注册到服务注册表中, 在服务终止时删除, 通过心跳来定期刷新

    优点: 客户端可以进行负载均衡

    缺点: 客户端需要为每种语言开发不同的服务发现逻辑

  • 服务端发现模式

    优点: 客户端无需关注发现的细节, 客户端只需要简单的向负载均衡器发送请求,实际上减少了编程语言框架需要完成的发现逻辑

    缺点: 负载均衡器需要高可用

  • 服务注册表

    服务注册表由若干使用复制协议保持同步的服务器构成


参考资料

2016-12-20 补充

  • 考虑把大型项目拆解成微服务吗? 收益/代价/DevOps

    我非常确信分解一个大型项目到微服务的过程是值得的。然而,这是一个长期的投资,可能需要一段时间才能收到回报