在单体架构中,不同模块之间可以通过项目引用的方式直接使用接口调用,单次请求在同一台机器的同一个进程内进行,这种调用方式称为本地调用,但是在微服务架构中,每个服务都是独立的进程,并且通常部署在不同的服务器,无法简单的使用本地调用的方式,而是需要远程服务调用来实现服务间的通信。
通信模式的划分
目前有很多种进程间通信的技术供开发者选择,可以使用基于同步请求/响应的通信机制,例如:http Restful和 grpc。也可以使用异步的基于消息的通信机制,例如,AMQT,MQTT等。从服务端和客户端的交互方式上,可以将通信模式按两种维度划分。第一个维度关注的是一对一和一对多:
- 一对一:每个客户端请求由一个服务端处理,例如:同步请求/响应模式,异步请求/响应模式,单向通知模式。
- 一对多:每个客户端请求由多个服务端处理,例如:消息发布/订阅模式
第二个维度关注的同步和异步:
- 同步模式:客户端请求需要服务端实时响应,客户端等待响应时可能导致阻塞。
- 异步模式:客户端请求不会阻塞进程,服务端响应可以非实时的。
同步请求/响应模式
在实际的项目中,同步请求/响应模式是被应用最频繁的模式,底层的通信机制可以使用http协议或grpc。
由于http协议的简单,灵活,可靠,易于扩展等特性,被广泛应用于前/后端和服务间的通信,服务端通过http协议对外暴露API,RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。REST中的一个关键概念是资源,它通常表示一个业务对象,REST使用http动词来操作资源,使用URL引用这些资源。关于Resulful风格API的详细内容可以参考我转载的阮一峰老师的文章:Restful API设计规范
由于http动词的限制,Restful API并不能总是满足实际的业务需求,在需要支持多种更新操作的场景时,Rest无法通过有限的动词实现,避免此问题的进程间通信技术是使用RPC框架,对于RPC框架一个通俗的描述是:客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法。gRPC就是众多RPC框架中的一种,它是一个高性能、开源和通用的 RPC 框架,基于ProtoBuf(Protocol Buffers) 序列化协议开发,且支持众多开发语言。面向服务端和移动端,基于 HTTP/2 设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
消息发布/订阅模式
想象一个会员注册的场景,会员注册成功后,平台需要给会员发送注册成功的短信,增加注册积分,推送会员指引消息等等。这些对实时性要求不高,并且一对多的场景,就可以采用消息发布/订阅的模式来实现服务间的解耦,同时提升服务的响应速度。消息队列是实现消息发布/订阅的模式的通用解决方案。
从本质上说消息队列就是一个队列结构的中间件,通过它可以实现服务间的解耦、流量的削峰填谷、系统整体吞吐量的提升。
当会员注册成功后,应用不直接调用发送短信、发送积分,推送指引消息的接口,而是向消息队列中推送一条会员注册成功的消息(其中包含会员Id,会员名字,注册时间等重要信息),然后直接返回,由于不需要等待其他几个服务的执行结果,大大减少了会员注册接口的响应时间。同时下游的短信、积分、消息服务向消息队列订阅会员注册成功的消息,当收到新的消息时,再根据消息内容执行具体的业务的逻辑,这样就实现了服务间的解耦。后面会员注册成功需要增加新的需求时,只需要增加一个新的服务订阅该消息,实现具体的业务逻辑即可,而不需要修改会员注册的代码,这样就大大的提升了系统的封闭性和扩展性。
如何保证数据一致性
在分布式环境中,由于网络错误,服务不可用等原因,网络请求无法做到百分百的有效,在使用消息发布/订阅模式时,为了保证系统间数据一致性,需要注意以下几点:
- 生产端:生产端在发布消息时,需采用消息确认模式,将消息推送至消息队列,并保证消息队列将消息持久化至磁盘后,返回成功,否则需要做数据补偿操作,重试或者将消息至为失败状态等待人工处理。
- 消息队列端:消息队列需要提供主题及消息的持久化存储机制,保证队列重启后不会有消息丢失,同时也可以通过集群模式的数据副本冗余保证消息的一致性。
- 消费端:消费端采用ACK机制,在成功执行完消息的处理逻辑后,向消息队列返回消费成功的ACK,消息队列在收到消息的ACK后删除消息,否则会将消息保留,在指定时间将消息重新推送给消费端处理。由于网络的不确定性,可能存在消息队列将处理成功的消息重复推送到消费端的情况,所以要求消费端的逻辑必须保证幂等性,即针对同一条消息,无论执行多少次,结果是一致的。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/287793.html