Preface
Last article, we Netty
made a basic overview, know what Netty
and Netty
simple applications.
Netty source code analysis series (1) Netty overview
In this article, we will talk Netty
the architecture design of 0611472991822d. Before learning a framework, we must first understand its design principles, and then conduct in-depth analysis.
Next, we analyze the architecture design of Netty from three aspects.
Selector model
Java NIO
is based on the Selector model to achieve non-blocking I/O
. The bottom layer of Netty is based on Java NIO
, so the Selector model is also used.
Selector
model solves the traditional blocking I/O programming problem of one client and one thread. Selector provides a mechanism for monitoring one or more NIO channels and identifying when one or more NIO channels can be used for data transmission. In this way, one thread can manage multiple channels, thereby managing multiple network connections.
Selector
provides the ability to select and execute tasks that are ready. From a bottom-level perspective, the Selector polls whether the Channel is ready to perform each I/O operation. Selector allows a single thread to process multiple Channels. Selector is a multiplexing technology.
SelectableChannel
Not all Channels can be reused by Selector, only subclasses of abstract class SelectableChannel
can be reused by Selector.
For example, FileChannel
cannot be reused by the selector because FileChannel
is not a subclass of SelectableChannel
In order to be used with Selector, SelectableChannel
must first register an instance of this class register
This method returns a new SelectionKey
object, which indicates that Channel
has been registered Selector
Selector
registering to Channel
will remain registered until it is cancelled.
A Channel can be registered with any specific Selector at most once, but the same Channel can be registered to multiple Selectors. You can call the isRegistered
method to determine whether a Channel is registered with one or more Selectors.
SelectableChannel
can be safely used by multiple concurrent threads.
Channel registered to Selector
Use SelectableChannel
of register
method can be Channel
registered to Selector
. The method interface source code is as follows:
public final SelectionKey register(Selector sel, int ops)
throws ClosedChannelException
{
return register(sel, ops, null);
}
public abstract SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException;
The description of each option is as follows:
sel
: SpecifyChannel
to register forSelector
.ops
: Specify the operation of the channel to be queried forSelector
A Channel registered in the Selector represents a SelectionKey
event. The types of SelectionKey
OP_READ
: Readable event; value:1<<0
OP_WRITE
: writable event; value:1<<2
OP_CONNECT
: The event of the client connecting to the server (tcp connection), generally to create theSocketChannel
client channel; the value is:1<<3
OP_ACCEPT
: The server receives the client connection event, usually to create theServerSocketChannel
server channel; the value is:1<<4
The specific registration code is as follows:
// 1.创建通道管理器(Selector)
Selector selector = Selector.open();
// 2.创建通道ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 3.channel要注册到Selector上就必须是非阻塞的,所以FileChannel是不可以使用Selector的,因为FileChannel是阻塞的
serverSocketChannel.configureBlocking(false);
// 4.第二个参数指定了我们对 Channel 的什么类型的事件感兴趣
SelectionKey key = serverSocketChannel.register(selector , SelectionKey.OP_READ);
// 也可以使用或运算|来组合多个事件,例如
SelectionKey key = serverSocketChannel.register(selector , SelectionKey.OP_READ | SelectionKey.OP_WRITE);
worth noting: a
Channel
may only be registered to a Selector
once, if Channel
registered to Selector
multiple times, in fact, equivalent to update SelectionKey
of interest set
.
SelectionKey
Channel
relationship between 0611472991864f and Selector
is determined, and once Channel
in a certain ready state, it can be queried by the selector. The work then call Selector
of select
method completes. select
method is to query the ready status of the channel operation of interest.
// 当注册事件到达时,方法返回,否则该方法会一直阻塞
selector.select();
SelectionKey
contains the interest
collection, which represents the selected event collection of interest. The interest collection can be read and written through SelectionKey, for example:
// 返回当前感兴趣的事件列表
int interestSet = key.interestOps();
// 也可通过interestSet判断其中包含的事件
boolean isInterestedInAccept = interestSet & SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;
// 可以通过interestOps(int ops)方法修改事件列表
key.interestOps(interestSet | SelectionKey.OP_WRITE);
It can be seen that by using to operate on the interest set and the given SelectionKey constant, it can be determined whether a certain event is in the interest set.
SelectionKey contains the ready
collection. The ready set is the set of operations for which the channel is ready. After a selection, the ready collection will be accessed first. You can access the ready collection like this:
int readySet = key.readyOps();
// 也可通过四个方法来分别判断不同事件是否就绪
key.isReadable(); //读事件是否就绪
key.isWritable(); //写事件是否就绪
key.isConnectable(); //客户端连接事件是否就绪
key.isAcceptable(); //服务端连接事件是否就绪
We can SelectionKey
to get current channel
and selector
//返回当前事件关联的通道,可转换的选项包括:`ServerSocketChannel`和`SocketChannel`
Channel channel = key.channel();
//返回当前事件所关联的Selector对象
Selector selector = key.selector();
You can attach an object or other information to the SelectionKey, so that you can easily identify a specific channel.
key.attach(theObject);
Object attachedObj = key.attachment();
You can also attach objects when registering Channel with the Selector register()
SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);
Traverse SelectionKey
Once the call select
method, and the return value indicates that one or more channels is ready, and then by calling selector
of selectedKey()
method, access SelectionKey
set of ready channels as follows:
Set<SelectionKey> selectionKeys = selector.selectedKeys();
You can traverse the selected key set to access the ready channel, the code is as follows:
// 获取监听事件
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
// 迭代处理
while (iterator.hasNext()) {
// 获取事件
SelectionKey key = iterator.next();
// 移除事件,避免重复处理
iterator.remove();
// 可连接
if (key.isAcceptable()) {
...
}
// 可读
if (key.isReadable()) {
...
}
//可写
if(key.isWritable()){
...
}
}
Event driven
Netty is an asynchronous event-driven network application framework. In Netty, events refer to things that are of interest to certain operations. For example, in aChannel
registeredOP_READ
, indicating that theChannel
interested in reading, whenChannel
when there readable data, it will get a notification of an event.
The Netty
event-driven model includes the following core components.
Channel
Channel (pipe) is a basic abstraction of Java NIO, which represents an open connection to entities such as hardware devices, files, network sockets, or a program that can complete one or more different I/O
operations.
Callback
A callback is a method, a reference to a method that has been provided to another method. This allows the latter to call the former at an appropriate time. Netty uses callbacks to handle events internally; when a callback is triggered, related events can be ChannelHandler
interface.
For example: In the previous article, in the pipeline processor code of the server developed by Netty, when Channel
is a readable message in NettyServerHandler
, the callback method channelRead
will be called.
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
//读取数据实际(这里我们可以读取客户端发送的消息)
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server ctx =" + ctx);
Channel channel = ctx.channel();
//将 msg 转成一个 ByteBuf
//ByteBuf 是 Netty 提供的,不是 NIO 的 ByteBuffer.
ByteBuf buf = (ByteBuf) msg;
System.out.println("客户端发送消息是:" + buf.toString(CharsetUtil.UTF_8));
System.out.println("客户端地址:" + channel.remoteAddress());
}
//处理异常, 一般是需要关闭通道
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
Future
Future can be regarded as a placeholder for the result of an asynchronous operation; it will be completed at some point in the future and provide access to its results. Netty providesChannelFuture
for use in asynchronous operations. Each Netty's Outbound I/O operations will all return aChannelFuture
(completely asynchronous and event-driven).
The following is an ChannelFutureListener
use of 06114729918a01.
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ChannelFuture future = ctx.channel().close();
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
//..
}
});
}
Event and handler
In Netty, events are classified according to outbound/inbound data flow:
triggered by 16114729918a6f inbound data or related status changes include:
- The connection has been activated or deactivated.
- Data read.
- User event.
- Error event,
outbound event is the result of an action that will be
- Open or close the connection to the remote node.
- Write or flush data to the socket.
Each event can be distributed to a user-implemented method in the ChannelHandler
The following figure shows how an event is handled by ChannelHandler
ChannelHandler
provides a basic abstraction for the processor, which can be understood as a callback that is executed in response to a specific event.
Chain of Responsibility Model
Chain of Responsibility Pattern (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 a next node object is automatically maintained internally. When a request is sent from the head end of the chain, it will be passed to each node object in turn along the path of the chain until an object handles the request.
The focus of the chain of responsibility model is on this "chain". A chain handles similar requests, determines who will handle the request in the chain, and returns the corresponding results. In Netty, the ChannelPipeline
interface is defined to abstract the chain of responsibility.
The chain of responsibility model defines an abstract handler (Handler) role that abstracts the request and defines a method to set and return a reference to the next handler. In Netty, the ChannelHandler
interface is defined to assume this role.
The advantages and disadvantages of the chain of responsibility model
advantage:
- The sender does not need to know which object will process the request sent by itself, which realizes the decoupling of the sender and the receiver.
- Simplified the design of the sender object.
- You can add nodes and delete nodes dynamically.
shortcoming:
- All requests are traversed from the head of the chain, which is detrimental to performance.
- It is not convenient to debug. Since this mode uses a similar recursive method, the logic is more complicated when debugging.
scenes to be used:
- A request requires a series of processing work.
- Business flow processing, such as document approval.
- Expand and supplement the system.
ChannelPipeline
Netty's ChannelPipeline
design adopts the chain of responsibility design pattern, and the bottom layer adopts the data structure of a doubly linked list, which connects the various processors on the chain in series.
For every request from the client, Netty believes that ChannelPipeline
have the opportunity to process it. Therefore, all requests to the stack are propagated from the head node to the tail node (coming to the end). The msg of the node will be released).
inbound event : usually refers to the inbound data generated by the IO thread (common understanding: events coming up from the bottom of the socket are all inbound).
For example EventLoop
received selector
of OP_READ
event, the inbound call processor socketChannel.read(ByteBuffer)
After receiving the data, which will result in channel ChannelPipeline
next included in channelRead
method is called.
Outbound event : Usually refers to the actual output operation performed by the IO thread (common understanding: events that want to actively operate to the bottom of the socket are all outbound).
For example, the bind
method intentionally requests that server socket
bound to the given SocketAddress
, which will cause the ChannelPipeline
in the next outbound processor contained in the bind
to be called.
Pass the event to the next processor
The processor must call ChannelHandlerContext
to pass the event to the next processor.
The propagation method of inbound and outbound events is shown in the following figure:
The following example illustrates how event propagation is usually done:
public class MyInboundHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Connected!");
ctx.fireChannelActive();
}
}
public class MyOutboundHandler extends ChannelOutboundHandlerAdapter {
@Override
public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
System.out.println("Closing...");
ctx.close(promise);
}
}
Summarize
It is precisely because of Netty's layered architecture design is very reasonable , that the development of various application servers and protocol stacks based on Netty can develop rapidly.
end
I am a code farmer who is being beaten and working hard to advance. If the article is helpful to you, remember to like and follow, thank you!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。