UDP Adapters

本部分介绍如何配置和使用 UDP 适配器。

Outbound UDP Adapters (XML Configuration)

以下示例配置 UDP 出站通道适配器:

<int-ip:udp-outbound-channel-adapter id="udpOut"
    host="somehost"
    port="11111"
    multicast="false"
    socket-customizer="udpCustomizer"
    channel="exampleChannel"/>

multicast 设置为 true 时,还应该在 host 属性中提供多播地址。

UDP 是一种高效但不稳定的协议。Spring 集成增加了两个属性以提高可靠性:check-lengthacknowledge。当将 check-length 设置为 true 时,适配器将使用长度字段(以网络字节顺序为四字节)前置消息数据。这使得接收方能够验证接收到的数据包的长度。如果接收系统使用的缓冲区太小以至于无法包含数据包,则该数据包可能会被截断。length 头部提供了一种检测此问题的机制。

从版本 4.3 开始,您可以将 port 设置为 0,在这种情况下,操作系统将选择端口。可以在适配器启动后且 isListening() 返回 true 时,通过调用 getPort() 来发现已选择的端口。

从版本 5.3.3 开始,您可以添加一个 SocketCustomizer Bean,以在创建 DatagramSocket 之后对其进行修改(例如,调用 setTrafficClass(0x10))。

以下示例展示了一个出站通道适配器,它向数据报数据包中添加长度检查:

<int-ip:udp-outbound-channel-adapter id="udpOut"
    host="somehost"
    port="11111"
    multicast="false"
    check-length="true"
    channel="exampleChannel"/>

数据包的接收方还必须配置为预料实际数据前面会带有长度。对于 Spring Integration UDP 入站通道适配器,设置其 check-length 属性。

第二项可靠性改进允许使用应用程序级别确认协议。接收方必须在指定时间内向发送方发送一个确认。

以下示例展示了一个出站通道适配器,它向数据报数据包中添加长度检查,并等待确认:

<int-ip:udp-outbound-channel-adapter id="udpOut"
    host="somehost"
    port="11111"
    multicast="false"
    check-length="true"
    acknowledge="true"
    ack-host="thishost"
    ack-port="22222"
    ack-timeout="10000"
    channel="exampleChannel"/>

acknowledge 设置为 true 意味着数据包的接收方可以解释添加到包含确认数据(主机和端口)的数据包中的标头。接收方很可能是 Spring Integration 入站通道适配器。

当 multicast 为 true 时,另一个属性(min-acks-for-success)指定了在 ack-timeout 内必须接收多少个确认。

从版本 4.3 开始,您可以将 ackPort 设置为 0,在这种情况下,操作系统将选择端口。

Outbound UDP Adapters (Java Configuration)

以下示例展示了如何使用 Java 配置出站 UDP 适配器:

@Bean
@ServiceActivator(inputChannel = "udpOut")
public UnicastSendingMessageHandler handler() {
    return new UnicastSendingMessageHandler("localhost", 11111);
}

(或 MulticastSendingChannelAdapter 用于多播)。

Outbound UDP Adapters (Java DSL Configuration)

以下示例展示了如何使用 Java DSL 配置出站 UDP 适配器:

@Bean
public IntegrationFlow udpOutFlow() {
    return f -> f.handle(Udp.outboundAdapter("localhost", 1234)
                    .configureSocket(socket -> socket.setTrafficClass(0x10)))
                .get();
}

Inbound UDP Adapters (XML Configuration)

以下示例展示了如何配置一个基本的单播入站 UDP 通道适配器。

<int-ip:udp-inbound-channel-adapter id="udpReceiver"
    channel="udpOutChannel"
    port="11111"
    receive-buffer-size="500"
    multicast="false"
    socket-customizer="udpCustomizer"
    check-length="true"/>

以下示例展示了如何配置一个基本的多播入站 UDP 通道适配器:

<int-ip:udp-inbound-channel-adapter id="udpReceiver"
    channel="udpOutChannel"
    port="11111"
    receive-buffer-size="500"
    multicast="true"
    multicast-address="225.6.7.8"
    check-length="true"/>

默认情况下,不针对入站数据包执行反向 DNS 查找:在未配置 DNS(例如,Docker 容器)的环境中,这会导致连接延迟。为了将 IP 地址转换为用于消息标头的主机名,可以通过将 lookup-host 属性设置为 true 来覆盖默认行为。

从版本 5.3.3 开始,您可以添加一个 SocketCustomizer bean 来修改在创建 DatagramSocket 之后。它用于接收套接字和任何用于发送 ack 的创建的套接字。

Inbound UDP Adapters (Java Configuration)

以下示例展示了如何使用 Java 配置入站 UDP 适配器:

@Bean
public UnicastReceivingChannelAdapter udpIn() {
    UnicastReceivingChannelAdapter adapter = new UnicastReceivingChannelAdapter(11111);
    adapter.setOutputChannelName("udpChannel");
    return adapter;
}

以下示例展示了如何使用 Java DSL 配置入站 UDP 适配器:

Inbound UDP Adapters (Java DSL Configuration)

@Bean
public IntegrationFlow udpIn() {
    return IntegrationFlow.from(Udp.inboundAdapter(11111))
            .channel("udpChannel")
            .get();
}

Server Listening Events

从版本 5.0.2 开始,当入站适配器启动并开始监听时,会发送一个 UdpServerListeningEvent。当适配器配置为在端口 0 上进行监听时,这非常有用,这意味着操作系统选择了该端口。如果您需要在连接到套接字的其他一些进程启动之前进行等待,也可以使用它代替轮询 isListening()

Advanced Outbound Configuration

<int-ip:udp-outbound-channel-adapter> (UnicastSendingMessageHandler) 有 destination-expressionsocket-expression 选项。

您可以将 destination-expression 用作硬编码 host-port 对的运行时替代,以根据 requestMessage 确定发往数据报数据包的目标地址(其中评估上下文使用根对象)。表达式必须计算为 URI、URI 样式中的 String(参见 RFC-2396)或 SocketAddress。您还可以对此表达式使用入站 IpHeaders.PACKET_ADDRESS 标头。在此框架中,DatagramPacketMessageMapper 在我们以消息形式接收并转换数据报时填充此标头。标头值恰好是输入数据报的 DatagramPacket.getSocketAddress() 的结果。

使用 socket-expression,出站通道适配器可以使用(例如)一个入站通道适配器套接字通过其接收数据的相同端口发送数据报。在我们的应用程序充当 UDP 服务器并且客户端在网络地址转换 (NAT) 的后面操作的情况下,这非常有用。该表达式必须评估为 DatagramSocketrequestMessage 用作评估上下文的根对象。您不能将 socket-expression 参数与 multicastacknowledge 参数一起使用。以下示例展示了如何配置一个带有转换器的 UDP 入站通道适配器,该转换器转换为大写并使用套接字:

<int-ip:udp-inbound-channel-adapter id="inbound" port="0" channel="in" />

<int:channel id="in" />

<int:transformer expression="new String(payload).toUpperCase()"
                       input-channel="in" output-channel="out"/>

<int:channel id="out" />

<int-ip:udp-outbound-channel-adapter id="outbound"
                        socket-expression="@inbound.socket"
                        destination-expression="headers['ip_packetAddress']"
                        channel="out" />

以下示例展示了使用 Java DSL 的等效配置:

@Bean
public IntegrationFlow udpEchoUpcaseServer() {
    return IntegrationFlow.from(Udp.inboundAdapter(11111).id("udpIn"))
            .<byte[], String>transform(p -> new String(p).toUpperCase())
            .handle(Udp.outboundAdapter("headers['ip_packetAddress']")
                    .socketExpression("@udpIn.socket"))
            .get();
}