How to implement simple echo socket service in Spring Integration DSL

后端 未结 2 1658
忘掉有多难
忘掉有多难 2020-12-22 11:19

Please,
could you help with implementation of a simple, echo style, Heartbeat TCP socket service in Spring Integration DSL? More precisely how to plug Adapter/Handler/Ga

相关标签:
2条回答
  • 2020-12-22 11:50

    It's much simpler with the DSL...

    @SpringBootApplication
    @EnableScheduling
    public class So55154418Application {
    
        public static void main(String[] args) {
            SpringApplication.run(So55154418Application.class, args);
        }
    
        @Bean
        public IntegrationFlow server() {
            return IntegrationFlows.from(Tcp.inboundGateway(Tcp.netServer(1234)))
                    .transform(Transformers.objectToString())
                    .log()
                    .handle((p, h) -> "OK")
                    .get();
        }
    
        @Bean
        public IntegrationFlow client() {
            return IntegrationFlows.from(Gate.class)
                    .handle(Tcp.outboundGateway(Tcp.netClient("localhost", 1234)))
                    .transform(Transformers.objectToString())
                    .handle((p, h) -> {
                        System.out.println("Received:" + p);
                        return null;
                    })
                    .get();
        }
    
        @Bean
        @DependsOn("client")
        public Runner runner(Gate gateway) {
            return new Runner(gateway);
        }
    
        public static class Runner {
    
            private final Gate gateway;
    
            public Runner(Gate gateway) {
                this.gateway = gateway;
            }
    
            @Scheduled(fixedDelay = 5000)
            public void run() {
                this.gateway.send("foo");
            }
    
        }
    
        public interface Gate {
    
            void send(String out);
    
        }
    
    }
    

    Or, get the reply from the Gate method...

        @Bean
        public IntegrationFlow client() {
            return IntegrationFlows.from(Gate.class)
                    .handle(Tcp.outboundGateway(Tcp.netClient("localhost", 1234)))
                    .transform(Transformers.objectToString())
                    .get();
        }
    
        @Bean
        @DependsOn("client")
        public Runner runner(Gate gateway) {
            return new Runner(gateway);
        }
    
        public static class Runner {
    
            private final Gate gateway;
    
            public Runner(Gate gateway) {
                this.gateway = gateway;
            }
    
            @Scheduled(fixedDelay = 5000)
            public void run() {
                String reply = this.gateway.sendAndReceive("foo"); // null for timeout
                System.out.println("Received:" + reply);
            }
    
        }
    
        public interface Gate {
    
            @Gateway(replyTimeout = 5000)
            String sendAndReceive(String out);
    
        }
    

    Bonus:

    Consuming endpoints are actually comprised of 2 beans; a consumer and a message handler. The channel goes on the consumer. See here.

    EDIT

    An alternative, for a single bean for the client...

    @Bean
    public IntegrationFlow client() {
        return IntegrationFlows.from(() -> "foo", 
                        e -> e.poller(Pollers.fixedDelay(Duration.ofSeconds(5))))
                .handle(Tcp.outboundGateway(Tcp.netClient("localhost", 1234)))
                .transform(Transformers.objectToString())
                .handle((p, h) -> {
                    System.out.println("Received:" + p);
                    return null;
                })
                .get();
    }
    
    0 讨论(0)
  • 2020-12-22 12:09

    For anyone interested, here is one of the working solutions I made with help from Gary Russell. All credits to Gary Russell. Full project source code here.

    Highlights:

    • IntegrationFlows: Use only inbound and outbound Gateways.
    • No Adapters or Channels needed; no ServiceActivators or Message Gate proxies.
    • No need for ScheduledExecutor or Executors; client and server code got significatn
    • IntegrationFlows directly calls methods on client class and server class; I like this type of explicit connection.
    • Split client class on two parts, two methods: request producing part and response processing part; this way it can be better chained to flows.
    • explicitly define clientConnectionFactory/serverConnectionFactory. This way more things can be explicitly configured later.

    HeartbeatClientConfig

    @Bean
    public IntegrationFlow heartbeatClientFlow(
            TcpNetClientConnectionFactory clientConnectionFactory,
            HeartbeatClient heartbeatClient) {
        return IntegrationFlows.from(heartbeatClient::send,  e -> e.poller(Pollers.fixedDelay(Duration.ofSeconds(5))))
                .handle(Tcp.outboundGateway(clientConnectionFactory))
                .handle(heartbeatClient::receive)
                .get();
    }
    

    HeartbeatClient

    public class HeartbeatClient {
        private final Logger log = LogManager.getLogger(HeartbeatClient.class);
    
        public GenericMessage<String> send() {
            log.info("Sending Heartbeat");
            return new GenericMessage<String>("status");
        }
    
        public Object receive(byte[] payload, MessageHeaders messageHeaders) { // LATER: use transformer() to receive String here
            String messageStr = new String(payload);
            if (messageStr.equals("OK")) {
                log.info("Heartbeat OK response received");
            } else {
                log.error("Unexpected message content from server: " + messageStr);
            }
            return null;
        }
    }
    

    HeartbeatServerConfig

    @Bean
    public IntegrationFlow heartbeatServerFlow(
            TcpNetServerConnectionFactory serverConnectionFactory,
            HeartbeatServer heartbeatServer) {
        return IntegrationFlows
                .from(Tcp.inboundGateway(serverConnectionFactory))
                .handle(heartbeatServer::processRequest)
                .get();
    }
    

    HeartbeatServer

    public class HeartbeatServer {
        private final Logger log = LogManager.getLogger(HeartbeatServer.class);
    
        public Message<String> processRequest(byte[] payload, MessageHeaders messageHeaders) {
            String messageStr = new String(payload);
            if (messageStr.equals("status")) {
                log.info("Heartbeat received");
                return new GenericMessage<>("OK");
            } else {
                log.error("Unexpected message content from client: " + messageStr);
                return null;
            }
    
        }
    }
    
    0 讨论(0)
提交回复
热议问题