0%

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
final long nanoTime = System.nanoTime();
final long timeMillis = System.currentTimeMillis();
final long xorValue = nanoTime ^ timeMillis;
final long reverse = Long.reverse(xor);

log.info("{}", String.format("%64s", Long.toBinaryString(nanoTime)));
log.info("{}", String.format("%64s", Long.toBinaryString(timeMillis)));
log.info("{}", String.format("%64s", Long.toBinaryString(xorValue)));
log.info("{}", String.format("%64s", Long.toBinaryString(reverse)));

/*
输出如下所示
: 1111111100000110011110111010001111000001000000 -nanoTime
: 10111100001101101100000001000101100110110 -timeMillis
: 1111101011100111110011011010000111101101110110 -xorValue
: 110111011011110000101101100111110011101011111000000000000000000 -reverse,也即将xorValue换成2进制后,头尾互掉后的值
*/

至于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. initChannel(Channel)中,执行了了一个重要的操作:new了一个匿名的ChannelInitializer并且addLast到传入的ChannelChannelPipeline中,该ChannelInitializer主要执行了两件事:

  • ChannelInitializer.initChannel回调被调用时,将ServerBootstrapChannelHandler addList到Channel.pipeline(),而ServerBootstrapChannelHanlder可以在构造时通过handler(ChannelHandler)传入,例如传入一个new LoggingHandler(LogLevel.INFO),从而实现log输出,有两点需要注意:
    • ChannelInitializer.initChannel回调时,发生在ServerBootstrap.group中(也即大家常用的BossGroup),此时还没有注册到ServerBootstrap.childGroup中(也即大家常用的WorkerGroup)。
    • 该ChannelHandler全局只有一个,会被所有的EventLoop共享包括ServerBootstrap.groupServerBootstrap.childGroup在内的所有EventLoop
  • ChannelInitializer.initChannel回调被调用时,调用了ChannelEventLoop(该EventLoop隶属于父线程池)并执行了一个异步任务:向Channel.pipeline()中addLast了一个new ServerBootstrapAcceptorServerBootstrapAcceptor执行了两件重要的事情:
    • ServerBootstrap.childHandler addLast到Channel.pipeline()
    • 调用ServerBootstrap.childGroup.register(Channel)Channel注册到子线程池(所以AbstractChannel声明成员变量eventLoop时使用了volatile关键字修饰)