# Stomp 支持

# stomp 支持

Spring 集成版本 4.2 引入了 STOMP(简单文本定向消息传递协议)客户端支持。它基于 Spring 框架的消息传递模块 STOMP 包中的体系结构、基础设施和 API。 Spring 集成使用 Spring Stomp 组件中的许多(例如StompSessionStompClientSupport)。有关更多信息,请参见 Spring 框架参考手册中的Spring Framework STOMP Support (opens new window)章节。

你需要在项目中包含此依赖项:

Maven

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-stomp</artifactId>
    <version>5.5.9</version>
</dependency>

Gradle

compile "org.springframework.integration:spring-integration-stomp:5.5.9"

对于服务器端组件,你需要添加org.springframework:spring-websocket和/或io.projectreactor.netty:reactor-netty依赖关系。

# 概述

要配置 STOMP,你应该从 STOMP 客户机对象开始。 Spring 框架提供了以下实现方式:

  • WebSocketStompClient:构建在 Spring WebSocket API 之上,支持标准的 JSR-356 WebSocket、 Jetty 9,以及用于基于 HTTP 的 WebSocket SockJS 仿真的 SockJS 和 SockJS 客户端。

  • ReactorNettyTcpStompClient:构建于ReactorNettyTcpClient项目的reactor-netty之上。

你可以提供任何其他StompClientSupport实现。参见这些类的Javadoc (opens new window)

StompClientSupport类设计为工厂,以生成针对所提供的StompSessionHandlerStompSession,并通过回调StompSessionHandlerStompSession的抽象来完成所有剩余的工作。通过 Spring 集成适配器抽象,我们需要提供一些托管共享对象,以将我们的应用程序表示为具有唯一会话的 Stomp 客户机。为此, Spring 集成提供了StompSessionManager的抽象,以管理在任意提供的单身StompSession之间的StompSessionHandler。这允许对特定的 Stomp 代理使用入站出站通道适配器(或两者)。有关更多信息,请参见StompSessionManager(及其实现)Javadocs。

# Stomp 入站通道适配器

StompInboundChannelAdapter是一个一站式MessageProducer组件,它将你的 Spring 集成应用程序订阅到所提供的 stomp 目的地,并从它们接收消息(通过在连接的StompSession上使用所提供的MessageConverter从 stomp 帧转换)。通过在StompInboundChannelAdapter上使用适当的@ManagedOperation注释,你可以在运行时更改目的地(因此也可以更改 stomp 订阅)。

有关更多配置选项,请参见Stomp 名称空间支持StompInboundChannelAdapterJavadoc (opens new window)

# Stomp 出站通道适配器

StompMessageHandlerMessageHandlerMessageHandler,用于将传出的Message<?>实例通过destination发送到 stompdestination(在运行时通过 spel 表达式预先配置或确定)(由共享的StompSession提供)。

有关更多配置选项,请参见Stomp 名称空间支持StompMessageHandlerJavadoc (opens new window)

# Stomp 标头映射

STOMP 协议提供头作为其框架的一部分。Stomp 框架的整个结构具有以下格式:

....
COMMAND
header1:value1
header2:value2

Body^@
....

Spring Framework 提供StompHeaders来表示这些头。有关更多详细信息,请参见Javadoc (opens new window)。Stomp 帧被转换为和转换为Message<?>实例,这些头被映射为和转换为MessageHeaders实例。 Spring 集成为 Stomp 适配器提供了一个默认的HeaderMapper实现。实现方式是StompHeaderMapper。它分别为入站适配器和出站适配器提供fromHeaders()toHeaders()操作。

与许多其他 Spring 集成模块一样,引入了IntegrationStompHeaders类来将标准的 stomp 头映射到MessageHeaders,并以stomp_作为头名称前缀。此外,所有带有该前缀的MessageHeaders实例在发送到目标时都映射到StompHeaders

有关更多信息,请参见Javadoc (opens new window)中的这些类和mapped-headers属性描述。

# Stomp 集成事件

许多 STOMP 操作是异步的,包括错误处理。例如,Stomp 有一个RECEIPT服务器帧,当客户端帧通过添加RECEIPT头请求一个服务器帧时,它会返回这个服务器帧。为了提供对这些异步事件的访问, Spring 集成会发出StompIntegrationEvent实例,你可以通过实现ApplicationListener或使用<int-event:inbound-channel-adapter>(参见Receiving Spring Application Events)来获得这些实例。

具体地说,当stompSessionListenableFuture由于连接到 Stomp 代理失败而接收到onFailure()时,从AbstractStompSessionManager发出StompExceptionEvent。另一个例子是StompMessageHandler。它处理ERRORstomp 帧,这些帧是服务器对此StompMessageHandler发送的不正确(不接受)消息的响应。

在发送到StompSession的消息的异步答案中,StompMessageHandler发出StompReceiptEvent作为StompSession.Receiptable回调的一部分的StompReceiptEventStompReceiptEvent可以是正的,也可以是负的,这取决于RECEIPT帧是否在receiptTimeLimit期间从服务器接收,你可以在StompClientSupport实例上配置该帧。它默认为15 * 1000(以毫秒为单位,所以是 15 秒)。

只有当要发送的消息的RECEIPTstomp 报头不是null时,才会添加StompSession.Receiptable回调。
你可以分别通过其StompSession选项和StompSessionManager选项在StompSession报头上启用自动RECEIPT报头生成。

有关如何配置 Spring 集成以接受那些ApplicationEvent实例的详细信息,请参见Stomp 适配器 Java 配置

# Stomp 适配器 Java 配置

下面的示例展示了用于 Stomp 适配器的全面的 Java 配置:

@Configuration
@EnableIntegration
public class StompConfiguration {

    @Bean
    public ReactorNettyTcpStompClient stompClient() {
        ReactorNettyTcpStompClient stompClient = new ReactorNettyTcpStompClient("127.0.0.1", 61613);
        stompClient.setMessageConverter(new PassThruMessageConverter());
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.afterPropertiesSet();
        stompClient.setTaskScheduler(taskScheduler);
        stompClient.setReceiptTimeLimit(5000);
        return stompClient;
    }

    @Bean
    public StompSessionManager stompSessionManager() {
        ReactorNettyTcpStompSessionManager stompSessionManager = new ReactorNettyTcpStompSessionManager(stompClient());
        stompSessionManager.setAutoReceipt(true);
        return stompSessionManager;
    }

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

    @Bean
    public StompInboundChannelAdapter stompInboundChannelAdapter() {
        StompInboundChannelAdapter adapter =
        		new StompInboundChannelAdapter(stompSessionManager(), "/topic/myTopic");
        adapter.setOutputChannel(stompInputChannel());
        return adapter;
    }

    @Bean
    @ServiceActivator(inputChannel = "stompOutputChannel")
    public MessageHandler stompMessageHandler() {
        StompMessageHandler handler = new StompMessageHandler(stompSessionManager());
        handler.setDestination("/topic/myTopic");
        return handler;
    }

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

    @Bean
    public ApplicationListener<ApplicationEvent> stompEventListener() {
        ApplicationEventListeningMessageProducer producer = new ApplicationEventListeningMessageProducer();
        producer.setEventTypes(StompIntegrationEvent.class);
        producer.setOutputChannel(stompEvents());
        return producer;
    }

}

# Stomp 名称空间支持

Spring 集成 Stomp 命名空间实现了入站和出站通道适配器组件。要将其包含在配置中,请在应用程序上下文配置文件中提供以下名称空间声明:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:int="http://www.springframework.org/schema/integration"
  xmlns:int-stomp="http://www.springframework.org/schema/integration/stomp"
  xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration
    https://www.springframework.org/schema/integration/spring-integration.xsd
    http://www.springframework.org/schema/integration/stomp
    https://www.springframework.org/schema/integration/stomp/spring-integration-stomp.xsd">
    ...
</beans>

# 理解<int-stomp:outbound-channel-adapter>元素

下面的清单显示了 STOMP 出站通道适配器的可用属性:

<int-stomp:outbound-channel-adapter
                           id=""                      (1)
                           channel=""                 (2)
                           stomp-session-manager=""   (3)
                           header-mapper=""           (4)
                           mapped-headers=""          (5)
                           destination=""             (6)
                           destination-expression=""  (7)
                           auto-startup=""            (8)
                           phase=""/>                 (9)
1 组件 Bean 名.
MessageHandlerid的 Bean 别名注册为id加上.handler
如果不设置channel属性,在应用程序上下文中创建并注册了一个DirectChannel,并将这个id属性的值作为 Bean 名称。
在这种情况下,端点以 Bean 名称id加上.adapter注册。
2 如果id存在,则标识连接到此适配器的通道。
参见id
可选。
3 引用StompSessionManager Bean,它封装了低级连接和StompSession处理操作。
需要。
4 引用一个实现HeaderMapper<StompHeaders>的 Bean,它将 Spring 集成MessageHeaders映射到和从
stomp 帧头映射。
它与mapped-headers是互斥的。
它默认为StompHeaderMapper
5 用逗号分隔的要映射到 Stomp 框架标头的 Stomp 标头的名称列表。
只能提供如果header-mapper引用未设置。
此列表中的值也可以是简单的模式,以与标题名称进行匹配(例如myheader**myheader)。
一个特殊的标记(STOMP_OUTBOUND_HEADERS)表示所有标准的 stomp 标题(内容长度、收据、心跳),
它们是默认包含的。
如果你想添加自己的标头并希望同时映射标准标头,则必须包含该令牌或通过使用header-mapper提供自己的HeaderMapper实现。
6 STOMP 消息发送到的目的地的名称。
它与destination-expression是互斥的。
7 在运行时针对每个 Spring 积分Message作为根对象求值的 SPEL 表达式。
它与destination是互斥的。
8 布尔值,表示此端点是否应自动启动。
默认为true
9 这个端点应该在其中开始和停止的生命周期阶段。
值越低,这个端点开始得越早,停止得越晚。
默认值是Integer.MIN_VALUE
值可以是负数。
参见[SmartLifeCycle(https:/DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/api/context/smartlifycle.html)。

# 理解<int-stomp:inbound-channel-adapter>元素

下面的清单显示了 STOMP 入站通道适配器的可用属性:

<int-stomp:inbound-channel-adapter
                           id=""                     (1)
                           channel=""                (2)
                           error-channel=""          (3)
                           stomp-session-manager=""  (4)
                           header-mapper=""          (5)
                           mapped-headers=""         (6)
                           destinations=""           (7)
                           send-timeout=""           (8)
                           payload-type=""           (9)
                           auto-startup=""           (10)
                           phase=""/>                (11)
1 组件 Bean name.
如果不设置channel属性,将在应用程序上下文中创建并注册一个DirectChannel,并将这个id属性的值作为 Bean 名称。
在这种情况下,端点以 Bean 名id加上.adapter注册。
2 标识连接到此适配器的通道。
3 应该发送ErrorMessage实例的MessageChannel Bean 引用。
4 请参阅[<int-stomp:outbound-channel-adapter>](#stomp-出站-通道-适配器)上的相同选项。
5 用逗号分隔的要从 stomp 帧头映射的 stomp 头的名称列表。
只有在header-mapper引用未设置的情况下,才能提供此选项。
此列表中的值也可以是与头名称匹配的简单模式(例如,myheader**myheader)。
特殊令牌(STOMP_INBOUND_HEADERS)表示所有标准的 Stomp 标头(内容长度、收据、心跳等)。
默认情况下,它们都包括在内。
如果你想添加自己的标头,并希望同时映射标准标头,你还必须包括这个令牌,或者使用header-mapper提供你自己的HeaderMapper实现。
6 请参阅[<int-stomp:outbound-channel-adapter>](#stomp-出站-通道-适配器)上的相同选项。
7 用逗号分隔的要订阅的目标名称列表。
可以在运行时通过addDestination()removeDestination()``@ManagedOperation注释修改目标(因此也是订阅)列表。
8 如果信道可以阻塞,则在向信道发送消息时等待的最大时间量(以毫秒为单位)。
例如,如果已达到其最大容量,则QueueChannel可以阻塞直到可用空间。
9 从传入的 Stomp 帧转换的目标payload的完全限定的 Java 类型名称。
它默认为String.class
10 请参阅[<int-stomp:outbound-channel-adapter>](#stomp-出站-通道-适配器)上的相同选项。
11 请参阅[<int-stomp:outbound-channel-adapter>](#stomp-出站-通道-适配器)上的相同选项。