使用通道和网关的Spring Integration DSL中的Echo套接字服务

时间:2019-03-24 23:20:07

标签: java spring-integration spring-integration-dsl spring-integration-ip

这是我的问题How to implement simple echo socket service in Spring Integration DSL的变体。介绍了一个好的工作解决方案,但我想探索替代方案。我特别对基于客户端和服务器实现中显式使用入站和出站通道的解决方案感兴趣。有可能吗?

到目前为止,我能够提出:

HeartbeatClientConfig

...
@Bean
public IntegrationFlow heartbeatClientFlow(
        TcpNetClientConnectionFactory clientConnectionFactory,
        MessageChannel outboundChannel,
        PollableChannel inboundChannel) {
    return IntegrationFlows
            .from(outboundChannel)
            .handle(Tcp.outboundGateway(clientConnectionFactory))
            .channel(inboundChannel)
            .get();
}
...

HeartbeatClient

public HeartbeatClient(MessageChannel outboudChannel, PollableChannel inboundChannel) {
    this.inboundChannel = inboundChannel;
    this.outboudChannel = outboudChannel;
}
...
void run() {
    // ..in scheduled intervals in loop 
    outboudChannel.send(new GenericMessage<String>("status"));
    Message<?> message = inboundChannel.receive(1000);
}

客户端部分工作正常。问题出在服务器端。

HeartbeatServer

public HeartbeatServer(PollableChannel inboundChannel, MessageChannel outboudChannel) {
    this.inboundChannel = inboundChannel;
    this.outboudChannel = outboudChannel;
}
...
void run() {
    // ..in some kind of loop
    Message<?> message = inboundChannel.receive(1000); // presumably a blocking call
    ...
    outboudChannel.send(new GenericMessage<>("OK"));
    ...
}

HeartbeatServerConfig
这是最棘手的部分,我确定我错了。我只是不知道该怎么办。在这里,我天真地使用了客户端实现中似乎可行的逆方法。在Flow定义中切换入站和出站通道的意义上相反。

...
@Bean
public IntegrationFlow heartbeatServerFlow(
        MessageChannel outboundChannel,
        PollableChannel inboundChannel) {
    return IntegrationFlows
            .from(inboundChannel)
            .handle(Tcp.inboundGateway(Tcp.netServer(7777)))
            .channel(outboundChannel)
            .get();
}
...

服务器无法正常工作,引发关于Found ambiguous parameter type [class java.lang.Boolean] for method match ...的神秘异常,后跟一长串Spring和Spring Integration方法。

Full source code can be found here

2 个答案:

答案 0 :(得分:1)

您不能使用通道启动服务器端流。

流程从网关开始;它处理所有套接字通信。当收到消息时,它将其发送到频道。

您可以这样做...

 <?xml version="1.0" encoding="utf-8"?>
 <FrameLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".CategoryFragment">

  <android.support.v7.widget.RecyclerView
    android:id="@+id/listCategory"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
  </android.support.v7.widget.RecyclerView>

  </FrameLayout>

但是我想问你为什么要这么做,因为现在你必须管理自己的线程才能从请求通道接收并写入回复通道。为了使其正常工作,必须将请求消息中的@Bean public IntegrationFlow server(PollableChannel requests, MessageChannel replies) { return IntegrationFlows.from(Tcp.inboundGateway(Tcp.netServer(1234)) .replyChannel(replies)) .transform(Transformers.objectToString()) .channel(requests) .get(); } 标头复制到回复消息中。实际上,您实际上并不需要回复渠道;您可以直接将回复发送到replyChannel标头(这是内部情况,我们将回复通道桥接到标头通道)。

在网关线程上处理请求要简单得多。

答案 1 :(得分:0)

仅是对Gary的完美回答的补充,如果有人感兴趣,这是完整的代码。

我必须明确指定TcpNetServerConnectionFactory,才能将ByteArrayLengthHeaderSerializer设置为序列化器/反序列化器。没有它是行不通的。

HeartbeatServerConfig full code

@Bean
public TcpNetServerConnectionFactory connectionFactory() {
    TcpNetServerConnectionFactory connectionFactory = new TcpNetServerConnectionFactory(7777);
    connectionFactory.setSerializer(new ByteArrayLengthHeaderSerializer());
    connectionFactory.setDeserializer(new ByteArrayLengthHeaderSerializer());
    return connectionFactory;
}

@Bean
public IntegrationFlow heartbeatServerFlow(
        TcpNetServerConnectionFactory connectionFactory,
        PollableChannel inboundChannel, 
        MessageChannel outboundChannel) {
    return IntegrationFlows.from(Tcp.inboundGateway(connectionFactory)
            .replyChannel(outboundChannel))
            .channel(inboundChannel)
            .get();
}

HeartbeatServer full code

public void start() {
    Executors.newSingleThreadExecutor().execute(() -> {
        while (true) {
            try {
                Message<?> request = inboundChannel.receive();
                if (request == null) {
                    log.error("Heartbeat timeouted");
                } else {
                    MessageChannel outboudChannel = (MessageChannel)request.getHeaders().getReplyChannel();
                    String requestPayload = new String((byte[]) request.getPayload());
                    if (requestPayload.equals("status")) {
                        log.info("Heartbeat received");
                        outboudChannel.send(new GenericMessage<>("OK"));
                    } else {
                        log.error("Unexpected message content from client: " + requestPayload);
                    }
                }
            } catch (Exception e) {
                log.error(e);
            }
        }
    });
}

关键当然是从请求消息本身获取出站通道为:MessageChannel outboudChannel = (MessageChannel)request.getHeaders().getReplyChannel()

完整代码可在此处找到。