Netty入门——基于NIO实现机器客服案例

📅 发布时间:2026/7/13 22:20:52
Netty入门——基于NIO实现机器客服案例 Netty简单案例前言环境准备前置知识网络传输的几种实现方式BIO——同步阻塞IONIO——同步非阻塞IOAIO——异步非阻塞IO适用范围Netty简介特点核心组件使用场景运行简图案例简介关键代码客户端服务器端运行状况总结前言最近学完了Netty在这里关于Netty中实现NIO做一些小总结并附上一个小案例最好读者有一点Netty的基础。这里附上git的地址看一下netty的各种案例运行一下。github环境准备Maven 3.X、JDK15前置知识网络传输的几种实现方式建议看一下之前的一篇文章《Java网络编程之阻塞式IO与非阻塞IO》中关于阻塞和非阻塞IO在Java中的使用。BIO——同步阻塞IO阻塞式的IO服务器以轮询的方式不断查看是否有新的连接。当然其性能可以使用线程池得到略微改善。NIO——同步非阻塞IO通过Selector以及Channel的组合使用实现了多路复用虽然实现了服务器的异步处理但是客户端必须要在服务器响应到达才能发起下一个请求即客户端需要某线程持续监听是否有响应发送回来。大体流程如下AIO——异步非阻塞IO该模式不仅服务器实现了异步、客户端也实现了异步能够在请求没有到达之前继续向服务器发送数据。这里之后补充。适用范围NIO方式适用于连接数目多且连接比较短轻操作的架构比如聊天服务器并发局限于应用中JDK1.4开始支持。AIO方式使用于连接数目多且连接比较长重操作的架构比如HTTP服务器等充分调用OS参与并发操作JDK7开始支持Netty简介Netty是高性能的水平扩展的分布式架构特点健壮、安全、高可用、高性能、更新快、易用核心组件channel传入、传出的数据载体回调给请求的响应Feature异步编程的的一个任务的开启事件和ChannelHandler很多框架前端后端都包含这类发布者订阅者设计思想使用场景内部的RPC框架低延迟高吞吐量负载和性能测试用于负载和性能测试框架可以通过Netty和Redis结合来以最小的负载测试端到端的消息吞吐量。同步协议的异步客户端Netty为同步的协议创建异步的客户端如Kafka、Memcached。使得在同步和异步之间来回切换不需要更改任何上有代码。消息推送、实时流量监控都可以使用Netty有待大家自己探索。运行简图简而言之服务器监听端口就是新建一个ServerChannel客户端建立连接connect也是建立一个连接之后ServerChannel收到之后通过EventLoopGroup把该channel分配到EventLoop如果该channel对应的任务已经存在于EventLoop中就直接执行否则就要放入EventLoop中等待执行。EventLoopGroupEventLoopGroup好比线程池EventLoop好比线程Channel的pipeline包含了所有的HandlerEventLoop的Task注册连接建立示意图服务器客户端案例简介客户通过一个终端输入查询的编号如果问题在库中存在人工客服返回相应的回答若不存在就会返回默认回复——”致电人工客服“如果是非法的输入就直接模拟服务器宕机所有的连接都断开。注意这里后续作为入门案例展示如果这里看不太懂建议看一下git的quickstart部分。这里只展示关键代码关键代码客户端publicvoidstart(){BootstrapbootstrapnewBootstrap();// 引导类的配置包括事件组channel类型以及处理器bootstrap.group(group).channel(NioSocketChannel.class).handler(createInitializer());ChannelFuturefuturebootstrap.connect(newInetSocketAddress(8080));future.syncUninterruptibly();channelfuture.channel();}// 注册所有的处理器privateChannelInitializerChannelcreateInitializer(){returnnewTerminalChatClientInitializer();}// 通过控制台不断地询问智能客服staticvoidchat()throwsIOException{while(true){StringinputgetInputMsg();if(!input.equals(OVER)){channel.writeAndFlush(Unpooled.copiedBuffer(input,StandardCharsets.UTF_8));}else{break;}}}// 处理器代码publicclassTerminalClientInHandlerextendsSimpleChannelInboundHandlerByteBuf{// 把响应打印出来OverrideprotectedvoidchannelRead0(ChannelHandlerContextctx,ByteBufmsg)throwsException{out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern(yyyy/MM/dd HH:mm:ss)));Stringrespmsg.toString(StandardCharsets.UTF_8);out.println(resp);}// 提示连接成功OverridepublicvoidchannelActive(ChannelHandlerContextctx)throwsException{out.println(connect to server successfully : ctx.channel().remoteAddress());}// 提示连接关闭OverridepublicvoidchannelInactive(ChannelHandlerContextctx)throwsException{out.println(已经关闭连接);}}服务器端publicvoidstart(){// 初始化服务器引导ServerBootstrapservernewServerBootstrap();server.group(mainGroup).channel(NioServerSocketChannel.class).childHandler(createInitializer(channelGroup));// 监听8080端口ChannelFuturefutureserver.bind(8080);future.syncUninterruptibly();channelfuture.channel();}privateChannelInitializerChannelcreateInitializer(ChannelGroupchannelGroup){returnnewTerminalChatServerInitializer(channelGroup);}publicvoiddestroy(){if(channel!null){channel.close();}mainGroup.shutdownGracefully();subGroup.shutdownGracefully();}// 处理器关键代码protectedvoidchannelRead0(ChannelHandlerContextctx,ByteBufmsg)throwsException{// 打印收到的时间和消息Stringinputmsg.toString(StandardCharsets.UTF_8);out.println(ctx.channel().remoteAddress()\ninput);try{// 解析查询的Id,并且响应相应的答案intidInteger.parseInt(input);StringresresMap.getOrDefault(id,请致电人工10086);ctx.writeAndFlush(Unpooled.copiedBuffer(res,StandardCharsets.UTF_8));}catch(Exceptione){// 非法输入模拟服务器宕机向所有的客户端发送 服务器关闭消息group.writeAndFlush(Unpooled.copiedBuffer(不明原因服务器暂时关闭,StandardCharsets.UTF_8));group.close();}}// 有新连接打印客户机地址并且加入到聊天群组OverridepublicvoidchannelActive(ChannelHandlerContextctx)throwsException{out.println(client connected successfully : ctx.channel().remoteAddress());group.add(ctx.channel());}// 某客户端下线打印客户机地址这里不需要手动从群组移除Netty已经帮我们实现了该功能OverridepublicvoidchannelInactive(ChannelHandlerContextctx)throwsException{out.println(客户端: ctx.channel().remoteAddress().toString() 结束);}运行状况总结相对于Java原生Nio的消息读取以及消息处理来说Netty的实现方式更加简单。完整代码放于git上了。若文章有错误欢迎大家指正同时希望大佬能够给予一些指导