9.3 消息传递架构
这里我们要讨论的是一种基于消息传递的架构,我用它实现了一个简单的聊天客户端。聊天客户端的细节并不重要,重要的是这个模式,那就让我们来谈谈消息传递本身吧。
首先要注意的是我们的设计里不共享任何状态。verticle对象之间通过向事件总线发送消息通信,这就是说我们不需要保护任何共享状态,因此根本不需要在代码中添加锁或使用synchronized关键字,编写并发程序变得更加简单。
为了确保不在verticle对象之间共享状态,我们对事件总线上传递的消息做了某些限制。例子中使用的消息是普通的Java字符串,它们天生就是不可变的,因此可以安全地在verticle对象之间传递。 接收处理程序无法改变String对象的状态,因此不会和消息发送者互相干扰。
Vert.x没有限制只能使用字符串传递消息,我们可以使用更复杂的JSON对象,甚至使用Buffer类构建自己的消息。这些消息是可变的,也就是说如果使用不当,消息发送者和接收者可以通过读写消息共享状态。
Vert.x框架通过在发送消息时复制消息的方式来避免这种问题。这样既保证接收者得到了正确的结果,又不会共享状态。无论是否使用Vert.x,确保消息不会共享状态都是最重要的。不可变消息是最简单的解决方式,但通过复制消息也能解决该问题。
使用verticle对象模型开发的并发系统易于测试,因为每个verticle对象都可以通过发送消息、验证返回值的方式单独测试。然后使用这些经过测试的模块组合成一个复杂系统,而不用担心使用共享的可变状态通信在集成时会遇到大量问题。当然,点对点的测试还是必须的,确保系统和预期的行为一致。
基于消息传递的系统让隔离错误变得简单,也便于编写可靠的代码。如果一个消息处理程序发生错误,可以选择重启本地verticle对象,而不用去重启整个JVM。
在第6章中,我们看到了如何使用Lambda表达式和Stream类库编写并行处理数据代码。并行机制让处理海量数据的速度更快,消息传递和稍后将会介绍的响应式编程是问题的另一面:我们希望在有限的并行运行的线程里,执行更多的I/O操作,比如连接更多的聊天客户端。无论哪种情况,解决方案都是一样的:使用Lambda表达式表示行为,构建API来管理并发。聪明的类库意味着简单的应用代码。
