如需使用最新稳定版本,请使用 Spring Integration 7.0.4spring-doc.cadn.net.cn

Hazelcast 支持

Spring Integration 提供通道适配器和其他实用组件,用于与内存数据网格 Hazelcast 进行交互。spring-doc.cadn.net.cn

您需要将以下依赖项包含到您的项目中:spring-doc.cadn.net.cn

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-hazelcast</artifactId>
    <version>6.4.10</version>
</dependency>
compile "org.springframework.integration:spring-integration-hazelcast:6.4.10"

Hazelcast 组件的 XML 命名空间和 schemaLocation 定义如下:spring-doc.cadn.net.cn

xmlns:int-hazelcast="http://www.springframework.org/schema/integration/hazelcast"
xsi:schemaLocation="http://www.springframework.org/schema/integration/hazelcast
          https://www.springframework.org/schema/integration/hazelcast/spring-integration-hazelcast.xsd"

Hazelcast 事件驱动入站通道适配器

Hazelcast 提供分布式数据结构,例如:spring-doc.cadn.net.cn

它还提供了事件监听器,以便监听对这些数据结构所做的修改。spring-doc.cadn.net.cn

Hazelcast 事件驱动入站通道适配器会监听相关的缓存事件,并将事件消息发送到定义的通道。 它支持基于 XML 和 JavaConfig 的配置方式。spring-doc.cadn.net.cn

XML 配置:

<int-hazelcast:inbound-channel-adapter channel="mapChannel"
                      cache="map"
                      cache-events="UPDATED, REMOVED"
                      cache-listening-policy="SINGLE" />

Hazelcast 事件驱动入站通道适配器需要以下属性:spring-doc.cadn.net.cn

  • channel: 指定发送消息的通道;spring-doc.cadn.net.cn

  • cache: 指定被监听的分布式对象引用。 这是一个必需属性;spring-doc.cadn.net.cn

  • cache-events: 指定监听缓存事件。 这是一个可选属性,其默认值为 ADDED。 其支持的取值如下:spring-doc.cadn.net.cn

  • 支持的缓存事件类型对于 IMapMultiMap:包括 ADDED, REMOVED, UPDATED, EVICTED, EVICT_ALLCLEAR_ALLspring-doc.cadn.net.cn

  • 支持的缓存事件类型为 ReplicatedMapADDED, REMOVED, UPDATED, EVICTED;spring-doc.cadn.net.cn

  • 支持的缓存事件类型为 IListISetIQueueADDEDREMOVED。 对于 ITopic,没有对应的缓存事件类型。spring-doc.cadn.net.cn

  • cache-listening-policy: 指定缓存监听策略为 SINGLEALL。 这是一个可选属性,其默认值为 SINGLE。 每个监听相同缓存对象且具有相同 cache-events 属性的 Hazelcast 入站通道适配器,可以接收单个事件消息或所有事件消息。 如果设置为 ALL,则所有监听相同缓存对象且具有相同 cache-events 属性的 Hazelcast 入站通道适配器将接收所有事件消息。 如果设置为 SINGLE,它们将接收唯一的事件消息。spring-doc.cadn.net.cn

一些配置示例:spring-doc.cadn.net.cn

分布式映射
<int:channel id="mapChannel"/>

<int-hazelcast:inbound-channel-adapter channel="mapChannel"
                              cache="map"
                              cache-events="UPDATED, REMOVED" />

<bean id="map" factory-bean="instance" factory-method="getMap">
    <constructor-arg value="map"/>
</bean>

<bean id="instance" class="com.hazelcast.core.Hazelcast"
            factory-method="newHazelcastInstance">
    <constructor-arg>
        <bean class="com.hazelcast.config.Config" />
    </constructor-arg>
</bean>
分布式多映射
<int-hazelcast:inbound-channel-adapter channel="multiMapChannel"
                              cache="multiMap"
                              cache-events="ADDED, REMOVED, CLEAR_ALL" />

<bean id="multiMap" factory-bean="instance" factory-method="getMultiMap">
    <constructor-arg value="multiMap"/>
</bean>
分布式列表
<int-hazelcast:inbound-channel-adapter  channel="listChannel"
                               cache="list"
                               cache-events="ADDED, REMOVED"
                               cache-listening-policy="ALL" />

<bean id="list" factory-bean="instance" factory-method="getList">
    <constructor-arg value="list"/>
</bean>
分布式集合
<int-hazelcast:inbound-channel-adapter channel="setChannel" cache="set" />

<bean id="set" factory-bean="instance" factory-method="getSet">
    <constructor-arg value="set"/>
</bean>
分布式队列
<int-hazelcast:inbound-channel-adapter  channel="queueChannel"
                               cache="queue"
                               cache-events="REMOVED"
                               cache-listening-policy="ALL" />

<bean id="queue" factory-bean="instance" factory-method="getQueue">
    <constructor-arg value="queue"/>
</bean>
分布式主题
<int-hazelcast:inbound-channel-adapter channel="topicChannel" cache="topic" />

<bean id="topic" factory-bean="instance" factory-method="getTopic">
    <constructor-arg value="topic"/>
</bean>
复制映射
<int-hazelcast:inbound-channel-adapter channel="replicatedMapChannel"
                              cache="replicatedMap"
                              cache-events="ADDED, UPDATED, REMOVED"
                              cache-listening-policy="SINGLE"  />

<bean id="replicatedMap" factory-bean="instance" factory-method="getReplicatedMap">
    <constructor-arg value="replicatedMap"/>
</bean>

Java 配置示例:

以下示例展示了DistributedMap配置。 相同的配置也可用于其他分布式数据结构(IMapMultiMapReplicatedMapIListISetIQueueITopic):spring-doc.cadn.net.cn

@Bean
public PollableChannel distributedMapChannel() {
    return new QueueChannel();
}

@Bean
public IMap<Integer, String> distributedMap() {
    return hazelcastInstance().getMap("Distributed_Map");
}

@Bean
public HazelcastInstance hazelcastInstance() {
    return Hazelcast.newHazelcastInstance();
}

@Bean
public HazelcastEventDrivenMessageProducer hazelcastEventDrivenMessageProducer() {
    final HazelcastEventDrivenMessageProducer producer = new HazelcastEventDrivenMessageProducer(distributedMap());
    producer.setOutputChannel(distributedMapChannel());
    producer.setCacheEventTypes("ADDED,REMOVED,UPDATED,CLEAR_ALL");
    producer.setCacheListeningPolicy(CacheListeningPolicyType.SINGLE);

    return producer;
}

Hazelcast 连续查询入站通道适配器

Hazelcast 连续查询功能支持监听特定映射条目上执行的修改操作。 Hazelcast 连续查询入站通道适配器是一个事件驱动的通道适配器,它根据定义的谓词监听相关的分布式映射事件。spring-doc.cadn.net.cn

@Bean
public PollableChannel cqDistributedMapChannel() {
    return new QueueChannel();
}

@Bean
public IMap<Integer, String> cqDistributedMap() {
    return hazelcastInstance().getMap("CQ_Distributed_Map");
}

@Bean
public HazelcastInstance hazelcastInstance() {
    return Hazelcast.newHazelcastInstance();
}

@Bean
public HazelcastContinuousQueryMessageProducer hazelcastContinuousQueryMessageProducer() {
    final HazelcastContinuousQueryMessageProducer producer =
        new HazelcastContinuousQueryMessageProducer(cqDistributedMap(), "surname=TestSurname");
    producer.setOutputChannel(cqDistributedMapChannel());
    producer.setCacheEventTypes("UPDATED");
    producer.setIncludeValue(false);

    return producer;
}
<int:channel id="cqMapChannel"/>

<int-hazelcast:cq-inbound-channel-adapter
                channel="cqMapChannel"
                cache="cqMap"
                cache-events="UPDATED, REMOVED"
                predicate="name=TestName AND surname=TestSurname"
                include-value="true"
                cache-listening-policy="SINGLE"/>

<bean id="cqMap" factory-bean="instance" factory-method="getMap">
    <constructor-arg value="cqMap"/>
</bean>

<bean id="instance" class="com.hazelcast.core.Hazelcast"
            factory-method="newHazelcastInstance">
    <constructor-arg>
        <bean class="com.hazelcast.config.Config" />
    </constructor-arg>
</bean>

它支持以下六个属性:spring-doc.cadn.net.cn

  • channel: 指定发送消息的通道;spring-doc.cadn.net.cn

  • cache: 指定要监听的分布式 Map 引用。 必填;spring-doc.cadn.net.cn

  • cache-events: 指定要监听的缓存事件。 可选属性,默认值为 ADDED。 支持的值为 ADDEDREMOVEDUPDATEDEVICTEDEVICT_ALLCLEAR_ALLspring-doc.cadn.net.cn

  • predicate: 指定一个谓词,用于监听对特定映射条目执行的修改。 必需;spring-doc.cadn.net.cn

  • include-value: 指定在连续查询结果中包含 value 和 oldValue。 可选,默认为 truespring-doc.cadn.net.cn

  • cache-listening-policy: 指定缓存监听策略为 SINGLEALL。 可选,默认值为 SINGLE。 每个监听相同缓存对象且具有相同 cache-events 属性的 Hazelcast CQ 入站通道适配器,可以接收单个事件消息或所有事件消息。 如果设置为 ALL,则所有监听相同缓存对象且具有相同 cache-events 属性的 Hazelcast CQ 入站通道适配器将接收所有事件消息。 如果设置为 SINGLE,它们将接收唯一的事件消息。spring-doc.cadn.net.cn

Hazelcast 集群监控入站通道适配器

Hazelcast 集群监视器支持监听在集群上执行的修改操作。 Hazelcast 集群监视器入站通道适配器是一个事件驱动的通道适配器,它监听相关的成员资格、分布式对象、迁移、生命周期和客户端事件:spring-doc.cadn.net.cn

@Bean
public PollableChannel eventChannel() {
    return new QueueChannel();
}

@Bean
public HazelcastInstance hazelcastInstance() {
    return Hazelcast.newHazelcastInstance();
}

@Bean
public HazelcastClusterMonitorMessageProducer hazelcastClusterMonitorMessageProducer() {
    HazelcastClusterMonitorMessageProducer producer = new HazelcastClusterMonitorMessageProducer(hazelcastInstance());
    producer.setOutputChannel(eventChannel());
    producer.setMonitorEventTypes("DISTRIBUTED_OBJECT");

    return producer;
}
<int:channel id="monitorChannel"/>

<int-hazelcast:cm-inbound-channel-adapter
                 channel="monitorChannel"
                 hazelcast-instance="instance"
                 monitor-types="MEMBERSHIP, DISTRIBUTED_OBJECT" />

<bean id="instance" class="com.hazelcast.core.Hazelcast"
            factory-method="newHazelcastInstance">
    <constructor-arg>
        <bean class="com.hazelcast.config.Config" />
    </constructor-arg>
</bean>

它支持以下三个属性:spring-doc.cadn.net.cn

  • channel: 指定发送消息的通道;spring-doc.cadn.net.cn

  • hazelcast-instance: 指定用于监听集群事件的 Hazelcast 实例引用。 这是一个必需属性;spring-doc.cadn.net.cn

  • monitor-types: 指定要监听的状态类型。 这是一个可选属性,默认值为 MEMBERSHIP。 支持的值为 MEMBERSHIPDISTRIBUTED_OBJECTMIGRATIONLIFECYCLECLIENTspring-doc.cadn.net.cn

Hazelcast 分布式 SQL 入站通道适配器

Hazelcast 允许在分布式映射上运行分布式查询。 Hazelcast 分布式 SQL 入站通道适配器是一种轮询式入站通道适配器。 它执行定义的分布式 SQL 命令,并根据迭代类型返回结果。spring-doc.cadn.net.cn

@Bean
public PollableChannel dsDistributedMapChannel() {
    return new QueueChannel();
}

@Bean
public IMap<Integer, String> dsDistributedMap() {
    return hazelcastInstance().getMap("DS_Distributed_Map");
}

@Bean
public HazelcastInstance hazelcastInstance() {
    return Hazelcast.newHazelcastInstance();
}

@Bean
@InboundChannelAdapter(value = "dsDistributedMapChannel", poller = @Poller(maxMessagesPerPoll = "1"))
public HazelcastDistributedSQLMessageSource hazelcastDistributedSQLMessageSource() {
    final HazelcastDistributedSQLMessageSource messageSource =
        new HazelcastDistributedSQLMessageSource(dsDistributedMap(),
            "name='TestName' AND surname='TestSurname'");
    messageSource.setIterationType(DistributedSQLIterationType.ENTRY);

    return messageSource;
}
<int:channel id="dsMapChannel"/>

<int-hazelcast:ds-inbound-channel-adapter
            channel="dsMapChannel"
            cache="dsMap"
            iteration-type="ENTRY"
            distributed-sql="active=false OR age >= 25 OR name = 'TestName'">
    <int:poller fixed-delay="100"/>
</int-hazelcast:ds-inbound-channel-adapter>

<bean id="dsMap" factory-bean="instance" factory-method="getMap">
    <constructor-arg value="dsMap"/>
</bean>

<bean id="instance" class="com.hazelcast.core.Hazelcast"
            factory-method="newHazelcastInstance">
    <constructor-arg>
        <bean class="com.hazelcast.config.Config" />
    </constructor-arg>
</bean>

它需要一个轮询器,并支持四个属性:spring-doc.cadn.net.cn

  • channel: 指定消息发送到的通道。 这是一个必需属性;spring-doc.cadn.net.cn

  • cache: 指定要查询的分布式 IMap 引用。 这是必需属性;spring-doc.cadn.net.cn

  • iteration-type: 指定结果类型。 分布式 SQL 可在 EntrySetKeySetLocalKeySetValues 上运行。 这是一个可选属性,默认值为 VALUE。 支持的值为 ENTRY, `KEYLOCAL_KEYVALUEspring-doc.cadn.net.cn

  • distributed-sql: 指定 SQL 语句的 where 子句。 它是一个必填属性。spring-doc.cadn.net.cn

Hazelcast 出站通道适配器

Hazelcast Outbound Channel Adapter 监听其定义的通道,并将传入的消息写入相关的分布式缓存。 它期望分布式对象定义使用 cachecache-expressionHazelcastHeaders.CACHE_NAME 之一。 支持的分布式对象包括:IMapMultiMapReplicatedMapIListISetIQueueITopicspring-doc.cadn.net.cn

@Bean
public MessageChannel distributedMapChannel() {
    return new DirectChannel();
}

@Bean
public IMap<Integer, String> distributedMap() {
    return hzInstance().getMap("Distributed_Map");
}

@Bean
public HazelcastInstance hzInstance() {
    return Hazelcast.newHazelcastInstance();
}

@Bean
@ServiceActivator(inputChannel = "distributedMapChannel")
public HazelcastCacheWritingMessageHandler hazelcastCacheWritingMessageHandler() {
    HazelcastCacheWritingMessageHandler handler = new HazelcastCacheWritingMessageHandler();
    handler.setDistributedObject(distributedMap());
    handler.setKeyExpression(new SpelExpressionParser().parseExpression("payload.id"));
    handler.setExtractPayload(true);
    return handler;
}
<int-hazelcast:outbound-channel-adapter channel="mapChannel"
                    cache-expression="headers['CACHE_HEADER']"
                    key-expression="payload.key"
                    extract-payload="true"/>

需要以下属性:spring-doc.cadn.net.cn

  • channel: 指定发送消息的通道;spring-doc.cadn.net.cn

  • cache: 指定分布式对象引用。 可选;spring-doc.cadn.net.cn

  • cache-expression: 通过 Spring 表达式语言 (SpEL) 指定分布式对象。 可选;spring-doc.cadn.net.cn

  • key-expression: 通过 Spring 表达式语言 (SpEL) 指定键值对的键。 仅对 IMapMultiMapReplicatedMap 分布式数据结构为可选且必需。spring-doc.cadn.net.cn

  • extract-payload: 指定是发送整个消息还是仅发送有效负载。 可选属性,默认值为 true。 如果为 true,则仅将有效负载写入分布式对象。 否则,将通过转换消息头和有效负载来写入整个消息。spring-doc.cadn.net.cn

通过在请求头中设置分布式对象名称,可以通过同一通道将消息写入不同的分布式对象。 如果未定义 cachecache-expression 属性,则必须在请求 MessageHazelcastHeaders.CACHE_NAME 头部中设置值。spring-doc.cadn.net.cn

Hazelcast 领导者选举

如果需要领导者选举(例如,在需要高可用的消息消费者场景中,仅允许一个节点接收消息),可以使用基于 Hazelcast 的LeaderInitiatorspring-doc.cadn.net.cn

@Bean
public HazelcastInstance hazelcastInstance() {
    return Hazelcast.newHazelcastInstance();
}

@Bean
public LeaderInitiator initiator() {
    return new LeaderInitiator(hazelcastInstance());
}

当一个节点被选举为领导者时,它会向所有应用程序监听器发送一个OnGrantedEventspring-doc.cadn.net.cn

Hazelcast 消息存储

对于分布式消息状态管理,例如用于持久化的 QueueChannel 或跟踪的 Aggregator 消息组,提供了 HazelcastMessageStore 实现:spring-doc.cadn.net.cn

@Bean
public HazelcastInstance hazelcastInstance() {
    return Hazelcast.newHazelcastInstance();
}

@Bean
public MessageGroupStore messageStore() {
    return new HazelcastMessageStore(hazelcastInstance());
}

默认情况下,SPRING_INTEGRATION_MESSAGE_STORE IMap 用于将消息和组作为键值对进行存储。 任何自定义的 IMap 都可以提供给 HazelcastMessageStorespring-doc.cadn.net.cn

Hazelcast 元数据存储

使用后端 Hazelcast IMap 可实现一个 ListenableMetadataStore。 默认创建的 Map 名称为 SPRING_INTEGRATION_METADATA_STORE,该名称可自定义。spring-doc.cadn.net.cn

@Bean
public HazelcastInstance hazelcastInstance() {
    return Hazelcast.newHazelcastInstance();
}

@Bean
public MetadataStore metadataStore() {
    return new HazelcastMetadataStore(hazelcastInstance());
}

The HazelcastMetadataStore 实现了 ListenableMetadataStore,允许您通过 addListener(MetadataStoreListener callback) 注册类型为 MetadataStoreListener 的自定义监听器以监听事件。spring-doc.cadn.net.cn

Hazelcast 锁注册表

使用后端 Hazelcast 分布式ILock支持,可获得LockRegistry的实现:spring-doc.cadn.net.cn

@Bean
public HazelcastInstance hazelcastInstance() {
    return Hazelcast.newHazelcastInstance();
}

@Bean
public LockRegistry lockRegistry() {
    return new HazelcastLockRegistry(hazelcastInstance());
}

当与共享MessageGroupStore(例如Aggregator存储管理)一起使用时,HazelcastLockRegistry可用于在多个应用程序实例间提供此功能,使得同一时间只有一个实例可以操作该组。spring-doc.cadn.net.cn

对于所有分布式操作,CP 子系统必须在 HazelcastInstance 上启用。

带有 Hazelcast 的消息通道

Hazelcast IQueueITopic 分布式对象本质上是消息传递原语,可以在不额外实现 Hazelcast 模块的情况下,与 Spring Integration 核心组件一起使用。spring-doc.cadn.net.cn

The QueueChannel can be supplied by any java.util.Queue, including the mentioned Hazelcast distributed IQueue:spring-doc.cadn.net.cn

@Bean
PollableChannel hazelcastQueueChannel(HazelcastInstance hazelcastInstance) {
    return new QueueChannel(hazelcastInstance.getQueue("springIntegrationQueue"));
}

将此配置放置在应用程序的 Hazelcast 集群的多个节点上,将使 QueueChannel 作为分布式对象,且只有一个节点能够从该 IQueue 轮询单个 Message。 此功能类似于 PollableJmsChannelPollableKafkaChannelPollableAmqpChannelspring-doc.cadn.net.cn

如果生产者端不是 Spring Integration 应用程序,则无法配置 QueueChannel,因此将使用普通的 Hazelcast IQueue API 来生产数据。 在这种情况下,QueueChannel 方法在消费者端是错误的:必须改用 入站通道适配器 方案:spring-doc.cadn.net.cn

@Bean
public IQueue<String> myStringHzQueue(HazelcastInstance hazelcastInstance) {
    return hazelcastInstance.getQueue("springIntegrationQueue");
}

@Bean
@InboundChannelAdapter(channel = "stringValuesFromHzQueueChannel")
Supplier<String> fromHzIQueueSource(IQueue<String> myStringHzQueue) {
    return myStringHzQueue::poll;
}

Hazelcast 中的 ITopic 抽象具有与 JMS 中的 Topic 类似的语义:所有订阅者都会接收到发布的消息。 通过一对简单的 MessageChannel Bean,该机制可作为开箱即用的功能得到支持:spring-doc.cadn.net.cn

@Bean
public ITopic<Message<?>> springIntegrationTopic(HazelcastInstance hazelcastInstance,
        MessageChannel fromHazelcastTopicChannel) {

    ITopic<Message<?>> topic = hazelcastInstance.getTopic("springIntegrationTopic");
	topic.addMessageListener(m -> fromHazelcastTopicChannel.send(m.getMessageObject()));
	return topic;
}

@Bean
public MessageChannel publishToHazelcastTopicChannel(ITopic<Message<?>> springIntegrationTopic) {
    return new FixedSubscriberChannel(springIntegrationTopic::publish);
}

@Bean
public MessageChannel fromHazelcastTopicChannel() {
    return new DirectChannel();
}

The FixedSubscriberChannelDirectChannel的优化变体,初始化时需要MessageHandler。 由于MessageHandler是一个函数式接口,可以为handleMessage方法提供一个简单的lambda表达式。 当消息发送到publishToHazelcastTopicChannel时,它会被发布到Hazelcast ITopic上。 com.hazelcast.topic.MessageListener也是一个函数式接口,因此可以为ITopic#addMessageListener提供lambda表达式。 因此,订阅fromHazelcastTopicChannel的消费者将消费所有发送到上述ITopic的消息。spring-doc.cadn.net.cn

一个 ExecutorChannel 可以与一个 IExecutorService 配合使用。 例如,通过相应的配置可以实现集群范围的单例:spring-doc.cadn.net.cn

@Bean
public HazelcastInstance hazelcastInstance() {
    return Hazelcast.newHazelcastInstance(
                new Config()
                    .addExecutorConfig(new ExecutorConfig()
                         .setName("singletonExecutor")
                         .setPoolSize(1)));
}

@Bean
public MessageChannel hazelcastSingletonExecutorChannel(HazelcastInstance hazelcastInstance) {
    return new ExecutorChannel(hazelcastInstance.getExecutorService("singletonExecutor"));
}