Search code examples
nettynio

How to design Netty Server for multiple output type


As in http we can implement multiple urls to perform different actions. How can we achieve the same thing with a Netty Server? More explicitly, I have to output lets say four types of google protobuf from the netty server depending on the request (which are also of 4 types). Should I create separate Netty Server for each request type or should I have different handlers in the same pipeline? In the later case I would have to have atleast 4*3 = 12 handlers (For every request type an inbound protobuf handler, an outbound protobuf handler and a business logic handler). Is it a good design?


Solution

  • There are a few different design options with different trade-offs to consider.

    1. Server per request/response type. Each call with a distinct request/response type is handled by its own dedicated Netty server. Fine-grained servers like this would fit a microservices architecture, and in general, you'd likely inherit the pros and cons of that architecture. Good build and deployment automation are pre-requisities to make this successful. Otherwise, you'll end up with extra work building, deploying and configuring multiple servers.
    2. Handler per request/response type. Run a single Netty server with distinct handlers per request/response type. The multiple handlers are decoupled from one another, which can make long-term maintenance easier. You might consider setting up your own abstract base class implementing ChannelHandler if you want some common logic across all handlers, such as common error handling. Then, all of the specific handlers would subclass that. As you have noted, there is a potential downside in that it leads to a proliferation of many classes, which might harm readability for people unfamiliar with the codebase.
    3. Single handler for multiple request/response types. You could write a single Netty handler for all request/response types. Internally, it would need to introspect on the request type (using instanceof or some kind of type descriminator field within the request object) and then dispatch to the appropriate logic for that request. This fits the front controller pattern. Compared to multiple handlers, this avoids the proliferation of multiple classes. The potential downside is that the dispatch logic can turn into a monolithic nested if-else or switch-case structure that must be maintained carefully every time new request/response types are added.

    In my experience, I've had success with both options 2 and 3, depending on the number of distinct request/response types in the API. For a relatively small number, a single handler works well, and the dispatch logic doesn't become too cumbersome to maintain. For a wide API footprint, it starts to become helpful to organize it into distinct handlers. There also can be hybrid approaches that still use multiple handlers, but each handler covers a group of multiple related request/response types.

    A micro-services architecture like option 1 can be attractive if you can get the sophistication in the build and deployment tools. If you can't invest in that tooling though, then it might just generate a lot of extra work.