Chain of Responsibility in Netty
Design Patterns-Responsibility Chain Patterns
Chain of Responsibility Pattern is a behavioral design pattern that creates a chain of processing objects for requests. Each node in the chain is regarded as an object. Each node handles different requests and maintains an object of the next node automatically. When a request is sent from the top of the chain, it is passed to each node object in turn along the path of the chain until an object processes the request.
The responsibility chain model mainly solves the decoupling of the process of initiating and processing requests. The handlers in the responsibility chain are responsible for processing requests. Users only need to send requests to the responsibility chain, without concern for the details of processing requests and the transmission of requests.
Realization of Responsibility Chain Model
The implementation of the responsibility chain pattern has four main elements: processor Abstract class, specific processor implementation class, preservation of processor information, processing and execution.
Pseudo-code example:
// *** Collective form stores filters similar to those in tomcat *** // Processor abstract class class AbstractHandler{void doHandler(Object arg0)} // Processor Specific Implementation Class class Handler1 extends AbstractHandler{assert coutine;} class Handler2 extends AbstractHandler{assert coutine;} class Handler3 extends AbstractHandler{assert coutine;} // Create a collection and store all processor instance information List handlers = new List(); handlers.add(handler1, handler2, handler3); // Processing requests, calling processors void process(request){ for(handler in handlers){ handler.doHandler(request); } } // Initiate request calls and process requests through chains of responsibility call.process(request); // *** Linked List Form Call Usage in netty*** // Processor abstract class class AbstractHandler{ AbstractHandler next;//Next node void doHandler(Object arg0) } // Processor Specific Implementation Class class Handler1 extends AbstractHandler{assert coutine;} class Handler2 extends AbstractHandler{assert coutine;} class Handler3 extends AbstractHandler{assert coutine;} // Cascade processors into linked lists for storage pipeline = head[handler1 -> handler2 -> handler3]tail; // Processing requests, calling processors from beginning to end void process(request){ handler = pipeline.findOne; //find first while(handler != null){ handler.doHandler(request); handler = handler.next; } }
Specific code examples of responsibility chain implementation in the form of linked lists:
// Use of Linked List Form Call in netty public class PipelineDemo { // Create a head for initialization as the beginning of the chain of responsibility, but there is no specific treatment. public HandlerChainContext head = new HandlerChainContext( new AbstractHandler() { @Override void doHandler(HandlerChainContext handlerChainContext, Object arg0) { handlerChainContext.runNext(arg0); } }); public void processRequest(Object arg0) { this.head.handler(arg0); } public void addLast(AbstractHandler handler) { HandlerChainContext context = head; while (context.next != null) { context = context.next; } context.next = new HandlerChainContext(handler); } public static void main(String[] args) { PipelineDemo pipelineChainDemo = new PipelineDemo(); pipelineChainDemo.addLast(new Handler2()); pipelineChainDemo.addLast(new Handler1()); pipelineChainDemo.addLast(new Handler1()); pipelineChainDemo.addLast(new Handler2()); // Initiate request pipelineChainDemo.processRequest("Train whine~~"); } } // handler context, I am primarily responsible for maintaining chains, and chain execution class HandlerChainContext { HandlerChainContext next; // Next node AbstractHandler handler; public HandlerChainContext(AbstractHandler handler) { this.handler = handler; } void handler(Object arg0) { this.handler.doHandler(this, arg0); } // Continue with the next void runNext(Object arg0) { if (this.next != null) { this.next.handler(arg0); } } } // Processor abstract class abstract class AbstractHandler { // processor abstract void doHandler(HandlerChainContext handlerChainContext, Object arg0); } // Processor Specific Implementation Class class Handler1 extends AbstractHandler { @Override void doHandler(HandlerChainContext handlerChainContext, Object arg0) { arg0 = arg0.toString() + "..handler1 Little tail....."; System.out.println("I am Handler1 For example, I am dealing with:" + arg0); // Continue with the next handlerChainContext.runNext(arg0); } } // Processor Specific Implementation Class class Handler2 extends AbstractHandler { @Override void doHandler(HandlerChainContext handlerChainContext, Object arg0) { arg0 = arg0.toString() + "..handler2 Little tail....."; System.out.println("I am Handler2 For example, I am dealing with:" + arg0); // Continue with the next handlerChainContext.runNext(arg0); } } // Output results: //I'm an example of Handler 2, and I'm dealing with: train whine ~.. Handler 2's small tail.... //I'm an example of Handler 1, and I'm dealing with it: the trains whine ~... the tails of handler 2... the tails of handler 1.... //I'm an example of Handler 1, and I'm dealing with: train whine ~... handler 2's tail... handler 1's tail... handler 1's tail.... //I'm an example of Handler 2. I'm dealing with: the small tail of handler 2... the small tail of handler 1... the small tail of handler 1... the small tail of handler 2....
Channel Pipeline Responsibility Chain in Netty
The pipeline pipeline stores all the processor information of the channel. When creating the channel, a proprietary pipeline is created automatically. Inbound and outbound events call the processors on the pipeline.
Inbound and outbound events
Inbound events: Often referred to as IO threads generating inbound data
(Popular understanding: Events that come up from the bottom of the socket themselves are all inbound)
For example, EventLoop receives the OP_READ event of the selector, and when the inbound processor calls socketChannel.read(ByteBuffer) to receive the data, this will cause the channelRead method in the next Channel Pipeline included in the channel to be invoked.
Outbound events: Often referred to as IO threads performing actual output operations
(Popular understanding: Events that want to take the initiative to operate at the bottom of the socket are all outbound)
For example, when the bind method intentionally requests the server socket to be bound to a given SocketAddress, this will cause the bind method in the next outbound processor included in Channel Pipeline of the channel to be invoked.
handler in Pipeline
ChannelHandler: Used to handle IO events or intercept IO operations and forward them to the next processor in Channel Pipeline. This top-level interface definition function is very weak. When using events, it implements the following two sub-interfaces: Channel InBoundHandler for inbound IO events and Channel OutboundHandler for outbound IO events.
Adapter: For the convenience of development, Netty provides a simple implementation class to avoid all handler s implementing the one-time interface method.
ChannelInBoundHandler Adapter handles inbound IO events.
ChannelOutBoundHandler Adapter handles outbound IO events.
Channel Duplex Handler supports simultaneous processing of inbound and outbound events
ChannelHandlerContext: The object actually stored in Pipeline is not ChannelHandler, but a context object. The handler is wrapped in the context object. Through the ChannelPipeline interaction of the context, events are passed up or down, or pipeline modification is done through the context object.
Channel Pipeline is thread-safe, and ChannelHandler can be added or deleted at any time.
For example, you can insert an encryption handler into the exchange of sensitive information and delete it after the exchange.
General operations, initialization time added, less deletion.
API for managing handler in Pipeline:
Execution Analysis of handler
Analysis of register Inbound Event Processing
Analysis of bind Outbound Event Processing
Analysis of accept inbound event handling
Analysis of read Inbound Event Handling
Summary
Users have one or more channel handlers in the pipeline to accept IO events and request IO operations
A typical server will have the following handlers in each channel's pipeline, but depending on the complexity and characteristics of the protocol and business logic, it may be different:
Protocol Decoder - Converting Binary Data into Java Objects
Protocol Encoder - Converting Java Objects into Binary Data
Business Logic Processor - Executing Actual Business Logic
The application of responsibility chain mode ensures the high scalability of Netty