在许多框架中,都使用到了Netty作为网络通信的框架。比如Dobbo、RocketMQ等十分出名且使用广泛的框架中。
使用简便
在不使用Netty之前搭建一个服务器的代码如下所示:
// 服务器
public static void main(String[] args) {
try (ServerSocketChannel channel = ServerSocketChannel.open()) {
// 绑定端口
channel.bind(new InetSocketAddress(8080));
// 创建Selector 监听事件
Selector selector = Selector.open();
// 设置channel为非阻塞
channel.configureBlocking(false);
// 将Channel注册到Selector中,并且监听连接事件
channel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 当有连接事件发生时,返回事件个数
int count = selector.select();
// 获取所有事件
Set<SelectionKey> keys = selector.selectedKeys();
// 遍历所有事件,逐一处理
Iterator<SelectionKey> iter = keys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
// 业务逻辑
iter.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 客户端
public static void main(String[] args) {
try (SocketChannel channel = SocketChannel.open()) {
channel.bind(new InetSocketAddress(8080));
// 编写业务代码
} catch (Exception e) {
e.printStackTrace();
}
}
需要编写大量的代码以及处理非常多的异常。
引入了Netty之后,代码就长这样了:
// 服务端
public static void main(String[] args) {
// 创建启动器
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 设置线程组
serverBootstrap.group(getLoopGroup("zsk-server"));
// 设置启动的Channel类型
serverBootstrap.channel(NioServerSocketChannel.class);
// 添加业务逻辑Handler
serverBootstrap.childHandler(new ServerInitializer());
// 一些连接的配置
serverBootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30000);
// 绑定端口
serverBootstrap.bind(8080);
}
// 客户端
public static void main(String[] args) {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(getLoopGroup("zsk-client"));
bootstrap.channel(NioSocketChannel.class);
// 编写业务handler
bootstrap.handler(new ClientInitializer());
bootstrap.connect(new InetSocketAddress(8080));
}
代码清晰且简便,不需要自己处理服务器创建的逻辑,只需要关注业务逻辑代码的编写,简化开发。
一些性能的优化
优化SelectionKeySet
获取底层配置类
使用反射将内部原始的属性替换成自建的
将SelectorImpl
中的selectedKeys、publicSelectedKeys替换成Netty的SelectedSelectionKeySet
。
优秀的设计
- 对象池:由于
Netty
中有着许多被复用的对象,为了避免过度新增以及回收,将其池化。 - JNI:
Netty
在内部封装了一些本地native
方法,使用了C++的一些特性。 - ByteBuf:在Netty中,重新设计了一个用于字节处理的类:ByteBuf。该类对于JDK自带的
ByteBuffer
来说功能更加强大。 - 线程模型:所有读写操作都在一个线程内,省去了
volatile
等线程可见机制。不需要考虑并发的操作。
- 批量发送:将多次写入的数据一起发送出去,避免多次系统调用。(Java->JNI->c->用户空间->内核空间)
总结
Netty凭借其简洁的API设计、强大的功能和卓越的性能,成为了许多网络编程框架的首选,广泛应用于诸如Dubbo、RocketMQ等高性能分布式系统中。