netty权威指南读书笔记_2020.01.25

第一章:linux io 模型

轮训:阻塞,非阻塞

回调:I/O多路复用,信号,异步

多路复用:linux的select/poll,将fd注册到select/poll上,selcet/poll通过线性遍历执行回调,fd大小有限制,通过将fd注册到select/poll,可以直接处理多个客户端的请求(并发),由于select的fd大小限制,线性遍历导致性能也是线性下降,使用epoll替代了select,fd大小不受限制,在socket层回调,只有活跃socket才会回调,无需遍历,使用内存映射,减少内存复制,epoll的api也更简洁

第二章:BIO / NIO / AIO

BIO:同步阻塞IO,主要使用 ServerSocket Socket 对象,通过Input/OutputStream进行读写,每一个Client的请求,都相应的要在服务端创建一个线程来处理请求,可以直接压垮服务器

改进BIO:把请求封装,放到任务队列,使用线程池处理任务队列,由于同步阻塞IO的原因如写快读慢,就可能导致线程处理过慢,任务队列累计,最终拒绝服务

NIO:异步非阻塞IO,有阻塞/非阻塞模式,java 1.4使用的还是select模型,改进后使用了epoll,一个线程就可以处理多路复用的轮训就可以实现高并发的客户端接入,如果channel的活跃数量上升,epoll也会退化成select。主要使用ServerSocketChannel SocketChannel Selector对象,通过Channel加缓存Buffer进行读写,每一个Client都是多路复用器上的一个Channel,通过轮训多路复用器上的活跃channel,判断事件类型,并进行对应的处理,由于缓存的读写都是非阻塞的,所以存在读写半包的情况,NIO只是相对于BIO是异步的。

AIO:真正的异步IO,主要使用AsynchronousSererverSocketChannel,AsynchronousSocketChannel 对象,通过Channel+Buffer异步读写数据

第三章:Netty NIO 实现一个时间服务器

NioEventLoopGroup 线程组,包含了一组NIO线程

Bootstrap 辅助启动类 bind方法,connect方法

ChannelInitializer

ChannelHandlerAdapter 网络事件的读写操作

Unpooled

ByteBuf

ChannelHandlerContext

第四章:拆包/粘包的netty解决方案

拆包/粘包的产生:应用层数据大于缓冲区的大小,TCP协议的读写滑动窗口会动态变化,TCP本身有个MSS的限制会导致TCP层的数据报拆分,以太网层有MTU限制会导致数据报拆分,

解决方案:定长分割,分隔符分割,协议分割

第五章:字符分割器,定长分割器的使用

ServerBootStrap,BootStrap

childHandler,handler

ByteBuf同一个对象发多次的情况,要重置读写位置

第六章:编解码

java序列化:java内部的序列化协议,无法跨语言,编码性能低,编码之后的码流也太大

编解码框架:

Google Protobuf 机构化存储,高效,跨语言,跨平台,扩展性,官方支持java

Facebook Thrift 解决大数据量的传输通信,跨语言调用

JBoss Marshalling JBoss内部使用,应用范围有限

第七章:MessagePack

高效的二进制序列化框架,码流小,跨语言,api简单,Message注解

解码channel:MessageToMessageDecoder

编码channel:MessageToByteEncoder

数据报定长分割器:

LengthFieldPrepender:使用n个字节表示消息体长度

LengthFieldBasedFrameDecoder:

第八/九章

Google Protobuf ,JBoss Marshalling

第十章:Http协议开发(实现一个十分轻量级的服务端,跟Tomcat相比)

实现Http协议的文件服务器:

HttpRequestDecoder

HttpObjectAggregator 将http request/response 聚合成 => FullHttpRequest,FullHttpResponse

HttpResponseEncoder

ChunkWriteHandler 支持异步发送大的码流

SimpleChannelInboundHandler

实现Http协议+json的协议栈:

客户端:Order对象转json的encoder,json转Order对象的decoder

服务端:json转Order的decoder,Order转json的encoder

第十一章:websocket开发

http的缺点:半双工,协议复杂,长轮询容易受攻击

websocket:全双工;对代理,防火墙,路由器透明;协议简单(没有cookie,头部信息,身份验证);没有安全开销;ping/pong保活;实时推送,无需轮询

流程:client先使用http协议发起一次协议升级请求,Upgrade:websocket,server回复协议升级,即握手成功,所在netty里面需要判断消息类型(http请求消息,还是websocket的消息),js好像没有发送ping的api,可以在server发送ping,接收client的pong

核心类:

WebSocketFrame:websocket 通信的抽象消息体(有关闭帧/文本/二进制/ping/pong消息等实现)

WebSocketServerHandshakerFactory 构建协议升级请求的response,并动态加入WebSocketEncoder,WebSocketDecoder

第十二章:私有协议开发

定义消息结构,开发编解码器,开发消息处理器,处理自己关心的数据,然后放行数据,组合成client/server

LengthFieldBasedFrameDecoder:定长分割器,要除去本身代表长度的字节长度,

例如:|code(4B)|lenght(4B)|1B|2B|3B|4B| 放到ByteBuf之后readableBytes是18,除去code和length之后的协议长度是10

MesagePack的限制是必须要使用Message注解,对于复杂对象+历史遗留原因,可能会出现某些对象无法加上该注解

fireChannelRead,fireXXX channel链放行

源码解读:

ByteBuf:缓冲区

堆外内存:I/O通信的时候少一次用户进程到内核的复制,高性能,但是内存回收不方便

堆内存:用于I/O通信的时候,需要复制到内核的缓冲区,影响性能,但是内存管理方便

池化内存区:对于高并发的情况,频繁申请/释放大的内存块影响性能,所以有了池化内存区,可以复用内存区,预先申请一大块的内存区域,分割成若干chunk,chuank再分为若干page,page组织方式是二叉树

非池化内存:每次都重新开辟一块内存

Channel/Unsafe

channel负责链路的建立/关闭,读写,具体实现由Unsafe实现,可以获取eventloop

ChannelPipeline/ChannelHandler

ChannelPipeline用链表的形式组织ChannelHandler,支持动态增删改查

EventLoop

处理I/O任务跟非IO任务

第二十章:netty 架构:

高性能:异步非阻塞IO,堆外直接内存的使用,内存池的使用,环形缓冲区,reactor模型,正确的处理并发

可靠:读写空闲时间的心跳检测

第二十一章:多线程在netty中的使用

synchronized() {

while(conditon){

await();

}

}

volatile 变量线程修改可见,避免指令重排

结合AtomicIntegerFiledUpdater ,实现volatile变量的并发更新问题