初入netty的概念及结构体系
标签: 初入netty的概念及结构体系 jQuery博客 51CTO博客
2023-07-17 18:24:09 81浏览
netty的概念及结构体系
最近在netty的相关知识,跟此跟大家分享下学习成果
在学习netty之前,我们需要先了解下java的网络编程
一、Java网络编程
在早期的Java API中只支持由本地系统套接字库提供的所谓的阻塞函数
package com.liwai.game;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class MyTest {
public static void main(String[] args) throws IOException {
int portNumber = 8081;
ServerSocket serverSocket = new ServerSocket(portNumber);
Socket clientSocket = serverSocket.accept();
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out =
new PrintWriter(clientSocket.getOutputStream(), true);
String request, response;
while ((request = in.readLine()) != null) {
if ("Done".equals(request)) {
break;
}
response = processRequest(request);
out.println(response);
}
}
public MyTest() throws IOException {
}
}
这段代码片段将只能同时处理一个连接,要管理多个并发客户端,需要为每个新的客户端Socket 创建一个新的Thread
这种传统阻塞I/O的系统,要管理多个并发客户端,需要为每个新的客户端Socket 创建一个新的Thread ,一个请求对应一个线程这种模式,一旦有高并发的大量请求,就会有如下问题:
1、在任何时候都可能有大量的线程处于休眠状态,只是等待输入或者输出数据就绪,造成资源浪费。
2、需要为每个线程的调用栈都分配内存,其默认值大小区间为64 KB到1 MB,具体取决于操作系统。
3、如果网络I/O堵塞或者有网络抖动或者网络故障等,线程的阻塞时间可能很长。整个系统也变的不可靠。
1.1 Java NIO
NIO最开始是新的输入/输出(New Input/Output)的英文缩写**,**NIO代表非阻塞I/O(Non-blocking I/O),而阻塞I/O(blocking I/O)是旧的输入/输出(old input/output,OIO)
与阻塞I/O模型相比,这种模型提供了更好的资源管理:
1.使用较少的线程便可以处理许多连接,因此也减少了内存管理和上下文切换所带来开销;
2.当没有I/O操作需要处理的时候,线程也可以被用于其他任务。
二、Netty的核心组件
Netty的主要构件块:
- Channel ;
- 回调;
- Future ;
- 事件和ChannelHandler 。
1.Channel
Channel是Java NIO的一个基本构造。
它代表一个到实体(如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一个或者多个不同的I/O操作的程序组件)的开放连接,如读操作和写操作
目前,可以把Channel 看作是传入(入站)或者传出(出站)数据的载体。因此,它可以被打开或者被关闭,连接或者断开连接。
2.回调
一个回调其实就是一个方法,一个指向已经被提供给另外一个方法的方法的引用。这使得后者可以在适当的时候调用前者。回调在广泛的编程场景中都有应用,而且也是在操作完成后通知相关方最常见的方式之一。
如:
当一个新的连接已经被建立时,ChannelHandler的channelActive() 回调方法将会被调用,并将打印出一条信息。
3. Future
Future 提供了另一种在操作完成时通知应用程序的方式。这个对象可以看作是一个异步操作的结果的占位符;它将在未来的某个时刻完成,并提供对其结果的访问。
JDK预置的Future,只允许手动检查对应的操作是否已经完成,或者一直阻塞直到它完成。这是非常繁琐的,所以Netty提供了它自己的实现——ChannelFuture ,用于在执行异步操作的时候使用。
ChannelFuture 提供了几种额外的方法,这些方法使得我们能够注册一个或者多个ChannelFutureListener 实例。监听器的回调方法operationComplete() ,将会在对应的操作完成时被调用。然后监听器可以判断该操作是成功地完成了还是出错了。如果是后者,我们可以检索产生的Throwable 。简而言之,由ChannelFutureListener 提供的通知机制消除了手动检查对应的操作是否完成的必要。
每个Netty的出站I/O操作都将返回一个ChannelFuture;也就是说,它们都不会阻塞。正如我们前面所提到过的一样,Netty完全是异步和事件驱动的。
package com.liwai.game;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
/**
* @author syp
*/
public class MyTest {
public MyTest() {
Channel channel = null;
// Does not block
ChannelFuture future = channel.connect(new InetSocketAddress("192.168.0.1", 25));
future.addListener((ChannelFutureListener) future1 -> {
if (future1.isSuccess()) {
ByteBuf buffer = Unpooled.copiedBuffer(
"Hello", Charset.defaultCharset());
ChannelFuture wf = future1.channel()
.writeAndFlush(buffer);
} else {
Throwable cause = future1.cause();
cause.printStackTrace();
}
});
}
}
上述代码展示了一个ChannelFuture 作为一个I/O操作的一部分返回的例子。这里,connect() 方法将会直接返回,而不会阻塞,该调用将会在后台完成。因为线程不用阻塞以等待对应的操作完成,所以它可以同时做其他的工作,从而更加有效地利用资源。
事实上,回调和Future 是相互补充的机制;它们相互结合,构成了Netty本身的关键构件块之一。
4. 事件和ChannelHandler
我们可以认为每个Channel-Handler 的实例都类似于一种为了响应特定事件而被执行的回调。
在内部,ChannelHandler 自己也使用了事件和Future ,使得它们也成为了你的应用程序将使用的相同抽象的消费者。
三. 总结
1.Future、回调和ChannelHandler
Netty的异步编程模型是建立在Future 和回调的概念之上的, 而将事件派发到ChannelHandler 的方法则发生在更深的层次上。结合在一起,这些元素就提供了一个处理环境,使你的应用程序逻辑可以独立于任何网络操作相关的顾虑而独立地演变。这也是Netty的设计方式的一个关键目标。
拦截操作以及高速地转换入站数据和出站数据,都只需要你提供回调或者利用操作所返回的Future 。这使得链接操作变得既简单又高效,并且促进了可重用的通用代码的编写。
2.选择器、事件和EventLoop
在内部,将会为每个Channel 分配一个EventLoop ,用以处理所有事件,包括:
- 注册感兴趣的事件;
- 将事件派发给ChannelHandler ;
- 安排进一步的动作。
EventLoop 本身只由一个线程驱动,其处理了一个Channel 的所有I/O事件,并且在该EventLoop 的整个生命周期内都不会改变。这个简单而强大的设计消除了你可能有的在你的ChannelHandler 中需要进行同步的任何顾虑,因此,你可以专注于提供正确的逻辑
参考书籍:《netty实战》
更多内容请关注微信公众号“外里科技”
好博客就要一起分享哦!分享海报
此处可发布评论
评论(0)展开评论
展开评论