Java Network Programming--Chain of Responsibility in Netty

Keywords: Java Netty socket Tomcat

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

Posted by coldwerturkey on Thu, 26 Sep 2019 06:54:01 -0700