Netty synchronous client with asynchronous callers

前端 未结 1 1670
萌比男神i
萌比男神i 2020-12-17 05:40

I am creating a server which consumes commands from numerous sources such as JMS, SNMP, HTTP etc. These are all asynchronous and are working fine. The server maintains a sin

1条回答
  •  心在旅途
    2020-12-17 05:52

    Instead of designing your application on a blocking manner using SynchronousQueue, design it in a nonblocking manner using SynchronousQueue>.

    Your public Future issueCommandToLegacyHardware(Command command) should then use offer() to add a DefaultPromise<>() to the Queue, and then the netty pipeline can use remove() to get the response for that request, notice I used remove() instead of take(), since only under exceptional circumstances, there is none element present.

    A quick implementation of this might be:

    public class MyLastHandler extends SimpleInboundHandler {
        private final SynchronousQueue> queue;
    
        public MyLastHandler (SynchronousQueue> queue) {
            super();
            this.queue = queue;
        }
    
        // The following is called messageReceived(ChannelHandlerContext, Response) in 5.0.
        @Override
        public void channelRead0(ChannelHandlerContext ctx, Response msg) {
            this.queue.remove().setSuccss(msg); // Or setFailure(Throwable)
        }
    }
    

    The above handler should be placed last in the chain.

    The implementation of public Future issueCommandToLegacyHardware(Command command) can look:

    Channel channel = ....;
    SynchronousQueue> queue = ....;
    
    public Future issueCommandToLegacyHardware(Command command) {
        return issueCommandToLegacyHardware(command, channel.eventLoop().newPromise());
    }
    
    public Future issueCommandToLegacyHardware(Command command, Promise promise) {
        queue.offer(promise);
        channel.write(command);
        return promise;
    }
    

    Using the approach with the overload on issueCommandToLegacyHardware is also the design pattern used for Channel.write, this makes it really flexable.

    This design pattern can be used as follows in client code:

    issueCommandToLegacyHardware(
        Command.TAKE_OVER_THE_WORLD_WITH_FIRE, 
        channel.eventLoop().newPromise()
    ).addListener(
        (Future f) -> {
            System.out.println("We have taken over the world: " + f.get());
        }
    );
    

    The advantage of this design pattern is that no unneeded blocking is used anywhere, just plain async logic.

    Appendix I: Javadoc:

    Promise Future DefaultPromise

    0 讨论(0)
提交回复
热议问题