Netty4.x 的逆袭之路 —— 初识 Netty

    科技2022-08-30  113

    Netty4.x 的逆袭之路

    2020年9月18日

    (傅哥的谆谆教诲)

    Java 目前有三种IO共存;分别是 BIO、NIO 和 AIO

    BIO : Block-IO 阻塞 IO 发起请求, 阻塞等待, 完成请求 (并发场景通过开启多条线程来完成)NIO : New - IO 阻塞 IO 轮询事件查询器(轮询为阻塞等待, 此处可手动释放阻塞), 当有事件产生时解除阻塞AIO : Asynchronous IO 异步 IO 轮询动作不在阻塞用户线程, 当有事件产生时通知用户线/.

    BIO : 面向流 一个字节输入一个字节输出 NIO : 面向缓冲区;

    补充: IO 的四种模型 BIO、NIO、AIO、多路复用

    按照傅哥的例子写了两个IO的操作 AIO 和 BIO , 之前自己用 NIO 写过一个 reactor 主从多线程的模式. 所以没有继续去写 NIO 的代码

    写下来发现傅哥他从第一行代码开始就将整个架构的思想放了进去. 包括抽象的 适配器与通道的处理器 channelhandler. 思想领会了 即开闭原则, 这里用到了模板方法设计模式, 定制了规范, 不同的处理器由自己去实现.

    整体感觉代码较复杂 , 可扩展性较好. 入门学习难度 128 颗星.

    总结:

    有些事情,先不要想为什么,先去做。做完也许你就知道了答案。不做就一直在怀疑。

    接触到Netty之后发现, 原来傅哥从一开始就想着用Netty的思路来进行代码编写案例教程 此时竖起大拇指.

    2020年9月23日

    傅哥英语不怎么样, bing. emm…

    public static void main(String[] args) { new NettyServer().bing(7397); } private void bing(int port) {

    netty 正式开始, 这里为了学习效率, 使用的为源码包. 直接拉的netty github 4.1 的分支代码 这里有个比较有意思的事情, 就是netty源码拿下来运行时发现少个包 import io.netty.util.collection.IntObjectMap;

    当然最后来发现这个包是在编译期使用groovy脚本生成的 a-source-code/netty4.1/common/src/main/script/codegen.groovy


    使用.java的源码netty, 然后根据傅哥的代码下来, 程序正常执行. 日志正常输出

    链接报告开始 链接报告信息:有一客户端链接到本服务端 链接报告IP:192.168.30.32 链接报告Port:2333 链接报告完毕

    总结

    101:

    EventLoopGroup 是什么? 这个模式很像之前写的 NIO 主从模型

    ChannelFuture sync = bootstrap.bind(port).sync(); sync.channel().closeFuture().sync();

    这两行代码干了什么? sync

    parentGroup.shutdownGracefully(); childGroup.shutdownGracefully(); 优雅的关闭?? 有多优雅?

    bootstrap 配置的一系列参数问题, 为什么是 128 ? 为什么用 childhandler

    2020年9月23日

    傅哥前面的大篇幅代码没解释有点难受

    102 案例跑通

    链接报告开始 链接报告信息:有一客户端链接到本服务端 链接报告IP:192.168.30.32 链接报告Port:2333 链接报告完毕 Wed Sep 23 14:40:22 CST 2020接收到消息:1111 Wed Sep 23 14:40:24 CST 2020接收到消息:1111 Wed Sep 23 14:40:25 CST 2020接收到消息:1111 Wed Sep 23 14:40:27 CST 2020接收到消息:1111

    总结

    102:

    新增两个关键的内容, 疑惑

    ChannelInitializerChannelInboundHandlerAdapter ==> ChannelHandlerAdapter ==> ChannelHandler

    管道初始化, 管道处理器

    建立连接的时候插根管子在两个 Socket 上 (客户端----服务端)?

    目前思路还是比较清晰

    通过 ServerBootstrap 来配置并启动服务端 Netty配置通道初始化器 ChannelInitializer在初始化出来的 SocketChannel 的管道中(最后)增加自己的管道(pipeline.addLast), ChannelHandler

    2020年9月23日 解码器

    预习

    在netty中是否可以自动的把接收的Bytebuf数据转String,不需要我手动处理? 答;有,可以在管道中添加一个StringDecoder。 解码器?

    在网络传输过程中有半包粘包的问题,netty能解决吗? 答:能,netty提供了很丰富的解码器,在正确合理的使用下就能解决半包粘包问题。

    常用的String字符串下有什么样的解码器呢? 答:不仅在String下有处理半包粘包的解码器在处理其他的数据格式也有,其中谷歌的protobuf数据格式就是其中一个。 对于String的有以下常用的三种:

    LineBasedFrameDecoder 基于换行DelimiterBasedFrameDecoder 基于指定字符串FixedLengthFrameDecoder 基于字符串长度

    本篇主题: netty4.1基础入门篇三《NettyServer字符串解码器》

    103 案例跑通

    Lvgo-链接报告IP:192.168.30.32 Lvgo-链接报告Port:2333 链接报告开始 {公众号:bugstack虫洞栈 >获取学习源码} 链接报告信息:有一客户端链接到本服务端 链接报告IP:192.168.30.32 链接报告Port:2333 链接报告完毕 Wed Sep 23 15:40:24 CST 2020接收到消息:lvgorice@gmai.com

    定长报文解码器

    "C:\Program Files\Java\jdk1.8.0_152\bin\java.exe" "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.4\lib\idea_rt.jar=59635:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.4\bin" -Dfile.encoding=GBK -classpath "C:\Program Files\Java\jdk1.8.0_152\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_152\jre\lib\rt.jar;F:\7MI\lvgo-blog-code\network-programming\target\classes;F:\7MI\lvgo-blog-code\source-code\netty\transport\target\classes;F:\7MI\lvgo-blog-code\source-code\netty\common\target\classes;F:\7\erayt-maven-repo\org\jctools\jctools-core\3.1.0\jctools-core-3.1.0.jar;F:\7MI\lvgo-blog-code\source-code\netty\buffer\target\classes;F:\7MI\lvgo-blog-code\source-code\netty\resolver\target\classes;F:\7MI\lvgo-blog-code\source-code\netty\codec\target\classes;F:\7\erayt-maven-repo\org\openjdk\jol\jol-core\0.10\jol-core-0.10.jar" cc.lvgo.netty4x.netty_basics.netty103.server.NettyServer 2333 端口已开启, 造作中.... Lvgo-链接报告IP:192.168.30.32 Lvgo-链接报告Port:2333 链接报告开始 {公众号:bugstack虫洞栈 >获取学习源码} 链接报告信息:有一客户端链接到本服务端 链接报告IP:192.168.30.32 链接报告Port:2333 链接报告完毕 Wed Sep 23 15:50:54 CST 2020接收到消息:lvgorice@gmai.com Wed Sep 23 15:50:56 CST 2020接收到消息:1lvgorice@gmai.co Wed Sep 23 15:50:57 CST 2020接收到消息:m1lvgorice@gmai.c Wed Sep 23 15:50:58 CST 2020接收到消息:om1lvgorice@gmai. Wed Sep 23 15:50:58 CST 2020接收到消息:com1lvgorice@gmai Wed Sep 23 15:50:58 CST 2020接收到消息:.com1lvgorice@gma Wed Sep 23 15:50:58 CST 2020接收到消息:i.com1lvgorice@gm Wed Sep 23 15:50:58 CST 2020接收到消息:ai.com1lvgorice@g

    总结

    103:

    解码器放在输出的前面. 管道思想 . 解码器同样在管道后面增加, 先用解码通道, 在使用我们. 理解成一根水管, 对水质进行过滤处理等操作从源头流过来, 流到控制台…

    对于#102中的问题, ChannelInitializer 似乎有了答案. 当有连接进入的时候, 即使用该初始化器进行管道初始化 io/netty/bootstrap/ServerBootstrap#channelRead{child.pipeline().addLast(childHandler);} 第一根管道就是我们配置在启动器上的childHandler 而后的内容目前仍理解为一根根管子继续在后面接

    2020年9月23日 解码器

    预习

    本章节主要介绍服务端在收到数据后,通过writeAndFlush发送ByteBuf字节码向客户端传输信息。 因为我们使用客户端模拟器的编码是GBK格式,所以代码中也需要将字节码转换为GBK,否则会乱码

    104 案例跑通

    2333 端口已开启, 造作中.... io/netty/channel/nio/NioEventLoop.java:447 死循环事件选择器阻塞中...... 死循环开启事件选择器. 监控事件发生 ----strategy : 1 选择器事件处理过程 io/netty/channel/nio/NioEventLoop.java:698 事件行动值 : 16 通道初始化完成... 基于换行符解码 io/netty/channel/nio/NioEventLoop.java:447 死循环事件选择器阻塞中...... 链接报告信息:有一客户端链接到本服务端 io/netty/channel/nio/NioEventLoop.java:447 死循环事件选择器阻塞中......

    总结

    104:

    服务端在写回的时候用到了原来的客户端的 ChannelHandlerContext , 就可以理解为将内容写到了原来的那根通道里面channelInactive/exceptionCaught 断开连接和出现异常时回调的两个方法通过源码发现了几个比较关键的类 NioEventLoop , 由它来完成整个事件的轮询 DefaultChannelPipeline、AbstractNioByteChannel、AbstractNioChannel 这几个类在调试源码的时候频繁的出现。

    2020年9月23日 编码器

    预习

    本章节我们使用new StringEncoder(Charset.forName(“GBK”))进行进行字符串编码,用以实现服务端在发送数据的时候只需要传输字符串内容即可。

    105 案例跑通

    基于换行符解码 链接报告信息:有一客户端链接到本服务端 io/netty/channel/nio/NioEventLoop.java:447 死循环事件选择器阻塞中...... 死循环开启事件选择器. 监控事件发生 ----strategy : 1 选择器事件处理过程 io/netty/channel/nio/NioEventLoop.java:698 事件行动值 : 1 Wed Sep 23 21:50:16 CST 2020接收到消息:lvgorice@gmail.com io/netty/channel/nio/NioEventLoop.java:447 死循环事件选择器

    总结

    105:

    可以通过在服务端进行编解码处理, 这样使得客户端只需进行正常的数据传输就好,无需关系编解码问题

    初始阶段暂时减少源码的阅读, 先将整个流程走完后再去细细品味源码

    2020年9月23日 群发

    预习

    在微信或者QQ的聊天中我们经常会用到一些群聊,把你的信息发送给所有用户。 那么为了实现群发消息,在netty中我们可以使用ChannelGroup方式进行群发消息。 如果为了扩展验证比如你实际聊天有不同的群,那么可以定义ConcurrentHashMap结构来存放ChannelGroup。ChannelGroup中提供了一些基础的方法;添加、异常、查找、清空、发放消息、关闭等。

    ChannelGroup 通道组? 把连接进来的 SocketChannel 放到组里, 然后通过 循环遍历将消息发送给不同的 SocketChannel ?

    106 案例跑通

    死循环开启事件选择器. 监控事件发生 ----strategy : 1 选择器事件处理过程 io/netty/channel/nio/NioEventLoop.java:698 事件行动值 : 1 Wed Sep 23 22:20:08 CST 2020接收到消息:lvgorice@gmail.com io/netty/channel/nio/NioEventLoop.java:447 死循环事件选择器阻塞中...... 死循环开启事件选择器. 监控事件发生 ----strategy : 0 io/netty/channel/nio/NioEventLoop.java:447 死循环事件选择器阻塞中...... 死循环开启事件选择器. 监控事件发生 ----strategy : 1 选择器事件处理过程 io/netty/channel/nio/NioEventLoop.java:698 事件行动值 : 1 客户端断开链接/192.168.232.1:12177 io/netty/channel/nio/NioEventLoop.java:447 死循环事件选择器阻塞中...... 死循环开启事件选择器. 监控事件发生 ----strategy : 1 选择器事件处理过程 io/netty/channel/nio/NioEventLoop.java:698 事件行动值 : 1 客户端断开链接/192.168.232.1:12178 io/netty/channel/nio/NioEventLoop.java:447 死循环事件选择器阻塞中......

    总结

    106:

    ChannelGroup 可以用来管理连接到服务端的 channel

    2020年9月23日 客户端

    预习

    netty 的客户端. 之前一直都在使用模拟器与我们的服务端进行连接. netty 的客户端还一直都没有接触到.

    107 案例跑通

    连接成功, 通道 ID : 685fac28 死循环事件选择器阻塞轮询中...... io/netty/channel/nio/NioEventLoop.java:447 发生事件 ---- strategy = 1 选择器事件处理过程 io/netty/channel/nio/NioEventLoop.java:698 事件行动值 : 8 执行到这里表示已经连接成功了 死循环事件选择器阻塞轮询中...... io/netty/channel/nio/NioEventLoop.java:447

    总结

    107:

    ServerBootstrap 与 Bootstrap 只有名字不一样么?

    对于ChannelInitializer看起来客户端与服务端似乎是一样的.

    connect.channel().closeFuture().sync();

    这一步代码究竟干了什么???

    2020年9月23日 客户端拆包粘包. 编解码. 数据收发

    预习

    本章节涉及到的知识点有;LineBasedFrameDecoder、StringDecoder、StringEncoder、ChannelInboundHandlerAdapter等。

    108 案例跑通

    连接成功, 通道 ID : 4c678cfc 死循环事件选择器阻塞轮询中...... io/netty/channel/nio/NioEventLoop.java:447 发生事件 ---- strategy = 1 选择器事件处理过程 io/netty/channel/nio/NioEventLoop.java:698 事件行动值 : 8 执行到这里表示已经连接成功了 死循环事件选择器阻塞轮询中...... io/netty/channel/nio/NioEventLoop.java:447 发生事件 ---- strategy = 1 选择器事件处理过程 io/netty/channel/nio/NioEventLoop.java:698 事件行动值 : 1 死循环事件选择器阻塞轮询中...... io/netty/channel/nio/NioEventLoop.java:447 发生事件 ---- strategy = 1 选择器事件处理过程 io/netty/channel/nio/NioEventLoop.java:698 事件行动值 : 1 断开连接/127.0.0.1:8080

    总结

    108:

    ChannelInboundHandlerAdapter 加入重点关注类. 对其整个生命周期仍是一团迷雾. 目前积累了较多问题. 需通过源码分析才可找到答案. 建议先继续往下看. 别分神. 问题先记在这里

    对 ChannelInboundHandlerAdapter 中提供的方法已经比较清楚

    channelInActive 不活跃时/断开连接时 回调这个方法channelActive 当将数 sockerchannel 引入到自定义 handler 时会调用这个方法exceptioncaugth 出现异常时channelRead 当有数据可读时调用

    2020年9月24日 自定义编解码

    预习

    ByteToMessageDecoder、MessageToByteEncoder;

    案例采用 02, 03 标识进行分包

    还么开始, 我的问题就出现了. 在客户端或服务端, 均出现了编码通道和解码通道, 这是什么意思 难道不是服务端发送的时候编码, 接收的时候解码. 区分开来吗? 放在一起? 怎么区分?? 难道说根据 这节通道的内容有选择的执行? channel通道跳跃??

    109 案例跑通 - 失败

    没跑通

    总结

    109:

    字节 -> 字符 解码字符 -> 字节 编码案例没跑通原因出现在自定义编解码问题上. 问题暂时搁置.

    2020年9月24日 ChannelOutboundHandlerAdapter

    预习

    ChannelOutboundHandlerAdapter, 终于到了前面有疑惑的地方了.

    110 案例跑通

    2333 端口已开启, 造作中.... 链接报告信息:有一客户端链接到本服务端/127.0.0.1:2689 Fri Sep 25 00:19:40 CST 2020接收到消息:乌拉踏踏 Fri Sep 25 00:19:40 CST 2020接收到消息:I'm coming Fri Sep 25 00:19:40 CST 2020接收到消息:德玛西亚

    总结

    110:

    ChannelInboundHandler拦截和处理入站事件ChannelOutboundHandler拦截和处理出站事件ChannelHandler和ChannelHandlerContext通过组合或继承的方式关联到一起成对使用事件通过ChannelHandlerContext主动调用如read(msg)、write(msg)和fireXXX()等方法,将事件传播到下一个处理器

    注意:入站事件在 ChannelPipeline 双向链表中由头到尾正向传播,出站事件则方向相反。 当客户端连接到服务器时,Netty 新建一个 ChannelPipeline 处理其中的事件,而一个 ChannelPipeline 中含有若干 ChannelHandler 。 如果每个客户端连接都新建一个 ChannelHandler 实例,当有大量客户端时,服务器将保存大量的ChannelHandler实例。 为此,Netty提供了Sharable注解,如果一个 ChannelHandler 状态无关,那么可将其标注为Sharable,如此,服务器只需保存一个实例就能处理所有客户端的事件。

    这一节内容比较靠近原理. 包括之前疑问的几个点也有涉及, 比如 ChannelOutboundHandler , 一个出站事件拦截和处理处理器. 对于出站入站的概念还是有点模糊. 不是很清楚, 还需要打磨.

    2020年9月25日 Netty 之 UDP

    预习

    Netty 的 UDP 使用, DataGram

    Internet 协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP,User Datagram Protocol)。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据报的方法。RFC 768 [1] 描述了 UDP。 Internet 的传输层有两个主要协议,互为补充。无连接的是 UDP,它除了给应用程序发送数据包功能并允许它们在所需的层次上架构自己的协议之外,几乎没有做什么特别的的事情。面向连接的是 TCP,该协议几乎做了所有的事情。

    111 案例跑通

    2020-09-25 11:17:48 UDP服务端接收到消息:你好端口7397的bugstack虫洞栈,我是客户端小爱,你在吗! 2020-09-25 11:17:48 UDP客户端接收到消息:微信公众号:bugstack虫洞栈,通知:我已经收到你的消息

    总结

    111:

    发送方每次发包都要指定地址, 如果是接收方, 可以直接拿着通道继续输出对于服务端的几个参数的问题 SO_BROADCAST 广播. 在这个案例中没有介绍具体的内容.读写缓冲区的配置. 不设置可不可以? 目前还没去搜索答案. 继续搁置. 等待答案浮出水面

    2020年9月25日 Netty 之 Http

    预习

    Netty不仅可以搭建Socket服务,也可以搭建Http、Https服务。本章节我们通过一个简单的入门案例,来了解Netty搭建的Http服务,在我们后续的Netty网关服务中会使用到这样的功能点。

    超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。

    在后端开发中接触HTTP协议的比较多,目前大部分都是基于Servlet容器实现的Http服务,往往有一些核心子系统对性能的要求非常高,这个时候我们可以考虑采用NIO的网络模型来实现HTTP服务,以此提高性能和吞吐量,Netty除了开发网络应用非常方便,还内置了HTTP相关的编解码器,让用户可以很方便的开发出高性能的HTTP协议的服务,Spring Webflux默认是使用的Netty。

    112 案例跑通

    总结

    112:

    通过 HttpRequestDecoder 忘写了解到. socket 建立的这根 channel , 我们使用 pipeline 接入新的 channel 时, 更像加入了一个拦截器, 将他引入到了我们希望 channel 中 比如你不写 HttpRequestDecoder ,那么在对事件的处理时, HttpRequest 这部分数据你就取不到. 因为你没有对应的管道给它供他使用.

    早前了解 tomcat 源码时, servlet 对于请求的分发处理, 看起来正是和此处的写法相同. 设计如此巧妙, 让人神往

    Processed: 0.008, SQL: 9