前言: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>() {@Overrideprotected 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 {@Overridepublic 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)}@Overridepublic 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>() {@Overrideprotected 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 {@Overridepublic 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);}}@Overridepublic 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 版用户指南;