netty源码学习笔记
ChannelId的那些事儿
ChannelId组成
ChannelId默认由io.netty.channel.DefaultChannelId实现,最终结果存储在一个data的byte数组中,data数组内由5个字段组成,从低位到高位分别是:6或8字节的MACHINE_ID,4字节的PROCESS_ID,4字节的SEQUENCE,8字节的TIMESTAMP和4字节的RANDOM,下表以8字节的`MACHINE_ID举例:
| 字段 | RANDOM | TIMESTAMP | SEQUENCE | PROCESS_ID | MACHINE_ID |
|---|---|---|---|---|---|
| 字节 | [27-24](4字节) | [23-16](8字节) | [15-12](4字节) | [11-8](4字节) | [7-0](6或8字节) |
| 说明 | 随机 | 自定义 | 初值为0,每次递增1 | 优先从系统属性io.netty.processId获取,类型为int | 优先从系统属性io.netty.machineId获取,类型为String,长度为17或23 |
在DefaultChannelId的实现中,**TIMESTAMP**字段并没有直接使用系统的时间戳,例如:System.nanoTime(),而是自定义了一个时间戳生成方法:Long.reverse(System.nanoTime()) ^ System.currentTimeMillis(),举例如下:
1 | final long nanoTime = System.nanoTime(); |
至于Netty为什么这么设计,只是在源码中有一句简单的注释:// timestamp (kind of),翻译过来就是一种(Netty式的)时间戳,可能就是想这么实现吧。
ChannelId的表示
ChannelId有两个函数:asShortText()和asLongText(),
asShortText()使用懒加载的方式,输出**RANDOM字段转换成16进制并存储到String shortValue,例如:60fb0e5f**asLongText()也采用懒加载的方式,将data数组中的5个字段转换成16进制以后,从**MACHINE_ID开始,到RANDOM结束,用“-”连起来,最后存储到String longValue中,例如:00000000000000e0-00003738-00000000-3a5715eac38e517e-60fb0e5f**
bind那些事儿
1 | ServerBootstrap->ReflectiveChannelFactory: newChannel() |
在上述的1. initChannel(Channel)中,执行了了一个重要的操作:new了一个匿名的ChannelInitializer并且addLast到传入的Channel的ChannelPipeline中,该ChannelInitializer主要执行了两件事:
- 当
ChannelInitializer.initChannel回调被调用时,将ServerBootstrap的ChannelHandleraddList到Channel.pipeline(),而ServerBootstrap的ChannelHanlder可以在构造时通过handler(ChannelHandler)传入,例如传入一个new LoggingHandler(LogLevel.INFO),从而实现log输出,有两点需要注意:ChannelInitializer.initChannel回调时,发生在ServerBootstrap.group中(也即大家常用的BossGroup),此时还没有注册到ServerBootstrap.childGroup中(也即大家常用的WorkerGroup)。- 该ChannelHandler全局只有一个,会被所有的EventLoop共享包括
ServerBootstrap.group和ServerBootstrap.childGroup在内的所有EventLoop
- 当
ChannelInitializer.initChannel回调被调用时,调用了Channel的EventLoop(该EventLoop隶属于父线程池)并执行了一个异步任务:向Channel.pipeline()中addLast了一个new ServerBootstrapAcceptor,ServerBootstrapAcceptor执行了两件重要的事情:- 将
ServerBootstrap.childHandleraddLast到Channel.pipeline() - 调用
ServerBootstrap.childGroup.register(Channel)将Channel注册到子线程池(所以AbstractChannel声明成员变量eventLoop时使用了volatile关键字修饰)
- 将