简体   繁体   中英

How to route using message headers in Spring Integration DSL Tcp

I have 2 server side services and I would like route messages to them using message headers, where remote clients put service identification into field type .

Is the code snippet, from server side config, the correct way? It throws cast exception indicating that route() see only payload, but not the message headers. Also all example in the Spring Integration manual shows only payload based decisioning.

@Bean
public IntegrationFlow serverFlow( // common flow for all my services, currently 2
        TcpNetServerConnectionFactory serverConnectionFactory,
        HeartbeatServer heartbeatServer,
        FeedServer feedServer) {
    return IntegrationFlows
            .from(Tcp.inboundGateway(serverConnectionFactory))
            .<Message<?>, String>route((m) -> m.getHeaders().get("type", String.class),
                (routeSpec) -> routeSpec
                .subFlowMapping("hearbeat", subflow -> subflow.handle(heartbeatServer::processRequest))
                .subFlowMapping("feed", subflow -> subflow.handle(feedServer::consumeFeed)))
            .get();
}

Client side config:

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

@Bean
public IntegrationFlow feedClientFlow(
        TcpNetClientConnectionFactory clientConnectionFactory) {
    return IntegrationFlows.from(FeedClient.MessageGateway.class)
            .enrichHeaders(c -> c.header("type", "feed"))
            .log()
            .handle(outboundGateway(clientConnectionFactory))
            .get();
}

And as usual here is the full demo project code , ClientConfig and ServerConfig .

There is no standard way to send headers over raw TCP. You need to encode them into the payload somehow (and extract them on the server side).

The framework provides a mechanism to do this for you, but it requires extra configuration.

See the documentation .

Specifically...

The MapJsonSerializer uses a Jackson ObjectMapper to convert between a Map and JSON. You can use this serializer in conjunction with a MessageConvertingTcpMessageMapper and a MapMessageConverter to transfer selected headers and the payload in JSON.

I'll try to find some time to create an example of how to use it.

But, of course, you can roll your own encoding/decoding.

EDIT

Here's an example configuration to use JSON to convey message headers over TCP...

@SpringBootApplication
public class TcpWithHeadersApplication {

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

    // Client side

    public interface TcpExchanger {

        public String exchange(String data, @Header("type") String type);

    }

    @Bean
    public IntegrationFlow client(@Value("${tcp.port:1234}") int port) {
        return IntegrationFlows.from(TcpExchanger.class)
                .handle(Tcp.outboundGateway(Tcp.netClient("localhost", port)
                        .deserializer(jsonMapping())
                        .serializer(jsonMapping())
                        .mapper(mapper())))
                .get();
    }

    // Server side

    @Bean
    public IntegrationFlow server(@Value("${tcp.port:1234}") int port) {
        return IntegrationFlows.from(Tcp.inboundGateway(Tcp.netServer(port)
                        .deserializer(jsonMapping())
                        .serializer(jsonMapping())
                        .mapper(mapper())))
                .log(Level.INFO, "exampleLogger", "'Received type header:' + headers['type']")
                .route("headers['type']", r -> r
                        .subFlowMapping("upper",
                                subFlow -> subFlow.transform(String.class, p -> p.toUpperCase()))
                        .subFlowMapping("lower",
                                subFlow -> subFlow.transform(String.class, p -> p.toLowerCase())))
                .get();
    }

    // Common

    @Bean
    public MessageConvertingTcpMessageMapper mapper() {
        MapMessageConverter converter = new MapMessageConverter();
        converter.setHeaderNames("type");
        return new MessageConvertingTcpMessageMapper(converter);
    }

    @Bean
    public MapJsonSerializer jsonMapping() {
        return new MapJsonSerializer();
    }

    // Console

    @Bean
    @DependsOn("client")
    public ApplicationRunner runner(TcpExchanger exchanger,
            ConfigurableApplicationContext context) {

        return args -> {
            System.out.println("Enter some text; if it starts with a lower case character,\n"
                    + "it will be uppercased by the server; otherwise it will be lowercased;\n"
                    + "enter 'quit' to end");
            Scanner scanner = new Scanner(System.in);
            String request = scanner.nextLine();
            while (!"quit".equals(request.toLowerCase())) {
                if (StringUtils.hasText(request)) {
                    String result = exchanger.exchange(request,
                            Character.isLowerCase(request.charAt(0)) ? "upper" : "lower");
                    System.out.println(result);
                }
                request = scanner.nextLine();
            }
            scanner.close();
            context.close();
        };
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM