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