| 1) 用户程序自定义的普通任务 [举例说明] |
| 2) 用户自定义定时任务 |
| 3) 非当前 Reactor 线程调用 Channel 的各种方法 |
| 例如在推送系统的业务线程里面,根据用户的标识,找到对应的 Channel 引用,然后调用 Write 类方法向该用户推送消息,就会进入到这种场景。最终的 Write 会提交到任务队列中后被异步消费 |
- 在NettyServerHandler类用模拟1个很费事的操作
| |
| public class NettyServerHandler extends ChannelInboundHandlerAdapter { |
| |
| @Override |
| public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { |
| |
| Thread.sleep(5 * 1000); |
| ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端~(>^ω^<)喵2", CharsetUtil.UTF_8)); |
| } |
| } |
| |
| |
| |
| |
| .....服务器 is ready... |
| server ctx =ChannelHandlerContext(NettyServerHandler |
| 客户端发送消息是:hello, server: (>^ω^<)喵 |
| 客户端地址:/127.0.0.1:56416 |
| |
| |
| 客户端 ok.. |
| client ChannelHandlerContext(NettyClientHandler |
| 服务器回复的消息:hello, 客户端~(>^ω^<)喵2 |
| 服务器的地址: /127.0.0.1:6668 |
| 服务器回复的消息:hello, 客户端~(>^ω^<)喵1 |
| 服务器的地址: /127.0.0.1:6668 |
| |
| |
| # 添加如下 |
| public class NettyServerHandler extends ChannelInboundHandlerAdapter { |
| |
| |
| |
| |
| |
| |
| @Override |
| public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { |
| |
| |
| |
| |
| |
| ctx.channel().eventLoop().execute(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| Thread.sleep(5 * 1000); |
| ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端~(>^ω^<)喵2", CharsetUtil.UTF_8)); |
| System.out.println("channel code=" + ctx.channel().hashCode()); |
| } catch (Exception ex) { |
| System.out.println("发生异常" + ex.getMessage()); |
| } |
| } |
| }); |
| |
| |
| ctx.channel().eventLoop().execute(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| Thread.sleep(5 * 1000); |
| ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端~(>^ω^<)喵3", CharsetUtil.UTF_8)); |
| System.out.println("channel code=" + ctx.channel().hashCode()); |
| } catch (Exception ex) { |
| System.out.println("发生异常" + ex.getMessage()); |
| } |
| } |
| }); |
| |
| |
| ctx.channel().eventLoop().schedule(new Runnable() { |
| @Override |
| public void run() { |
| |
| try { |
| Thread.sleep(5 * 1000); |
| ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端~(>^ω^<)喵4", CharsetUtil.UTF_8)); |
| System.out.println("channel code=" + ctx.channel().hashCode()); |
| } catch (Exception ex) { |
| System.out.println("发生异常" + ex.getMessage()); |
| } |
| } |
| }, 5, TimeUnit.SECONDS); |
| } |
| } |
| |
| # 测试 |
| # 启动服务端,再启动客户端 |
| # 服务端控制台打印如下 |
| .....服务器 is ready... |
| server ctx =ChannelHandlerContext(NettyServerHandler#0, [id: 0xafd22374, L:/127.0.0.1:6668 - R:/127.0.0.1:56738]) |
| 客户端发送消息是:hello, server: (>^ω^<)喵 |
| 客户端地址:/127.0.0.1:56738 |
| |
| # 客户端控制台打印如下 |
| 客户端 ok.. |
| client ChannelHandlerContext(NettyClientHandler#0, [id: 0xaf11456f, L:/127.0.0.1:56738 - R:/127.0.0.1:6668]) |
| 服务器回复的消息:hello, 客户端~(>^ω^<)喵1 |
| 服务器的地址: /127.0.0.1:6668 |
| 服务器回复的消息:hello, 客户端~(>^ω^<)喵2 |
| 服务器的地址: /127.0.0.1:6668 |
| 服务器回复的消息:hello, 客户端~(>^ω^<)喵3 |
| 服务器的地址: /127.0.0.1:6668 |
| 服务器回复的消息:hello, 客户端~(>^ω^<)喵4 |
| 服务器的地址: /127.0.0.1:6668 |
| |
| # 测试结果:这次,任务2、任务3、任务4并没有影响任务1的执行,任务1先执行后,任务2阻塞5秒后执行,任务3阻塞5秒后执行,任务4阻塞5秒后执行 |
| |
| # NettyServer类中可以获取到socketchannel的hash码 |
| # 可以使用一个集合管理SocketChannel,再推送消息时,可以将业务加入到各个channel对应的NIOEventLoop的taskQueue或者scheduleTaskQueue |
| @Override |
| protected void initChannel(SocketChannel ch) throws Exception { |
| System.out.println("客户socketchannel hashcode=" + ch.hashCode()); |
| ch.pipeline().addLast(new NettyServerHandler()); |
| } |
| |
| # 在NettyServerHandler中同样可以拿到channel的hash码 |
| System.out.println("channel code=" + ctx.channel().hashCode()); |
| |
| # 完整代码中查看 |
| public class NettyServerHandler extends ChannelInboundHandlerAdapter { |
| |
| @Override |
| public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { |
| |
| |
| |
| |
| ctx.channel().eventLoop().execute(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| Thread.sleep(5 * 1000); |
| ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端~(>^ω^<)喵2", CharsetUtil.UTF_8)); |
| System.out.println("channel code=" + ctx.channel().hashCode()); |
| } catch (Exception ex) { |
| System.out.println("发生异常" + ex.getMessage()); |
| } |
| } |
| }); |
| |
| ctx.channel().eventLoop().execute(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| Thread.sleep(5 * 1000); |
| ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端~(>^ω^<)喵3", CharsetUtil.UTF_8)); |
| System.out.println("channel code=" + ctx.channel().hashCode()); |
| } catch (Exception ex) { |
| System.out.println("发生异常" + ex.getMessage()); |
| } |
| } |
| }); |
| |
| |
| ctx.channel().eventLoop().schedule(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| Thread.sleep(5 * 1000); |
| ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端~(>^ω^<)喵4", CharsetUtil.UTF_8)); |
| System.out.println("channel code=" + ctx.channel().hashCode()); |
| } catch (Exception ex) { |
| System.out.println("发生异常" + ex.getMessage()); |
| } |
| } |
| }, 5, TimeUnit.SECONDS); |
| |
| } |
| } |
| 1) Netty 抽象出两组线程池,BossGroup 专门负责接收客户端连接,WorkerGroup 专门负责网络读写操作。 |
| 2) NioEventLoop 表示一个不断循环执行处理任务的线程,每个 NioEventLoop 都有一个selector,用于监听绑定在其上的 socket 网络通道。 |
| 3) NioEventLoop 内部采用串行化设计,从消息的读取->解码->处理->编码->发送,始终由IO 线程 NioEventLoop 负责 |
| • NioEventLoopGroup 下包含多个 NioEventLoop |
| • 每个 NioEventLoop 中包含有一个 Selector,一个 taskQueue |
| • 每个 NioEventLoop 的 Selector 上可以注册监听多个 NioChannel |
| • 每个 NioChannel 只会绑定在唯一的 NioEventLoop 上 |
| • 每个 NioChannel 都绑定有一个自己的 ChannelPipeline |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
2021-08-08 Sentinel限流、降级配置详解
2021-08-08 导入项目后报错问题
2021-08-08 Redis入门(三):新数据类型