Netty 学习记录
- NioEventLoopGroup 为一个线程池,默认线程数为 cpu 核数 * 2 其 顶层抽象父类为 AbstractEventExecutorGroup
- 通过children 属性可知,线程池是通过一个 EventExecutor 数组 来管理, 通过 类图可以知道 EventExecutor 是一个线程池抽象接口。
- 线程池中具体的线程 实现类 为 NioEventLoop
- NioEventLoop 中 有 selector 作为选择器, taskQueue 作为任务队列, executor 作为 任务执行线程
-
ChannelHandlerContext 为 上下文环境,包含 pipeline, pipeline 当中又有 channel引用,channel引用中又有 pipeline 引用,为相互引用关系。
-
channel 中又有 eventLoop,eventLoop 有 thread 线程
-
关于 taskQueue 任务队列中的任务执行线程 仍然为 channel当中的线程 也就是说,Thread-3-1 先执行了 channelRead 方法,再去执行 taskQueue当中的任务队列。也即为单线程串行处理逻辑。
代码
sever 端:
package netty.sever; 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; /** * @ClassName NettySever * @Description TODO * @Author guangmingdexin * @Date 2020/10/25 18:56 * @Version 1.0 **/ public class NettySever { public static void main(String[] args) throws InterruptedException { // 创建 BossGroup 和 WorkerGroup // 1.创建 两个线程组 bossGroup workerGroup // 2.bossGroup 只是处理连接请求,真正的和 客户端 业务处理 ,会交给 workGroup 完成 // 3.两个都是无限循环 NioEventLoopGroup bossGroup = new NioEventLoopGroup(1); NioEventLoopGroup workerGroup = new NioEventLoopGroup(); // 创建服务器端的启动对象,配置参数 ServerBootstrap bootstrap = new ServerBootstrap(); // 使用链式编程来进行设置 // 设置两个线程组 bootstrap.group(bossGroup, workerGroup) // 服务器通道实现 .channel(NioServerSocketChannel.class) // 设置线程连接队列的连接个数 .option(ChannelOption.SO_BACKLOG, 128) //设置保持活动连接个数 .childOption(ChannelOption.SO_KEEPALIVE, true) .childHandler(new ChannelInitializer<SocketChannel>() { // 给 pipeline 设置处理器 @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new NettySeverHandler()); } }); // 给 workGroup 对应得管道设置处理器 System.out.println("sever is ready!"); // 绑定一个端口并且同步,生成了一个 ChannelFuture 对象 ChannelFuture channelFuture = bootstrap.bind(6607).sync(); // 对关闭通道进行监听 channelFuture.channel().closeFuture().sync(); } }
package netty.sever; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.CharsetUtil; import java.util.Date; /** * @ClassName NettySeverHandler * @Description TODO * @Author guangmingdexin * @Date 2020/10/30 17:01 * @Version 1.0 **/ // 自定义 handler 需要 继承 netty 自带 handler public class NettySeverHandler extends ChannelInboundHandlerAdapter { /** * 读取数据 * *ctx: 上下文环境 含有 管道pipeline 通道 channel 地址 * *msg: 客户端发送的消息 * * @param ctx 上下文环境 * @param msg 信息 */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws InterruptedException { // 有一个长耗时的的业务,自定义 taskQueue System.out.println(new Date() + "处理当前通道线程 ID " + Thread.currentThread().getId() + "->" + Thread.currentThread()); ctx.channel().eventLoop().execute(() -> { try { System.out.println(new Date() + "处理自定义线程 ID " + Thread.currentThread().getId() + "->" + Thread.currentThread()); Thread.sleep(5 * 1000); ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端, 🐱1" , CharsetUtil.UTF_8)); System.out.println("自定义任务完成"); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println("go on ... " + new Date()); Thread.sleep(5 * 1000); System.out.println("end ... " + new Date()); // 将 msg 转成 ByteBuf // ByteBuf byteBuf = (ByteBuf) msg; // // System.out.println("客户端发送消息:" + byteBuf.toString(CharsetUtil.UTF_8)); // System.out.println("客户端地址: " + ctx.channel().remoteAddress()); } // 数据读取完毕 @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { // writeAndFlush : write + flush // 将数据写入缓存,并更新 ctx.writeAndFlush(Unpooled.copiedBuffer("hello 客户端" , CharsetUtil.UTF_8)); } // 处理异常, 关闭通道 @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
客户端:
package netty.client;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
* @ClassName NettyClient
* @Description TODO
* @Author guangmingdexin
* @Date 2020/10/30 17:12
* @Version 1.0
**/
public class NettyClient {
public static void main(String[] args) {
// 客户端需要一个事件循环组
EventLoopGroup eventExecutors = new NioEventLoopGroup();
// 创建 客户端启动对象
Bootstrap bootstrap = new Bootstrap();
// 设置相关参数
bootstrap.group(eventExecutors)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
nioSocketChannel.pipeline().addLast(new NettyClientHandler());
}
});
System.out.println("客户端 ok ..");
// 启动 客户端连接 服务器
// 关于 ChannelFuture 涉及到 netty 的异步模型
try {
ChannelFuture future = bootstrap.connect("127.0.0.1", 6607).sync();
// 对关闭通道进行 监听
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
eventExecutors.shutdownGracefully();
}
}
}
package netty.client;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
/**
* @ClassName NettyClientHandler
* @Description TODO
* @Author guangmingdexin
* @Date 2020/10/30 17:27
* @Version 1.0
**/
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
// 当通道就绪时就会触发该方法
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer("hello, sever: 🐱喵", CharsetUtil.UTF_8));
}
// 当通道有读取事件时,会触发
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println(byteBuf.toString(CharsetUtil.UTF_8));
System.out.println("服务器的地址: " + ctx.channel().remoteAddress());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
}
}
l.UTF_8));
System.out.println("服务器的地址: " + ctx.channel().remoteAddress());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
}
}
以上只是自己的一些学习思考,总结,如有错误,还请指出!