Java网络编程及Netty框架
2021-03-27 19:36:30 138 举报
AI智能生成
Java网络编程是一种基于Java语言进行的网络通信技术,它允许计算机之间通过网络进行数据传输和通信。在Java网络编程中,常用的API有Socket、ServerSocket等。而Netty框架是一个高性能、异步事件驱动的网络应用框架,用于快速开发可维护的高性能协议服务器和客户端。它提供了一套易于使用的API,使得开发者能够快速构建复杂的网络应用程序。Netty框架广泛应用于分布式系统、游戏服务器、聊天室等领域。总之,Java网络编程和Netty框架为开发者提供了强大的工具,帮助他们实现高效、可靠的网络通信。
作者其他创作
大纲/内容
缓冲区<br>
内存中预留指定大小的存储空间用于对IO的数据作临时存储<br>
优点<br>
减少实际的物理读写次数
内存区域重复使用,减少了动态分配/关闭内存的次数<br>
网络协议<br>
IO模型
Java原生提供
BIO<br>
NIO
组成
Channel
表示 IO 源与目标打开的连接,是双向的,但不能直接访问数据,只能与Buffer 进行交互
FileChannel的read方法和write方法都导致数据<u><b>复制了两次</b></u>!
Buffer<br>
与Channel进行交互,数据是从Channel读入缓冲区,从缓冲区写入Channel中的
方法
flip方法
clear方法
rewind方法
<font color="#f15a23"><b>直接缓冲区(DirectByteBuffer)</b></font>可减少一次系统空间到用户空间的拷贝。<br>直接缓冲区主要分配给那些易受基础系统的本机I/O 操作影响的大型、持久的缓冲区。<br>如果数据量比较小的中小应用情况下,可以考虑使用<b><font color="#f15a23">heapBuffer</font></b>,由JVM进行管理。<br>
Buffer创建和销毁的成本更高,不可控,通常会用<font color="#f15a23"><b>内存池</b></font>来提高性能。
Selector
可使一个单独的线程管理多个Channel<br>
方法
open方法:可创建Selector
register方法:向多路复用器注册通道,可以监听的事件类型:<b><font color="#f15a23">读、写、连接、accept</font></b>;<br>注册事件后会产生一个<b>SelectionKey</b>:它表示SelectableChannel 和Selector 之间的注册关系
wakeup方法:使尚未返回的第一个选择操作立即返回
建立过程
1.Selector.open():打开一个Selector;<br>2.ServerSocketChannel.open():创建服务端的Channel;<br>3.bind():绑定到某个端口上。并配置非阻塞模式;<br>4.register():注册Channel和关注的事件到Selector上;<br>5.select()轮询拿到已经就绪的事件<br>
AIO
netty框架<br>
组件
Channel
socket
一个channel在生命周期内只注册一于个EventLoop
EventLoop
控制流、多线程处理、并发
在生命周期内只和一个Thread绑定<br>
会被分配给 一个或多个Channel<br>
ChannelFuture
异步通知
Netty 框架中所有的 I/O 操作都为异步的,因此我们需要 ChannelFuture 的 addListener()注册一个 <b>ChannelFutureListener 监听事件</b>,当操作执行成功或者失败时,监听就会<b>自动触发返回结果</b>
EventLoopGroup
包含一个或多个EventLoop<br>
ChannelHandler
所有处理入站和处理出站数据的应用程序逻辑的地方<br>
主要用来处理各种事件,这里的事件很广泛,比如可以是连接、数据接收、异常、数据转换等
ChannelPipeLine<br>
提供了ChannelHandler 链的容器,并定义了用于在该链上传播入站和出站事件流的API
ChannelHandlerContext
当ChannelHandler 被添加到ChannelPipeline 时,它将会被分配一个ChannelHandlerContext,其代表了ChannelHandler 和ChannelPipeline 之间的绑定
内置通信传输模式<br>
NIO<br>
NioSocketChannel,异步的客户端 TCP Socket 连接
NioServerSocketChannel,异步的服务器端 TCP Socket 连接
NioDatagramChannel,异步的 UDP 连接
NioSctpChannel,异步的客户端 Sctp 连接
NioSctpServerChannel,异步的 Sctp 服务器端连接 这些通道涵盖了 UDP 和 TCP网络 IO以及文件 IO.
Epoll
用于Linux的本地非阻塞传输
Epoll是适用于Linux系统。而NIO则适用于所有的操作系统
Epoll的速度 > NIO速度
OIO
旧的阻塞I/O
Local
用于JVM内部通信的Local传输
Embeded
常用来编写单元测试
ByteBuf
Netty 的ByteBuffer 替代品是ByteBuf,一个强大的实现,既解决了JDK API 的局限性,又为网络应用程序的开发者提供了更好的API。<br>
ByteBuf 维护了<b>两个不同的索引</b>,名称以read 或者write 开头的ByteBuf 方法,将会推进其对应的索引,而名称以set 或者get 开头的操作则不会<br>
使用<br>
堆缓冲区
直接缓冲区<br>
复合缓冲区
概念及API<br>
随机访问/顺序访问/读写操作<br>
可丢弃字节/可读字节/可写字节<br>
索引管理<br>
查找操作<br>
派生缓冲区<br>
引用计数<br>
<br>
零拷贝技术
<ul><li><font color="#f15a23">支持自动扩容(<b>4M</b>)</font>,保证put方法不会抛出异常、通过内置的复合缓冲类型,实现零拷贝(zero-copy);</li><li>不需要调用flip()来切换读/写模式,读取和写入索引分开;方法链;</li><li>引用计数基于AtomicIntegerFieldUpdater用于内存回收;</li><li>PooledByteBuf采用二叉树来<font color="#f15a23">实现一个</font><b style=""><font color="#f44336">内存池</font></b><font color="#f15a23">,集中管理内存的分配和释放</font>,不用每次使用都新建一个缓冲区对象。</li><li>UnpooledHeapByteBuf每次都会新建一个缓冲区对象。</li></ul>
TCP/IP粘包半包<br>
原因
<ul><li>应用程序写入数据的字节大小大于<u>套接字</u>发送缓冲区的大小</li><li>进行MSS大小的TCP分段</li><li>以太网的payload大于MTU进行IP分片</li></ul>
解决方式
消息定长
FixedLengthFrameDecoder类
报文 尾增加特殊字符分割
行分隔符类:LineBasedFrameDecoder
自定义分隔符类 :DelimiterBasedFrameDecoder
将消息分为消息头和消息体
LengthFieldBasedFrameDecoder类。分为有头部的拆包与粘包、长度字段在前且有头部的拆包与粘包、多扩展头部的拆包与粘包。
编解码器
编码器
将消息编码为字节<br>
将消息编码为另一种消息<br>
解码器
将字节解码为消息<br>
将一种消息类型解码为另一种<br>
TooLongFrameException<br>
综述
特点
高并发、传输快、封装好<br>
优势
使用简单、功能强大、定制能力强、性能高、稳定、社区活跃<br>
高性能原因<br>
IO线程模型(IO多路复用)<br>
同步非阻塞、用更少的资源干更多的事
内存零拷贝<br>
不需要将buffer从一个内存区域移动到另一个内存区域
从OS角度:避免在用户态和内核态之间拷贝数据
netty的Zero-copy 与上面我们所提到到 OS 层面上的 Zero-copy 不太一样, <br>Netty的 Zero-coyp 完全是在用户态(Java 层面)的, <br>它的 Zero-copy 的更多的是偏向于 优化数据操作 这样的概念<br>
Netty 提供了 CompositeByteBuf 类, 它可以<b><font color="#f15a23">将多个 ByteBuf 合并为一个逻辑上的 ByteBuf,</font> </b>避免了各个 ByteBuf 之间的拷贝.
通过 wrap 操作, 我们可以<b><font color="#f15a23">将 byte[] 数组、ByteBuf、ByteBuffer等包装成一个 Netty ByteBuf 对象</font>,</b> 进而避免了拷贝操作.
ByteBuf 支持 <font color="#f44336"><b>slice 操作, 可以将 ByteBuf 分解为多个共享同一个存储区域的 ByteBuf, </b></font>避免了内存的拷贝.
通过 FileRegion 包装的<font color="#ff0000"><b>FileChannel.tranferTo 实现文件传输, 可以直接将文件缓冲区的数据发送到目标 Channel</b></font><font color="#f15a23">,</font> 避免了传统通过循环 write 方式导致的内存拷贝问题.
内存池设计<br>
申请的内存可以重复使用(直接内存)
内部通过一个二叉查找树管理内存分配情况<br>
串行化处理读写<br>
避免使用锁带来的性能开销<br>
高性能序列化协议<br>
支持protobuf等高性能序列化协议<br>
<font color="#f15a23"><b>默认</b></font>情况下Netty<font color="#f15a23"><b>启动CPU 处理器两倍的线程</b></font>,bind 完之后启动。
支持的心跳类型<br>
eaderIdleTime:为读超时时间(即测试端一定时间内未接受到被测试端消息)。
writerIdleTime:为写超时时间(即测试端一定时间内向被测试端发送消息)。
allIdleTime:所有类型的超时时间。
解决select()空转
netty解决办法
<ul><li>对Selector的select操作周期进行统计,每完成一次空的select操作进行一次计数,</li><li>若在某个周期内连续发生N(512)次空轮询,则触发了epoll死循环bug。</li><li>重建Selector,判断是否是其他线程发起的重建请求,若不是则将原SocketChannel从旧的Selector上去除注册,重新注册到新的Selector上,并将原来的Selector关闭。</li></ul>
reactor线程模型<br>
单线程模型
所有I/O操作都由一个线程完成,即多路复用、事件<font color="#f15a23">分发和处理都是在一个Reactor线程上完成</font>。<br>既要接收客户端的连接请求,向服务端发起连接,又要发送/读取请求或应答/响应消息。<br>一个NIO 线程同时处理成百上千的链路,性能上无法支撑,速度慢,若线程进入死循环,整个程序不可用,对于高负载、大并发的应用场景不合适<br>
多线程模型
<font color="#f15a23">有一个NIO 线程(Acceptor) 只负责监听服务端,接收客户端的TCP 连接请求</font>;<br><font color="#f15a23">一组线程池负责网络IO 的操作,即消息的读取、解码、编码和发送;</font><br>1 个NIO 线程可以同时处理N 条链路,但是1 个链路只对应1 个NIO 线程,这是为了防止发生并发操作问题。<br>但在并发百万客户端连接或需要安全认证时,一个Acceptor 线程可能会存在性能不足问题。<br>
主从多线程模型
<font color="#f15a23">一组线程</font>(Acceptor) 负责监听服务端,接收客户端的TCP 连接请求;<br><font color="#f15a23">一组线程</font>负责网络IO 的操作,即消息的读取、解码、编码和发送;<br>
netty中的具体运用:<br>基于多路复用器接收并处理用户请求,内部实现了<b><u>两个线程池,<font color="#f44336">boss线程池</font>和<font color="#e65100">work线程池</font></u></b>,<br><b>boss线程池的线程负责处理请求的accept事件</b>,当接收到accept事件的请求时,把对应的socket封装到一个Channel中,并交给work线程池,<br><b>work线程池负责请求的read和write事件</b>,由对应的Handler处理<br>
服务器推送技术
Ajax短轮询
实现机制
用一个定时器不停的去网站上请求数据
优点<br>
服务端基本不用改造<br>
服务器内存较友好,不需要保持连接<br>
缺点
服务器沉重压力和资源的浪费<br>
数据同步不及时
Comet<br>
实现机制<br>
基于 HTTP长连接、无须在浏览器端安装插件的“服务器推”技术
基于 <font color="#f44336"><b>AJAX 的长轮询(long-polling)方式</b></font><br>
服务器端会阻塞请求直到有数据传递或超时才返回。
客户端JavaScript 响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。
当客户端处理接收的数据、重新建立连接时,服务器端可能有新的数据到达;这些信息会被服务器端保存直到客户端重新建立连接,客户端会一次把当前服务器端所有的信息取回。
应用
Spring带来的DeferedResult<br>
Servlet3里的异步任务<br>
基于<font color="#ff0000">iframe及htmlfile流</font>的方式
服务器推送事件 Server-sent-events(SSE)<br>
原理
严格地说,HTTP 协议无法做到服务器主动推送信息。 <br>但是,有一种变通方法,就是<font color="#ff0000"><b><u>服务器向客户端声明,接下来要发送的是流信息</u>(streaming)。<br>也就是说,<u>发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。</u></b></font><br>这时,<font color="#ff0000"><b>客户端不会关闭连接,会一直等着服务器发过来的新的数据流</b></font>,视频播放就是这样的例子。<br>本质上,这种通信就是以流信息的方式,完成一次用时很长的下载。<br>
是 HTML 5 规范中的一个组成部分
SSE 是<font color="#ff0000">单向通道</font>,只能服务器向客户端发送消息,如果客户端需要向服务器发送消息,则需要一个新的 HTTP 请求。
SSE适用于更新频繁、低延迟并且数据都是从服务端到客户端。
<font color="#ff0000">文本协议</font>
优点<br>
SSE<font color="#ff0000"> 使用 HTTP 协议</font>,现有的服务器软件都支持。WebSocket 是一个独立协议。
SSE 属于轻量级,使用简单;WebSocket 协议相对复杂。
SSE 默认<u>支持断线重连</u>,WebSocket 需要自己实现。
<u>一般只用来传送文本,二进制数据需要编码后传送</u>,WebSocket 默认支持传送二进制数据。
SSE 支持自定义发送的消息类型。
WebSocket
借用了HTTP的协议来完成一部分握手<br>
实现比SSE负载,适用于需要进行复杂双向数据通讯的场景
二进制协议
特点<br>
HTML5中的协议,实现与<font color="#ff0000">客户端与服务器双向通道</font>,基于消息的<font color="#ff0000">文本或二进制数据</font>通信<br>
<u>适合于对数据的实时性要求比较强的场景</u>,如通信、直播、共享桌面,<font color="#ff0000">特别适合于客户与服务频繁交互的情况下</font>,如实时共享、多人协作等平台。<br>
采用新的协议,后端需要单独实现<br>
客户端并不是所有浏览器都支持<br>
全双工,可以进项双向的数据传输
简单(流)文本定向消息协议<br>STOMP<br>
STOMP协议的前身是TTMP协议(一个简单的基于文本的协议),专为消息中间件设计。是属于消息队列的一种协议, 和AMQP, JMS平级. 它的简单性恰巧可以用于定义websocket的消息体格式. STOMP协议很多MQ都已支持, 比如RabbitMq, ActiveMq。<br>
生产者(发送消息)、消息代理、消费者(订阅然后收到消息)<br>
STOMP是基于帧的协议<br>
序列化
影响序列化性能的关键因素
序列化后的码流大小(网络带宽的占用)
序列化的性能(CPU资源占用)
是否支持跨语言(异构系统的对接和开发语言切换)
xml方式
优点
人机可读性好,可指定元素或特性的名称
缺点
序列化数据只包含数据本身以及类的结构,不包括类型标识和程序集信息
只能序列化公共属性和字段;不能序列化方法
文件庞大,文件格式复杂,传输占带宽
适用场景
当做配置文件存储数据,实时数据转换
JSON
优点<br>
兼容性高、数据格式比较简单,易于读写、序列化后数据较小,可扩展性好,兼容性好、与XML相比,其协议比较简单,解析速度比较快
缺点
数据的描述性比XML差、不适合性能要求为ms级别的情况、额外空间开销比较大
适用场景
跨防火墙访问、可调式性要求高、基于Web browser的Ajax请求、传输数据量相对小,实时性要求相对低(例如秒级别)的服务
JDK Serializer
无法跨语言、序列化后的码流太大、序列化的性能差
不支持跨语言
FST
缺点
只争对Java,不跨语言
优点
序列化速度时JDK的10倍
序列化结果时JDK看的1/3
语法及其简洁
kryo
基于protobuf协议,只支持java语言,需要注册(Registration),然后序列化(Output),反序列化(Input)
缺点
主要支持Java
优点
同FST样,使用简单,配置灵活
性能和FST相差不多
Fastjson
采用一种“假定有序快速匹配”的算法
优点<br>
接口简单易用、目前java语言中最快的json库
缺点
过于注重快,而偏离了“标准”及功能性、代码质量不高,文档不全
适用场景
协议交互、Web输出、Android客户端
Thrift
不仅是序列化协议,还是一个RPC框架
优点
序列化后的体积小, 速度快、支持多种语言和丰富的数据类型、对于数据字段的增删具有较强的兼容性、支持二进制压缩编码
提供丰富的语言支持
缺点
使用者较少、跨防火墙访问时,<b>不安全、不具有可读性</b>,调试代码时相对困难、<b>不能与其他传输层协议共同使用(例如HTTP)</b>、<b>无法支持向持久层直接读写数据</b>,即不适合做数据持久化序列化协议
适用场景
分布式系统的RPC解决方案
Avro
Hadoop的一个子项目,解决了JSON的冗长和没有IDL的问题
优点
支持丰富的数据类型、简单的动态语言结合功能、具有自我描述属性、提高了数据解析速度、快速可压缩的二进制数据形式、可以实现远程过程调用RPC、支持跨编程语言实现
缺点
<b>对于习惯于静态类型语言的用户不直观</b>
适用场景
在Hadoop中做Hive、Pig和MapReduce的持久化数据格式
Protobuf
将数据结构以.proto文件进行描述,通过代码生成工具可以生成对应数据结构的POJO对象和Protobuf相关的方法和属性
优点
序列化后码流小,性能高、结构化数据存储格式(XML JSON等)、通过标识字段的顺序,可以实现协议的前向兼容、结构化的文档更容易管理和维护
缺点
需要<b>依赖于工具生成代码、支持的语言相对较少</b>,官方只支持Java 、C++ 、python、Go、C#
适用场景
对性能要求高的RPC调用、具有良好的跨防火墙的访问属性、适合应用层对象的持久化
protostuff
基于protobuf协议,但不需要配置proto文件,直接导包即可
Jboss marshaling
可以直接序列化java类, 无须实java.io.Serializable接口
Message pack
一个高效的二进制序列化格式
Hessian <br>
采用二进制协议的轻量级remoting onhttp工具
0 条评论
下一页