前言:Netty 作为Nio 模型的实现,相较于Selector ,进一步将api进行封装,使用更加的简单;在平常的开发中会发现许多组件的底层通信都使用了Netty,所以就非常有必要对Netty 的使用以及其工作原理进行了解了。
1 Netty介绍:
Netty是一个NIO基于事件驱动的客户端服务器框架,可以快速轻松地开发网络应用程序,例如协议服务器和客户端。它极大地简化和简化了网络编程,如TCP和UDP套接字服务器开发。
它是一个高性能、异步事件驱动的网络编程框架,它基于Java NIO(New I/O)开发,提供了一种简单易用的API,使得开发者能够轻松地构建高性能、可扩展的网络应用程序。Netty的设计目标是提供一个可嵌入、高性能、灵活、易于使用的网络编程框架,支持多种传输协议,如TCP、UDP、HTTP、WebSocket等,并且提供了丰富的功能,如SSL/TLS支持、压缩、编解码、流量控制、负载均衡、连接管理等。
Netty的核心组件包括:Channel、EventLoop、ChannelFuture、ChannelHandler和Bootstrap等。其中,Channel是网络通信的载体,EventLoop是异步事件驱动的核心,ChannelFuture是异步操作的结果,ChannelHandler是处理网络事件的组件,Bootstrap则是启动Netty应用程序的入口点。
Netty的优点在于其高性能、可扩展性和灵活性。它采用了异步非阻塞的IO模型,能够处理大量并发连接,同时提供了丰富的功能和扩展性,可以通过自定义ChannelHandler来实现各种业务逻辑。此外,Netty具有良好的文档和社区支持,能够在开发过程中提供及时的帮助和支持。
2 netty 使用:
2.1 服务端:
1)netty 服务端:
package org.lgx.bluegrass.bluegrasscoree.netty.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.lgx.bluegrass.bluegrasscoree.netty.hadler.DiscardServerHandler;
/**
* @Description TODO
* @Date 2023/3/13 16:49
* @Author lgx
* @Version 1.0
*/
public class DiscardServer {
private int port;
public DiscardServer(int port) {
this.port = port;
}
public static void main(String[] args) {
new DiscardServer(8080).run();
}
private void run() {
// 总线程
NioEventLoopGroup boss = new NioEventLoopGroup();
// 工作线程
NioEventLoopGroup worker = new NioEventLoopGroup();
try {
//netty 服务端
ServerBootstrap server = new ServerBootstrap();
// 服务端参数
server.group(boss,worker)
// nio socket 处理
.channel(NioServerSocketChannel.class)
// 子线程事件回调处理
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DiscardServerHandler());
}
})
// 主线程最大的分配线程数
.option(ChannelOption.SO_BACKLOG,128)
// 保持长连接
.childOption(ChannelOption.SO_KEEPALIVE,true);
// 启动服务
ChannelFuture f = server.bind(this.port).sync();
System.out.println("8080服务已启动");
// 关闭通道
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 线程关闭
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
2) 事件处理 Handler:
package org.lgx.bluegrass.bluegrasscoree.netty.hadler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
/**
* @Description TODO
* @Date 2023/3/13 16:38
* @Author lgx
* @Version 1.0
*/
public class DiscardServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// super.channelRead(ctx, msg);
// ((ByteBuf) msg).release();
// ByteBuf in = (ByteBuf) msg;
// try {
// while (in.isReadable()) { // (1)
// System.out.print((char) in.readByte());
// System.out.flush();
// }
// }finally {
// ReferenceCountUtil.release(msg);
// }
ctx.write(msg); // (1)
ctx.flush(); // (2)
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// super.exceptionCaught(ctx, cause);
cause.printStackTrace();
ctx.close();
}
}
2.2 客户端:
1) 客户端连接
package org.lgx.bluegrass.bluegrasscoree.netty.client;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.lgx.bluegrass.bluegrasscoree.netty.hadler.ClientHandler;
/**
* @Description TODO
* @Date 2023/3/13 17:17
* @Author lgx
* @Version 1.0
*/
public class Client {
private int port;
public Client(int port) {
this.port = port;
}
public static void main(String[] args) {
new Client(8080).run();
}
private void run() {
// 工作线程
NioEventLoopGroup worker = new NioEventLoopGroup();
try {
// netty 客户端
Bootstrap b = new Bootstrap();
b.group(worker)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ClientHandler());
}
});
ChannelFuture f = b.connect("localhost",this.port).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
worker.shutdownGracefully();
}
}
}
- 事件处理Handler:
package org.lgx.bluegrass.bluegrasscoree.netty.hadler;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
/**
* @Description TODO
* @Date 2023/3/13 17:21
* @Author lgx
* @Version 1.0
*/
public class ClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// super.channelRead(ctx, msg);
ByteBuf in = (ByteBuf) msg;
try {
while (in.isReadable()) { // (1)
System.out.print((char) in.readByte());
System.out.flush();
}
} finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// super.exceptionCaught(ctx, cause);
cause.printStackTrace();
ctx.close();
}
}
Netty 中对于其 配置的参数如ChannelOption.SO_KEEPALIVE,ChannelOption.SO_BACKLOG等,在后续对ServerBootstrap 进行解读时在进行探究;
3 netty 特点:
3.1 netty 解决Selector的使用不便:
Nio 模式,实现多路复用,一个线程监听,采用事件回调机制,处理多个客户端的数据传输;虽然使用selector 可以实现多路复用,但是需要自己进行注册的维护,轮训,对管道数据的读写也比较麻烦;
3.2 netty 的api流程简单:
服务端:
客户端:
3.3 零拷贝:netty 在接收到数据后,可以直接将数据在系统空间进行处理,并且通过系统空间写会到管道中;
1、接收和发送ByteBuffer使用堆外直接内存进行Socket读写;
2、提供了组合Buffer对象,可以聚合多个ByteBuffer对象;
3、transferTo0直接将文件缓冲区的数据发送到目标Channel;
3.4 提前分配后数据要存放的内存空间,并且可以反复利用;
3.5 责任链串行设计(连过长一定程度影响性能)
3.6 Netty 支持多语言,多协议;高性能序列化;
4 参考:
1 Netty 4.x 版用户指南;