SCS 用于构建高度可扩展的基于事件驱动的微服务,其目的是为了简化消息在 Spring Cloud 应用程序的开发。本章将讲述其中两个重要的模块,Messaging 和 Integration。
Spring Messaging:
Spring Messaging 是 Spring Framework 中的一个模块,其作用就是同意消息的编程模型。
- 消息 Messaging 的模型包括一个消息体 Payload 和 消息头 Header
# 构建一个空头消息
MessageBuilder.withPayload(payload).build();
- 消息通道 MessageChannel 用于接收消息,调用其中的 send 方法可以将消息发送到该消息通道中:
# 发送一条信息到消息通道,并设置过期时间
messageChannel.send(message, timeout);
发生消息的目的当然是为了消息被消费,那么如何消费消息通道的消息呢?
- MessageChannel 只是一个提供 send 方法的函数式接口,而 SubscribeChannel 对 MessageChannel 进行了增强,使得消费者可以通过订阅的方式获取到消息。
# message handler 订阅消息
messageChannel.subscribe(messageHandler);
# 取消订阅
messageChannel.unsubscribe(messageHandler);
PollableChannel 也是继承了 MessageChannel 的接口,但与 SubscribeChannel 不同的是,他是通过消费者对消息通道的轮询实现的。
- 消息的消费者
MessageHandler
MessageHandler 也是一个函数式接口,实现 handleMessage 方法后,订阅 messageChannel 即可在发布消息后,收到消息并处理。
# 函数式接口支持 lambda 表达式
messageChannel.subscribe(msg -> { System.out.println("receive: " + msg)});
以上就是 Messaging 的主要功能。
String Integration
Spring Integration 提供了 Spring 编程模型的扩展,是对 Spring Messaging 的扩展。 在开发过程中,我们可能会遇到几个场景:
- 广播发送消息
- 非阻塞发送消息
- 确保消息被消费
- 单播发送消息
Integration 内置了许多实现了不同功能的 Channel 来应对我们开发过程中遇到的问题。我们看看这些场景,Integration 是如何解决的:
消息通道 | 分发器 | 接口/父类 | 生产者 | 消费者 | 特点 |
---|---|---|---|---|---|
PublishSubscribeChannel | 广播 | SubscribeChannel | 阻塞直到消息分发完 | 异步消费信息 | 广播 |
QueueChannel | 单播 | PollableChannel | 队列不满非阻塞 | 异步消费信息 | 异步非阻塞 |
RendezvousChannel | 单播 | QueueChannel | 队列不满非阻塞 | 异步消费信息 | 确保消息被消费才能发送消息 |
DirectChannel | 单播 | SubscirbeChannel | 阻塞直到消息分发完 | 异步消费信息 | 负载均衡 |
- PublishSubscribeChannel 是 SubscribeChannel 的实现类。在消费者订阅时,在内部将通过
OrderedAwareCopyOnWriteArraySet
这个数据结构将 Message 的消费者,也就是 MessageHandler 存储下来。当消息到达时,会通过内置的 BroadcastingDispatcher 消息分发器,根据优先级进行广播。
OrderedAwareCopyOnWriteArraySet 是对 CopyOnWriteArraySet 的增强,内置了 OrderComparator 对象,他可以对泛型元素进行排序(元素需要实现 Ordered 接口),并且使用了 ReetrantReadWriteLock 保证他的强一致性。
- QueueChannel 默认在内部通过
LinkedBlockingQueue
实现对消息的接收,也可以设置为其他的队列。对于阻塞队列和非阻塞队列,QueueChannel 有着不同的策略。
- 阻塞队列是线程安全的,QueueChannel 仅在队列满时会阻塞消息生产者,仅在队列空时会阻塞消息消费者。
- 非阻塞队列是线程不安全的,虽然 QueueChannel 内部使用了 Semaphore,在这里可以视 Semaphore 为减少轮询的策略。QueueChannel 在队列满时不会阻塞消息生产者,而是交给队列执行饱和策略(不同的队列有不同的饱和策略,也许是扩容,也许是直接丢弃)。在队列空时,QueueChannel 将通过轮询执行。
- 在生产过程中,建议选择阻塞队列实现消息的接收与发送。
- RendezvousChannel 是 QueueChannel 的子类,唯一的不同是他仅使用 SynchronousQueue 作为内置队列。由于 SynchronousQueue 的特性,可以确保消息发送到消费者后才能继续往消息通道中发送消息。
- DirectChannel 属于 SubscribeChannel 的一种,它和 PublishSubscribeChannel 类似,拥有众多的订阅者。但他发送消息时,仅选择一名订阅者发送。所以 DirectChannel 实质上是一个单播的消息通道,内部是通过 UnicastingDispatcher 这个消息分发器实现单播的。而这个消息分发器内置 LoadBalancingStrategy 负载均衡器策略,在默认情况下采用轮询的负载均衡策略。
Spring Cloud Stream
开发中遇到的场景可能远不止这些,上面的消息通道在单机多线程场景下可以满足大多数的需求,但在分布式场景下,还需要做另外的增强。 分布式场景下,使用消息队列作为消息通道已经成为实现异步、削峰、解耦问题的常见解决方案。 Spring Cloud Stream 是 Spring Integration 的增强,它屏蔽了消息中间件的实现细节,并以统一的一套 API 来进行消息的发送/消费,消息中间件的实现细节由各消息中间件的 Binder 完成。 Binder 是提供与外部消息中间件继承的组建,为构造 Binding 提供了两个方法,分别是 bindConsumer 和 bindProducer,分别用于消费者和生产者。 Binding 是连接应用程序和消息中间件的桥梁。