客户端和服务端交互方式可以从两个维度来分:
进程通信的本质是交互消息,消息通常包含数据。所以一个重要的设计决策就是要设计消息的格式。
消息的格式可以分为两大类:文本和二进制。
基于文本的消息格式有JSON和XML。
这种格式的消息好处在于它的可读性很高,同时也是自描述(啥意思?就是字段名可以说明它自己是什么意思吧)。JSON是命名属性的集合,XML是命名元素和值得集合。这样的消息格式可以消息接受方只挑选出他们感兴趣的值,对消息结构的修改可以做到很好的后向兼容性。
使用基于文本格式消息的弊端主要是消息往往过度冗长,特别是 XML。消息的每一次传递都必须反复包含除了值以外的属性名称,这样会造成额外的开销。(确实是这样,用过的都知道0.0)。另外一个弊端是解析文本引入的额外开销,尤其是在消息较大的时候。因此,在对效率和性能敏感的场景下,你可能需要考虑基于二进制格式的消息。
二进制格式消息作者也写了两段,主要是平时从没用过这种格式,这里就不写了~
当客户端向服务端发送同步请求时,永远会面临服务端局部故障的风险。故障的原因可能是服务器挂了、正在维护或者因为负载过多响应缓慢。这时候服务端无法在有限的时间内给客户端回应,客户端将会阻塞,同时又接受新的请求导致资源耗尽,无法处理请求。例如下图调研链路中,移动应用调用Create order端的,再掉OrderServece代理,再掉用OrderService,如果OrderService挂了,OrderService代理会一直等下去,最终API GateWay资源耗尽,整个API GateWay将不可用。
所以我们需要合理的设计服务来防止故障在整个应用程序中的传导和扩散。解决这个问题分为两个部分
当一个服务同步调用另一个服务时,它应该使用Netflix 描述的方法(http://techblog.netflixcom/2012/02/faulttolerance-in-high-volumehtml)来保护自己。(啊 网飞还干这个?不是同一个吧 哈哈!)这种方法包括以下机制的组合。
未完待续
客户端向服务端发送请求的时候,需要知道服务端的网络地址,就是IP+端口。在以前,传统的应用程序是部署在物理机上,网络地址通常是静态的。所以客户端可以从配置文件中读取网络地址。但现在是基于云的微服务应用程序,其更具有动态性。确实,现在部署很多用的docker了。服务实例具有动态分配的网络位置。此外,由于自动扩展、故障和升级,服务实例集会动态更改。因此,客户端代码必须使用服务发现。
正如刚才所见,你无法使用服务的IP地址静态配置客户端。相反,应用程序必须使用动态发现机制。服务发现的关键组件是服务注册表,它是包含服务实例网络位置信息的一个数据库。
服务实例启动和停止时,服务发现机制会更新服务注册表。当客户端调用服务时,服务发现机制会查询服务注册表以获取可用服务实例的列表,并将请求路由到其中一个服务实例。
实现服务发现有以下两种主要方式:
这种服务发现方法时两种模式的组合:自注册模式和客户发现模式。
自注册模式就是服务实例向服务注册表注册自己。
客户端发现模式就是客户端从服务列表检索可用服务实例列表。并在他们之间进行负载均衡。
使用应用端服务发现模式,还需要客户端和服务端写一些发现和注册的代码,是有一些耦合的。而使用平台发现模式就简单了。
现在许多现代部署平台(如 Docker 和 Kubernetes)都具有内置的服务注册表和服务发现机制。部署平台为每个服务提供DNS 名称、虚IP (VIP)地址和解析为VIP 地址的DNS 名称。客户端向 DNS 名称和VIP 发出请求,部署平台自动将请求路由到其中一个可用服务实例。因此,服务注册、服务发现和请求路由完全由部署平台处理。如下:
这种发现使用了2种模式:
第三方注册:服务实例有第三方观察注册到服务注册表。不用服务端自己注册了。
服务端发现:客户端向路由器发出请求,路由器负责服务发现。
由平台提供服务发现机制的主要好处是服务发现的所有方面都完全由部署平台处理。服务和客户端都不包含任何服务发现代码。因此,无论使用哪种语言或框架,服务发现机制都可供所有服务和客户使用。
使用消息机制时,服务之间的通信是采用异步消息的方式完成。有的是使用消息代理,有的不使用消息代理。由于通信是异步的,因此客户端不会堵塞和等待回复,相反客户端都假定消息不会马上就收到。
消息是由消息头部和消息主体组成。
消息有几种不同类型的消息。
客户端将消息(通常是命令式消息)发送到服务所拥有的点对点通道。服务订阅该通道并处理该消息,但服务不会发回回复。
弊端
消息代理是所有消息的中介节点。发送方将消息写入消息代理,消息代理将消息发送给接收方。使用消息代理的一个重要好处是发送方不需要知道接收方的网络位置,另一个好处是消息代理缓冲消息,直到接收方能够处理它们。
流行的开源消息代理包括Apache ActiveMQ、RabbitMQ、Apache Kafka 。(第一个不了解,其他两个用过)
还有基于云的消息服务,例如AWS Kinesis(https://awsamazon.com/kinesis)和AWSsos(https://aws.amazon.com/sqs/)。
选择消息代理时,你需要考虑以下各种因素:
好处
弊端
消息接收方通常是多个实例处理消息,即便单个服务实例也可能用多个线程同时处理消息。所以带来的挑战就是如何保证每个消息只被处理消息,且按发来顺序处理。如消息发送方发来3个消息,创建订单、更新订单、取消订单,消费方要按顺序处理,且每个消息处理一次。
现代消息代理常用的解决方案是使用分片(分区)通道。
理想情况下,消息应该保证有且仅有一次传递。(就是kafka中提到的“精准一次”),但是这种传递方式成本非常高,大多数消息代理承诺至少成功传递一次。这时候就可能接收方有可能会收到重复消息,有两种方式可以处理它
工作中感觉这种用的最多,如创建订单,一般先查再插 + 订单号作为唯一索引,利用数据库保证幂等。
未完待续。。。