作者: http://dockone.io/people/hokingyang

微服务实战(一):微服务架构的优势与不足

微服务的优势是解耦,对开发运维效率和系统运行效率都有极大的提升。
微服务相比单体式应用的问题本质上是多进程编程对比单进程编程*的问题。
解决单机多进程的经验,如IPC、消息队列和mmap等在微服务中都能得到实践。
每个服务单独使用一个database,这为数据库CAP带来挑战。
例如,一个用户完成一笔消费,同时影响购物车/推荐系统/评论系统等的数据.在单体式应用容易实现一致性,分布式微服务必须使用RPC或消息队列等通讯机制,开销增大。

一些体会笔记

从某个角度来看, 我们从代码段落函数再从面向过程面向对象为的就是解耦,为了不做重复的工作. 一切都是为了当工程量日益增大时, 多个程序员之间的高效协同开发.
举个例子, 当个人程序员开发的时候,遇到重复代码,可能会先想着把代码块打包成函数, 仅暴露参数与返回值即可. 此时随着函数的体量越来越大,我们可以打包成python中的模块(module)或者java中的包(package),然后在每个工程当中引入模块或包即可.
但此时,随着工程数目的增加,相当于每一份工程代码当中都有大量的重复模块(例如, 可能都在从一个数据库里取一些基本数据并进行格式化),运行时来考虑的话就是每个进程当中都有相当一部分时间在做同样的事情. 并且最大的问题在于模块需要修改的时候,将直接影响到每一个工程.
因此,微服务的要点就是实现一个服务对应一个进程, 以进程来作为模块的隔离边界得到充分的解耦. 当模块需要改动时, 只需要保证调用的接口不变(参数与返回值),直接在单个服务上改动就可以完成升级.
可以说, 微服务的其他特征, 如运维部署和分布式通信方面的特点, 都是由"一个服务对应一个进程"这个特点带来的.

微服务实战(二):使用API Gateway

客户端到微服务直接通信

问题1: 客户端的需求量与每个微服务暴露的细粒度API数量的不匹配.
每个服务使用一个URL,一个client的WEB UI或者APP需要发起数十个HTTP请求,网络开销大.

问题2: 后端微服务的重构都需要客户端进行改动,特别是将一个微服务拆分成多个时,前端改动大.

采用一个API Gateway

使用一个GATEWAY为客户端统一服务.

GATEWAY还可能有其他功能,如授权、监控、负载均衡、缓存、请求分片和管理、静态响应处理等.

实现: GATEWAY以单个页面的需求进行定制化,选择性地挑选页面需要的服务,对客户端封装调用服务的过程,API GATEWAY是一个粗粒度的REST API.

API Gateway的优点和缺点

难点: 保证API GATEWAY的高可用.API GATEWAY作为统一入口,需要负担高性能可拓展的责任.

  • 采用反应性编程模型
  • 服务调用: 线程间通信-同步与异步结合
  • 服务发现
  • 处理部分失败

微服务实战(三):深入微服务架构的进程间通信


对客户端隐藏微服务耦合结构,各服务间需要通信.

交互模式:

解决两个问题:

  1. 一对一还是一对多
  2. 同步还是异步

调用端是否关心实时响应:

是: 同步,例如,购物车获取商品信息.

否: 异步,例如,商品加入购物车,推荐系统获得商品信息,给出推荐结果.例如,发布打车请求后回到地图页面,服务端异步地把打车请求分发给附近的司机.

一对多的交互模式有以下几种方式:

• 发布/ 订阅模式:客户端发布通知消息,被零个或者多个感兴趣的服务消费。

• 发布/异步响应模式:客户端发布请求消息,然后等待从感兴趣服务发回的响应。

实例:打车系统的IPC

上图中的服务通信使用了通知、请求/响应、发布/订阅等方式。例如,乘客通过移动端给『行程管理服务』发送通知,希望申请一次出租服务。『行程管理服务』发送请求/响应消息给『乘客服务』以确认乘客账号是有效的。紧接着创建此次行程,并用发布/订阅交互模式通知其他服务,包括定位可用司机的调度服务。

API的演化

定义API需要考虑新旧版本同时运行.

实现1: 规定缺省响应值,客户端对于响应默认不处理.

实现2: URL加入版本号使不同版本API共存.

处理部分失败

API GATEWAY的失败为线程失败: 会影响整体进程.

解决方案:

  1. 网络超时
  2. 限制请求次数
  3. 断路器模式:记录成功和失败请求的数量。如果失效率超过一个阈值,触发断路器使得后续的请求立刻失败。如果大量的请求失败,就可能是这个服务不可用,再发请求也无意义。在一个失效期后,客户端可以再试,如果成功,关闭此断路器。
  4. 提供回滚:当一个请求失败后可以进行回滚逻辑。例如,返回缓存数据或者一个系统默认值。

IPC技术

异步: 消息队列

同步: RPC/REST/Thrift

微服务实战(四):服务发现的可行方案以及实践案例

问题描述:

  1. 传统应用: 服务部署在固定的机器上.在配置当中写好IP地址即可.
  2. 微服务: 动态部署, 服务地址不固定.需要更复杂的服务发型机制,动态定位.

客户端发现模式:

在客户端与服务之间加上一层服务注册层管理服务. 在客户端实现负载均衡,查看可用服务,决定请求地址.
服务实例的网络位置是在启动时注册到服务注册表中,并且在服务终止时从注册表中删除。服务实例注册信息一般是使用心跳机制来定期刷新的。

最大的缺点是需要针对不同的编程语言注册不同的服务,在客户端需要为每种语言开发不同的服务发现逻辑。(?未理解)

服务端发现模式:

客户端通过负载均衡器向某个服务提出请求,负载均衡器向服务注册表发出请求,将每个请求转发往可用的服务实例。跟客户端发现一样,服务实例在服务注册表中注册或者注销。

简而言之,将客户端实现的功能搬到负载均衡器实现.

  • 优点: 客户端解耦.
  • 缺点: 负载均衡器需保证高可用.

服务注册

服务实例注册和注销主要有两类方式。

  1. 服务实例自动注册到服务注册表中,也就是自注册模式
  2. 某个系统模块负责处理注册和注销,也就是第三方注册模式

DNS注册

使用DNS绑定<域名,IP地址>实现动态发现服务地址. NGINX Plus支持.

微服务实践(五):微服务的事件驱动数据管理

单体式应用使用RDBMS的优势:

  1. ACID transactions
  2. SQL的方便查询与引擎的自带优化

要点:

微服务框架中各服务维护各自database,事务操作的ACID如今没法保障.

整体思路:

在多个服务之间找到通讯方式,把事务操作抽象为具体的事件,如创建订单,发起付款和点赞等. 由事件发起,触发相关的服务发布另一个事件,并更新自身数据库.

详细解决方案:

  1. 消息队列:发布/订阅
  2. 交易日志挖掘:感知数据库的变化
  3. 事件源:进一步解耦,创建一个基本服务进行事件管理与发布.

微服务实战(六):选择微服务部署策略

总结:

和虚拟机相比,如Docker这类容器技术拥有很多优势.
容器技术离宿主操作系统更近,各个不同容器之间共享内核态操作系统,只在用户态操作系统有各自的沙盒,安全性会收到一定程度上的考验,但是一般使用都不会有这类问题.

微服务实践(七):从单体式架构迁移到微服务架构

整体策略:

不做大规模迁移,逐步分离直到单体式架构被完全替代或自身成为微服务一部分.

策略1——停止挖掘

新功能以微服务实现

开发内容:

  1. 路由分发模块,区分新功能与原有功能.
  2. 胶水语言,新功能微服务往往与旧的单体式构架存在严重耦合,数据互相依赖.

微服务有三种方式访问单体应用数据:

  1. 使用单体应用提供的远程API
  2. 直接访问单体应用数据库
  3. 自己维护一份从单体应用中同步的数据

策略2——业务分层

  • 表现层——处理HTTP请求,要么响应一个RESTAPI请求,要么是提供一个基于HTML的图形接口。对于一个复杂用户接口应用,表现层经常是代码重要的部分。
  • 业务逻辑层——完成业务逻辑的应用核心
  • 数据访问层——访问基础元素,例如数据库和消息代理.

策略3——抽出服务

如何选择被抽出模块进行服务化:

  1. 变化频率最高的;
  2. 资源消耗高,时间与空间资源.
  3. 根据模块的同步/异步,抽出异步模块对完成同步功能的逻辑影响小.

总结:

使原有单体式框架逐步架空, 随着抽离程度越高,服务化会越来越容易.