Netty 是一个异步事件驱动的网络应用框架,用于快速开发可维护的高性能协议服务器和客户端。它极大地简化了 TCP/UDP 套接字服务器等网络编程,同时保持了高性能和高扩展性。Netty 最初由 JBoss 开发,现已成为 Java 网络编程的事实标准框架,被广泛应用于大数据、游戏、金融、物联网等领域。
Netty 的核心优势:
Netty 适用于各种需要网络通信的场景,特别是对性能有较高要求的领域:
// 传统BIO服务器示例
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
Socket socket = serverSocket.accept(); // 阻塞
new Thread(() -> {
// 处理连接
}).start();
}
缺点:
NIO 的核心改进:
// NIO服务器基本结构
Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);
ssc.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select(); // 阻塞直到有事件
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iter = keys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
if (key.isAcceptable()) {
// 处理新连接
} else if (key.isReadable()) {
// 处理读事件
}
iter.remove();
}
}
Netty 在 Java NIO 基础上进行了更高层次的抽象:
Netty 线程模型:
+---------------------------------------------------+
| Boss Group (NIO) |
| (处理连接请求,通常1-2个线程足够) |
+---------------------------------------------------+
|
v
+---------------------------------------------------+
| Worker Group (NIO) |
| (处理I/O操作,线程数通常为CPU核心数*2) |
+---------------------------------------------------+
|
v
+---------------------------------------------------+
| ChannelPipeline |
| (处理业务逻辑,包含多个ChannelHandler) |
+---------------------------------------------------+
Channel 是 Netty 对网络连接的抽象,主要实现:
NioSocketChannel
:非阻塞 TCP 客户端 ChannelNioServerSocketChannel
:非阻塞 TCP 服务器 ChannelEpollSocketChannel
:基于 epoll 的 TCP Channel(Linux)OioSocketChannel
:阻塞模式 TCP Channel常用方法:
write()
/writeAndFlush()
:写入数据close()
:关闭连接pipeline()
:获取关联的 ChannelPipelineeventLoop()
:获取关联的 EventLoopEventLoop 是 Netty 的核心执行单元,结合了线程和事件循环:
// EventLoop 继承关系
io.netty.util.concurrent
-> EventExecutor
-> EventExecutorGroup
-> EventLoopGroup
-> EventLoop
典型用法:
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 接收连接
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 处理I/O
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
// 添加处理器
}
});
ChannelPipeline 是处理网络事件的责任链,由一系列 ChannelHandler 组成:
Inbound事件(如连接建立、数据读取) → ChannelInboundHandler → ... → TailHandler
Outbound事件(如连接关闭、数据写入) ← ChannelOutboundHandler ← ... ← HeadHandler
ChannelInboundHandler:处理入站事件
channelActive()
:连接建立channelRead()
:数据读取exceptionCaught()
:异常处理ChannelOutboundHandler:处理出站事件
write()
:数据写入close()
:连接关闭编解码器:
ByteToMessageDecoder
:字节解码MessageToByteEncoder
:消息编码自定义 Handler 示例:
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 回显收到的消息
ctx.write(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
ByteBuf 是对 Java NIO ByteBuffer 的增强:
核心优势:
基本使用:
ByteBuf buf = Unpooled.buffer(1024); // 非池化缓冲区
buf.writeBytes("Hello".getBytes());
byte[] dst = new byte[buf.readableBytes()];
buf.readBytes(dst);
System.out.println(new String(dst));
ByteBuf 类型:
UnpooledHeapByteBuf
:堆内内存,JVM 管理UnpooledDirectByteBuf
:直接内存,减少拷贝PooledByteBuf
:池化实现,提高性能public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public void start() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new EchoServerHandler());
}
});
ChannelFuture f = b.bind().sync();
System.out.println("服务器启动,监听 " + port);
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new EchoServer(8080).start();
}
}
public class EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture f = b.connect().sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new EchoClient("localhost", 8080).start();
}
}
服务器端 Handler:
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
System.out.println("服务器收到: " + in.toString(CharsetUtil.UTF_8));
ctx.write(in);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
客户端 Handler:
public class EchoClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.writeAndFlush(Unpooled.copiedBuffer(
"Netty rocks!", CharsetUtil.UTF_8));
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
System.out.println("客户端收到: " + in.toString(CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
合理配置线程数:
业务线程池:
// 在ChannelHandler中使用业务线程池
EventExecutorGroup businessGroup = new DefaultEventExecutorGroup(16);
// 在Pipeline中添加时指定线程组
pipeline.addLast(businessGroup, "handler", new BusinessHandler());
使用池化的 ByteBuf:
// 在Bootstrap中配置
bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
避免内存泄漏:
ReferenceCountUtil.release(msg)
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128) // 连接队列大小
.childOption(ChannelOption.SO_KEEPALIVE, true) // 保持连接
.childOption(ChannelOption.TCP_NODELAY, true) // 禁用Nagle算法
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
public class IntegerToStringDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in,
List<Object> out) {
if (in.readableBytes() < 4) {
return;
}
out.add(String.valueOf(in.readInt()));
}
}
public class StringToIntegerEncoder extends MessageToByteEncoder<String> {
@Override
protected void encode(ChannelHandlerContext ctx, String msg,
ByteBuf out) {
out.writeInt(Integer.parseInt(msg));
}
}
// 添加Protobuf编解码器
pipeline.addLast(new ProtobufVarint32FrameDecoder());
pipeline.addLast(new ProtobufDecoder(MyMessage.getDefaultInstance()));
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new IdleStateHandler(60, 30, 0));
pipeline.addLast(new HeartbeatHandler());
public class HeartbeatHandler extends ChannelInboundHandlerAdapter {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
if (evt instanceof IdleStateEvent) {
IdleStateEvent e = (IdleStateEvent) evt;
if (e.state() == IdleState.READER_IDLE) {
ctx.close();
} else if (e.state() == IdleState.WRITER_IDLE) {
ctx.writeAndFlush(new PingMessage());
}
}
}
}
public class HttpServer {
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
p.addLast(new HttpServerCodec());
p.addLast(new HttpObjectAggregator(65536));
p.addLast(new HttpRequestHandler());
}
});
ChannelFuture f = b.bind(8080).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) {
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.wrappedBuffer("Hello World".getBytes()));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH,
response.content().readableBytes());
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
JVM 参数优化:
-server -Xms2g -Xmx2g -XX:+UseG1GC
-XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError
监控指标:
ChannelTrafficShapingHandler
监控流量ChannelDuplexHandler
统计处理时间全局异常处理:
public class ExceptionHandler extends ChannelDuplexHandler {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
if (cause instanceof IOException) {
// 网络异常
} else {
// 业务异常
}
ctx.close();
}
}
重连机制:
public void connect() {
bootstrap.connect().addListener((ChannelFuture future) -> {
if (!future.isSuccess()) {
future.channel().eventLoop().schedule(
this::connect, 5, TimeUnit.SECONDS);
}
});
}
单元测试:
@Test
public void testHandler() {
EmbeddedChannel channel = new EmbeddedChannel(new MyHandler());
ByteBuf input = Unpooled.buffer();
input.writeBytes("test".getBytes());
assertTrue(channel.writeInbound(input));
ByteBuf output = channel.readOutbound();
assertEquals("TEST", output.toString(CharsetUtil.UTF_8));
}
压力测试:
Netty 作为高性能网络编程框架,通过其精妙的架构设计解决了传统 NIO 的复杂性,同时提供了极高的性能。本文涵盖了:
无论是构建简单的网络应用还是复杂的高性能服务器,Netty 都能提供强大的支持。希望本指南能帮助你掌握 Netty 的核心技术,并在实际项目中发挥其强大威力。
PS:如果你在学习过程中遇到问题,别担心!欢迎在评论区留言,我会尽力帮你解决!