简体   繁体   English

使用 Spring 集成 DSL,如何配置不同集成之间的 I/O?

[英]Using Spring Integration DSL, how do I configure I/O between different integrations?

I have a Spring Boot application, and I'm trying to connect two different Spring Integration services together.我有一个 Spring 引导应用程序,我正在尝试将两个不同的 Spring 集成服务连接在一起。 I have a WebSocket listener, and a TCP listener defined using the available DSL examples for those two integrations.我有一个 WebSocket 侦听器和一个 TCP 侦听器,使用这两个集成的可用 DSL 示例定义。

However, I have been unable to find documentation on how to bridge them together.但是,我一直无法找到有关如何将它们连接在一起的文档。 In other words, when a WebSocket connection is opened by a browser client, and an incoming TCP connection is accepted from another system, I simply want to blindly pass all input and output from one to the other, in a bidirectional capacity.换句话说,当浏览器客户端打开 WebSocket 连接,并从另一个系统接受传入的 TCP 连接时,我只是想盲目地将所有输入和 Z78E6221F6393D1356681DB398F14CED6 双向传递。 Think of it as a simple telnet bridge between two protocols, using my Spring Boot app as a bridge.将其视为两个协议之间的简单 telnet 桥,使用我的 Spring Boot 应用程序作为桥。

It seems like this sort of direct linking should be very simple with the Spring Integration DSL, but I can't even find an example of bridging multiple connections even with the annotation interface.使用 Spring 集成 DSL 似乎这种直接链接应该非常简单,但即使使用注释接口,我什至找不到桥接多个连接的示例。 Whenever I send a message from either interface, I see a "Dispatcher has no subscribers" error.每当我从任一界面发送消息时,我都会看到“调度程序没有订阅者”错误。

Am I overcomplicating this, or do I need to write some business logic code to do bidirectional forwarding?我是否过于复杂了,还是我需要编写一些业务逻辑代码来进行双向转发?

Here is a simple TCP<->TCP bridge...这是一个简单的 TCP<->TCP 桥...

@SpringBootApplication
public class So66993561Application {

    public static void main(String[] args) {
        SpringApplication.run(So66993561Application.class, args);
    }

    @Bean
    AbstractServerConnectionFactory server() {
        return Tcp.netServer(1234)
                .deserializer(TcpCodecs.lf())
                .serializer(TcpCodecs.lf())
                .get();
    }

    @Bean
    AbstractClientConnectionFactory client() {
        return Tcp.netClient("localhost", 1235)
                .deserializer(TcpCodecs.lf())
                .serializer(TcpCodecs.lf())
                .get();
    }

    @Bean
    IntegrationFlow serverInbound(AbstractServerConnectionFactory server) {
        return IntegrationFlows.from(Tcp.inboundAdapter(server))
                .transform(Transformers.objectToString())
                .handle("service", "sendToNCL")
                .get();
    }

    @Bean
    IntegrationFlow serverOutbound(AbstractServerConnectionFactory server) {
        return IntegrationFlows.from(ServerGateway.class)
                .handle(Tcp.outboundAdapter(server))
                .get();
    }

    @Bean
    IntegrationFlow clientInbound(AbstractClientConnectionFactory client) {
        return IntegrationFlows.from(Tcp.inboundAdapter(client))
                .transform(Transformers.objectToString())
                .handle("service", "broadcastToClients")
                .get();
    }

    @Bean
    IntegrationFlow clientOutbound(AbstractClientConnectionFactory client) {
        return IntegrationFlows.from(ClientGateway.class)
                .handle(Tcp.outboundAdapter(client))
                .get();
    }

    @Bean
    @DependsOn("clientOutbound")
    public ApplicationRunner runner(AbstractClientConnectionFactory client) {
        return args -> {
            System.out.println("Hit enter when netcat is running");
            System.in.read(); // nc -l 1235
            client.getConnection(); // just open the connection.
        };
    }

}

interface ServerGateway {

    @Gateway(payloadExpression = "#args[0]",
            headers = @GatewayHeader(name = IpHeaders.CONNECTION_ID, expression = "#args[1]" ))
    void send(String out, String connectionId);

}

interface ClientGateway {

    void send(String out);

}

@Component
@DependsOn({ "clientOutbound", "serverOutbound" })
class Service {

    private final Set<String> serverClients = ConcurrentHashMap.newKeySet();

    private final ClientGateway client;

    private final ServerGateway server;

    private volatile boolean clientOpen;

    public Service(ClientGateway client, ServerGateway server) {
        this.client = client;
        this.server = server;
    }

    public void broadcastToClients(String in) {
        System.out.println("received from server: " + in);
        this.serverClients.forEach(client -> {
            try {
                this.server.send(in, client);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    public void sendToNCL(String in) {
        System.out.println("received from a client: " + in);
        if (this.clientOpen) {
            try {
                this.client.send(in);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @EventListener
    public void opens(TcpConnectionOpenEvent event) {
        System.out.println(event);
        if (event.getConnectionFactoryName().equals("server")) {
            this.serverClients.add(event.getConnectionId());
        }
        else {
            this.clientOpen = true;
        }
    }

    @EventListener
    public void closes(TcpConnectionCloseEvent event) {
        System.out.println(event);
        this.serverClients.remove(event.getConnectionId());
        if (event.getConnectionFactoryName().equals("client")) {
            this.clientOpen = false;
        }
    }

}
$ nc -l 1235
foo <<<<<<<<<<<<<<<<< received
bar <<<<<<<<<<<<<<<<< sent
$ nc localhost 1234
foo <<<<<<<<<<<<<<<<< sent
bar <<<<<<<<<<<<<<<<< received

EDIT编辑

Adding websocket server...添加websocket服务器...

@SpringBootApplication
public class So66993561Application {

    public static void main(String[] args) {
        SpringApplication.run(So66993561Application.class, args);
    }

    @Bean
    AbstractServerConnectionFactory server() {
        return Tcp.netServer(1234)
                .deserializer(TcpCodecs.lf())
                .serializer(TcpCodecs.lf())
                .get();
    }

    @Bean
    AbstractClientConnectionFactory client() {
        return Tcp.netClient("localhost", 1235)
                .deserializer(TcpCodecs.lf())
                .serializer(TcpCodecs.lf())
                .get();
    }

    @Bean
    IntegrationFlow serverInbound(AbstractServerConnectionFactory server) {
        return IntegrationFlows.from(Tcp.inboundAdapter(server))
                .transform(Transformers.objectToString())
                .handle("service", "sendToNCL")
                .get();
    }

    @Bean
    IntegrationFlow serverOutbound(AbstractServerConnectionFactory server) {
        return IntegrationFlows.from(ServerGateway.class)
                .handle(Tcp.outboundAdapter(server))
                .get();
    }

    @Bean
    IntegrationFlow clientInbound(AbstractClientConnectionFactory client) {
        return IntegrationFlows.from(Tcp.inboundAdapter(client))
                .transform(Transformers.objectToString())
                .handle("service", "broadcastToClients")
                .get();
    }

    @Bean
    IntegrationFlow clientOutbound(AbstractClientConnectionFactory client) {
        return IntegrationFlows.from(ClientGateway.class)
                .handle(Tcp.outboundAdapter(client))
                .get();
    }

    @Bean
    @DependsOn("clientOutbound")
    ApplicationRunner runner(AbstractClientConnectionFactory client) {
        return args -> {
            System.out.println("Hit enter when netcat is running");
            System.in.read(); // nc -l 1235
            client.getConnection(); // just open the connection.
        };
    }

    @Bean
    ServerWebSocketContainer serverWebSocketContainer() {
        return new ServerWebSocketContainer("/relay").withSockJs();
    }

    @Bean
    MessageHandler webSocketOutboundAdapter(ServerWebSocketContainer container) {
        return new WebSocketOutboundMessageHandler(container);
    }

    @Bean
    WebSocketInboundChannelAdapter wsInboundAdapter(ServerWebSocketContainer container) {
        return new WebSocketInboundChannelAdapter(container);
    }

    @Bean
    IntegrationFlow wsInbound(WebSocketInboundChannelAdapter wsInboundAdapter) {
        return IntegrationFlows.from(wsInboundAdapter)
                .handle("service", "sendToNCL")
                .get();
    }

    @Bean
    public IntegrationFlow wsOutbound(MessageHandler webSocketOutboundAdapter) {
        return IntegrationFlows.from(WsServerGateway.class)
                .handle(webSocketOutboundAdapter)
                .get();
    }

}

interface ServerGateway {

    @Gateway(payloadExpression = "#args[0]",
            headers = @GatewayHeader(name = IpHeaders.CONNECTION_ID, expression = "#args[1]" ))
    void send(String out, String sessionId);

}

interface ClientGateway {

    void send(String out);

}

interface WsServerGateway {

    @Gateway(payloadExpression = "#args[0]",
            headers = @GatewayHeader(name = SimpMessageHeaderAccessor.SESSION_ID_HEADER, expression = "#args[1]" ))
    void send(String out, String connectionId);

}

@Component
@DependsOn({ "clientOutbound", "serverOutbound", "wsOutbound" })
class Service {

    private final Set<String> serverClients = ConcurrentHashMap.newKeySet();

    private final ClientGateway client;

    private final ServerGateway server;

    private final WsServerGateway wsServer;

    private final ServerWebSocketContainer wsContainer;

    private volatile boolean clientOpen;

    public Service(ClientGateway client, ServerGateway server, WsServerGateway wsServer,
            ServerWebSocketContainer wsContainer) {

        this.client = client;
        this.server = server;
        this.wsServer = wsServer;
        this.wsContainer = wsContainer;
    }

    public void broadcastToClients(String in) {
        System.out.println("received from server: " + in);
        this.serverClients.forEach(client -> {
            try {
                this.server.send(in, client);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        });
        this.wsContainer.getSessions().keySet().forEach(session -> this.wsServer.send(in, session));
    }

    public void sendToNCL(String in) {
        System.out.println("received from a client: " + in);
        if (this.clientOpen) {
            try {
                this.client.send(in);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @EventListener
    public void opens(TcpConnectionOpenEvent event) {
        System.out.println(event);
        if (event.getConnectionFactoryName().equals("server")) {
            this.serverClients.add(event.getConnectionId());
        }
        else {
            this.clientOpen = true;
        }
    }

    @EventListener
    public void closes(TcpConnectionCloseEvent event) {
        System.out.println(event);
        this.serverClients.remove(event.getConnectionId());
        if (event.getConnectionFactoryName().equals("client")) {
            this.clientOpen = false;
        }
    }

}
$ websocat ws://localhost:8080/relay/websocket
foo <<<<<<<<<<<<<<<< received from nc -l connection
baz <<<<<<<<<<<<<<<< sent to nc -l connection

See https://github.com/spring-projects/spring-integration/blob/main/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/server/WebSocketServerTests.java for a sample how to configure WebScoket components.有关示例,请参见https://github.com/spring-projects/spring-integration/blob/main/spring-integration-websocket/src/test/java/org/springframework/integration/websocket/server/WebSocketServerTests.java配置 WebScket 组件。

Since you talk about a browser connection, you probably need a ServerConfig part to accept connections and forward messages to some webSocketInputChannel .由于您谈论的是浏览器连接,因此您可能需要一个ServerConfig部件来接受连接并将消息转发到某些webSocketInputChannel

The TcpSendingMessageHandler could be a subscriber to that channel to bridge the data into a target TCP/IP connection. TcpSendingMessageHandler可以是该通道的订阅者,以将数据桥接到目标 TCP/IP 连接。

A TcpReceivingChannelAdapter with the same TcpNetClientConnectionFactory could be used to receive a data from a TCP/IP connection and sends it to the webSocketOutputChannel for forwarding to the WebSocket session.具有相同TcpReceivingChannelAdapterTcpNetClientConnectionFactory可用于从 TCP/IP 连接接收数据并将其发送到webSocketOutputChannel以转发到 WebSocket session。

Keep in mind though, that simpSessionId header need to be transferred from the WebSocket request message to the TCP package and back for the future correlation when you are going to receive messages from TCP. Keep in mind though, that simpSessionId header need to be transferred from the WebSocket request message to the TCP package and back for the future correlation when you are going to receive messages from TCP. Since there is no headers notation in TCP, you would need to think about some sort of data wrapper to embed headers alongside with the payload into TCP/IP package.由于 TCP 中没有标头符号,因此您需要考虑某种数据包装器以将标头与有效负载一起嵌入 TCP/IP package。

See more info about async collaborating channel adapters in docs: https://docs.spring.io/spring-integration/reference/html/ip.html#ip-collaborating-adapters .在文档中查看有关异步协作通道适配器的更多信息: https://docs.spring.io/spring-integration/reference/html/ip.html#ip-collaborating-adapters And the next section about transferring headers.以及关于传输标头的下一部分。

This sample also provides some useful info about correlation and multiplexing with TCP/IP channel adapters: https://github.com/spring-projects/spring-integration-samples/tree/main/intermediate/tcp-client-server-multiplex此示例还提供了一些有关与 TCP/IP 通道适配器的关联和多路复用的有用信息: https://github.com/spring-projects/spring-integration-samples/tree/main/intermediate/tcp-client-server-multiplex

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用Spring Integration DSL进行Ftp轮询 - how to do Ftp polling with Spring integration DSL 在Spring集成中,如何捕获不同的异常? - In spring integration, how do I catching different exceptions? 如何更正“spring 集成是一种单向‘MessageHandler’,不适合配置‘outputChannel’”异常? - How do I correct the "spring integration is a one-way 'MessageHandler' and it isn't appropriate to configure 'outputChannel'" exception? 如何配置带有身份验证的嵌入式MongoDB,以用于Spring Boot集成测试? - How do I configure embedded MongoDB with authentication for use in spring boot integration tests? 如何缩短 Spring 数据存储库方法的查询 DSL? - How do I shorten this query DSL for Spring Data repository method? 如何使用注解将Spring安全性配置为在处女座上工作? - How do I configure Spring security to work on Virgo - using annotations? 如何使用EclipseLink和Spring配置动态编织? - How do I configure dynamic weaving using EclipseLink & Spring? 如何在Spring集成中使用Java DSL创建ws inbound-gateway? - How can I create ws inbound-gateway with Java DSL in Spring integration? 如何测试 Spring 集成变压器? - How do I test a Spring Integration Transformer? 如何使用spring DSL在camel中记录标头值 - How can I log a header value in camel using spring DSL
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM