1. 进程间通信(IPC)
在单体式应用中,各个模块之间的调用是通过编程语言级别的方法或者函数来实现的。但是一个基于微服务的分布式应用是运行在多台机器上的。
一般来说,每个服务实例都是一个进程。因此,如下图所示,服务之间的交互必须通过进程间通信(IPC)来实现。
2. 客户端与服务端的交互模式
交互模式可以从两个维度进行归类。(1)第一个维度是一对一还是一对多:
- 一对一:每个客户端请求有一个服务实例来响应。
- 一对多:每个客户端请求有多个服务实例来响应。
- 同步模式:客户端请求需要服务端即时响应,甚至可能由于等待而阻塞。
- 异步模式:客户端请求不会阻塞进程,服务端的响应可以是非即时的。
- 请求/响应:一个客户端向服务器端发起请求,等待响应。客户端期望此响应即时到达。在一个基于线程的应用中,等待过程可能造成线程阻塞。
- 通知(也就是常说的单向请求):一个客户端请求发送到服务端,但是并不期望服务端响应。
- 请求/异步响应:客户端发送请求到服务端,服务端异步响应请求。客户端不会阻塞,而且被设计成默认响应不会立刻到达。
- 发布/订阅模式:客户端发布通知消息,被零个或者多个感兴趣的服务消费。
- 发布/异步响应模式:客户端发布请求消息,然后等待从感兴趣服务发回的响应。
每个服务都是以上这些模式的组合,对某些服务,一个IPC机制就足够了;而对另外一些服务则需要多种IPC机制组合。下图展示了在一个打车服务请求中服务之间是如何通信的。
3. 客户端与服务端的接口API
不管选择了什么样的IPC机制,重要的是使用某种交互式定义语言(IDL)来精确定义一个服务的接口API。
接口API的定义实质上依赖于选择哪种IPC。如果使用消息机制,API则由消息频道(channel)和消息类型构成;如果选择使用HTTP机制,API则由URL和请求、响应格式构成。
API的变化是不可避免的,微小的改变可以和之前版本兼容。比如,你可能只是为某个请求和响应添加了一个属性。这时,客户端使用旧版API应该也能和新版本一起工作。但是有时候,API需要进行大规模的改动,并且可能与之前版本不兼容。因为你不可能强制让所有的客户端立即升级,所以支持老版本客户端的服务还需要再运行一段时间。如果你正在使用基于基于HTTP机制的IPC,例如REST,一种解决方案是把版本号嵌入到URL中。每个服务都可能同时处理多个版本的API。或者,你可以部署多个实例,每个实例负责处理一个版本的请求。
4. 处理部分失败
分布式系统中部分失败是普遍存在的问题。因为客户端和服务端是都是独立的进程,一个服务端有可能因为故障或者维护而停止服务,或者此服务因为过载而停止或者反应很慢。
假设推荐服务无法响应请求,那客户端就会由于等待响应而阻塞,这不仅会给客户带来很差的体验,而且在很多应用中还会占用很多资源,比如线程,以至于到最后由于等待响应被阻塞的客户端越来越多,线程资源被耗费完了。如下图所示:
- 网络超时:当等待响应时,不要无限期的阻塞,而是采用超时策略。使用超时策略可以确保资源不会无限期的占用。
- 限制请求的次数:可以为客户端对某特定服务的请求设置一个访问上限。如果请求已达上限,就要立刻终止请求服务。
- 断路器模式(Circuit Breaker Pattern):记录成功和失败请求的数量。如果失效率超过一个阈值,触发断路器使得后续的请求立刻失败。如果大量的请求失败,就可能是这个服务不可用,再发请求也无意义。在一个失效期后,客户端可以再试,如果成功,关闭此断路器。
- 提供回滚:当一个请求失败后可以进行回滚逻辑。例如,返回缓存数据或者一个系统默认值。
服务之间的通信采用同步的请求/响应模式,可以选择基于HTTP的REST或者Thrift。
服务之间的通信采用异步的、基于消息的通信模式,可以选择AMQP或者STOMP。大量开源消息中间件可供选择,比如RabbitMQ、Apache Kafka、Apache ActiveMQ和NSQ。
消息格式可以选择基于文本的,比如 JSON和XML;二进制格式(效率更高)的,比如Avro和Protocol Buffer。
5.1 采用异步的、基于消息的通信模式的例子,下图展示了打车软件如何使用发布/订阅:
5.2 采用同步的、基于请求/响应的通信模式的例子:下图展示了打车软件如何使用REST:
使用基于HTTP的协议的好处:
- HTTP非常简单并且大家都很熟悉。
- 可以使用浏览器扩展(比如Postman)或者curl之类的命令行来测试API。
- 内置支持请求/响应模式的通信。
- HTTP对防火墙友好。
- 不需要中间代理,简化了系统架构。
使用基于HTTP的协议的不足之处:
- 只支持请求/响应模式交互。可以使用HTTP通知,但是服务端必须一直发送HTTP响应才行。
- 因为客户端和服务端直接通信(没有代理或者buffer机制),在交互期间必须都在线。
- 客户端必须知道每个服务实例的URL。客户端必须使用服务实例发现机制。
1. http://dockone.io/article/549
2. http://www.ibm.com/developerworks/cn/java/j-lo-SpringHATEOAS/
没有评论:
发表评论