1.netty是一个 异步\ 基于事件驱动的网络应用
tcp/ip---->原生的JDK IO/网络---->nio--->netty----->RPC
AVRO 实现数据文件共享
https://netty.io/wiki/related-projects.html
AKKA:
FLINK : 内存级的计算框架
SPARK: 内存级的计算框架
一.Netty介绍和应用场景
做基于网络的高并发,异步高性能的通信框架
互联网行业
游戏行业
大数据领域
BIO 同步阻塞, 适用于链接数目较小且固定的架构, 这种方式对服务器资源要求较高, 并发局限于应用中: 通过线程池改善实现多个客户的链接
NIO 同步非阻塞 一个线程处理多个请求, 即客户端发送的请求都会注册到多路复用器上, 多路复用器轮询到链接有I/O请求就会处理
NIO适用于链接数目多且链接比较段架构,比如聊天服务器弹幕系统 服务期间通讯等
Netty可以做长连接
AIO适用于链接数据较多且链接比较长 重操作 目前没有广泛使用
BIO编程简单流程
1.服务端启动给一个ServerSocket
2.客户端启动Socket对服务器进行通信,默认情况下服务端需要对每个客户建立一个线程与之通讯
3.客户端发出请求后, 先自选服务器是否有线程响应,如果没有则会等待,或者被拒绝
4.如果有响应, 客户端线程会等待线程结束后才继续执行
二.NIO
NIO三大核心部分:channer buffer selector
1.每一个channel都对应一个buffer
2.一个selector对应一个线程, 一个线程可以对应多个channel(连接)
3.多个channel可以注册到一个selector
4.程序切换到哪个channel是由事件决定的, Event就是一个很重要的概念
5.selector会根据不同的时间,在各个通道上切换
6.Buffer就是一个内存块,底层是有一个数组
7.数据的读取写入是通过Buffer, 这个和BIO本质区别, BIO中要么是输入流或者输出流, 不能双向,但是NIO的Bufer是可以读也可以写,需要使用方法 flip()切换
8.channel是双向的,可以反映底层操作系统的情况,比如Linux底层的操作系统通道就是双向的
面向缓冲区 面向块编程
比较: bio面向流, nio面向块, 块比流高效很多
三.Buffer
四个属性:capacity limit position mark
常用方法: capacity () position() postion(int newPosition) 设置缓冲区的位置
limit() limit(int newLimit) limit属性指的的第几个不是下角标比如 limit(3) 是第三个下脚标为2
clear()清除缓冲区 flip()读写切换---》重置mark和position
1.ByteBuffer常用方法:
allocateDirect(int capacity) // 创建直接缓冲区
allocate(int capacity) 创建缓冲区
get() 从当前位置position上get, get之后position会自动 +1
get(int index) // 根据下脚标获取数据
听到11讲
put(byte b) // 从当前位置上添加,put之后,position会自动 +1
put(int index, byte b)// 从绝对位置上put即将元素放到下脚标为index的位置上
通道Channel,类似流,内置于JAVA流中
通道可以同时进行读写,而流自能读或者写
FileChannel: 用于文件数据读写
DatagramChannel:用户UDP数据读写
ServerSocketChannel 和 SocketChannel 用于TCP的数据读写
常用方法FileChannle
read(ByteBuffer dst) : 从通道读取数据到缓冲区
write(ByteBuffer src): 把缓冲区的数据写入 通道
transferFrom(ReableByteChannel src, long position, long count), 从目标通道中复制数据到当前通道
transferTo(long position, long count, WritableByteChannel target), 把数据从当前通道复制给目标通道 零拷贝
Buffer可以转为只读buffer
18讲
1.MappedByteBuffer 可以让文件直接在内存(堆外内存)中修改,而如何同步到文件由NIO完成
说明:1.MappedByteBuffer 可以让文件直接在内存(堆外内存)中修改,操作系统不需要拷贝一次
2.前面的读写都是通过一个Buffer完成, NIO还支持通过多个Buffer(即Buffer数组)完成读写操作,即Scatering(分散) 和 Gathering(聚合)
scattering: 将数据写入buffer时,可以采用buffer数组依次写入【分散】
Gathering: 从buffer读取数据时,可以采用buffer数组依次读取【聚合】
Selector(选择器)
1.基本介绍
JAVA
1)Seletor 能够检测多个注册的通道上是否有事件发生(注意:多个Channlel以事件的方式注册到同一个Selector),如果事件发生,便获取事件然后对每个事件进行处理,这样可以只用一个线程去管理多个通道,也就是管理多个连接和请求
2)只有在 连接/通道 真正有读写事件发生时,才会进行读写,大大减少系统开销,并且不同为每个连接都创建一个线程,不同去维护多个线程
3)避免了多线程之间的上下文切换导致的开销
特点:
1)Netty 的IO 线程 NioEventLoop聚合了Selector, 可以处理多个客户端连接
2)
Selector类的相关方法
Selector是一个抽象类
静态方法open();// 得到一个选择器对象
select(long timeout); // 监控所有注册的通道,当其中有IO操作进行时,将对应的SelectionKey加入到内部集合中并返回,参数用来设置超时时间
public Set<SelectionKey> selectionKeys(); // 从内部集合中得到所有的SelectionKey
Selector相关方法注意事项
selector.select: 阻塞
selector.select(1000) // 阻塞1000毫秒, 然后返回
selector.wakeup() 喚醒 selector
selector.selectNow// 不阻塞,立马返回
selector selectionKey serverSocketChannel socketChannel关系:
1.当客户段端连接时,会通过ServerSocketChannel得到对应的SocketChannel,ServerSocketChannel本身也要注册到Selector上,关心链接为连接Accept
2. Selector进行监听, select 方法,返回有事件发生的通道个数
3. 将SocketChannel注册到Selector上 register(Selector sel, int ops), 一个Selector可以注册多个SocketChannel
4. 注册后返回一个SelectionKey, 会和该Selector关联(集合)
5. 进一步得到各个SelectionKey(有事件发生的)
6. 再通过SelectionKey反向获取SockeyChannel,方法channel()
7.可以通过得到的Channel,完成业务处理
注意:客户端和服务端的SocketChannel要么同时配置为非阻塞要么都配置为阻塞
26讲
SelectionKey
1)SelectionKey, 表示Selector和网络通道的注册关系,共4种:
int OP_Accept = 1 << 4: 有新网络连接可以accept,值未16(1向左位移4位)
int OP_CONNECT = 1 << 3; : 代表连接已经建立, 值为8
int OP_READ = 1 << 0: 代表度操作,值为1
int OP_WRITE = 1 << 2: 代表写操作,值为4
2)SelectionKey相关方法
selector();// 返回与之关联的Selector对象
channel(); // 得到与之关联的通道
attachment(); // 得到与之关联的共享数据
// interestOps(int ops); // 设置或者改变监听事件
isAcceptable(); 是否可以accept
isReadable(); 是否可以读
isWriteable(); 是否可以写
ServeSocketChannel
1)在服务端监听新的客户端socket的连接
2)相关方法
open();得到一个ServerSocketChannel通道
bind(SocketAddress local); 设置服务端端口
configureBlocking(boolean block); 设置阻塞或非阻塞模式,取值false表示采用非阻塞模式
accept(); 接受一个链接,返回代表这个连接的通道对象
register(Selector sel, int ops); 注册一个选择器并设置监听事件
SocketChannel
1)SocketChannel,网络IO通道,具体负责进行读写操作。NIO把缓冲区数据写入到通道,或者把通道的数据读到缓冲区
2)相关方法
open(); 得到一个SocketChannel通道
configureBlocking(boolean block); 设置阻塞或者非阻塞模式,取值false表示非阻塞么模式
connect(SocketAdress remote); 链接服务器
finishConnect(); 如果上面的方法链接失败,接下来就要通过该方法完成链接操作
write(Bytebuffer src); 往通道写数据
read(Bytebuffer dst); 从通道里读数据到缓冲区
register(Selector sel, int ips, Object att); 注册一个选择器并设置监听事件,最后一个参数可以设置共享数据
close();关闭通道
NIO 网络编程应用示例-群聊系统
1.先编写服务端
1.1服务器启动并监听 6667
1.2服务器接收客户端消息,并实时转发(处理上线和下线)
2.编写客户端
1.1链接服务器
1.2发送消息
1.3接收消息
下次观看33讲
NIO与零拷贝
是指从操作系统角度看的,是没有CPU拷贝
1)零拷贝网络编程的关键,
2)mmap(memery map 内存映射) sendFile
DMA(direct memeory access )拷贝:直接内存拷贝不使用CPU
mmap优化: 不是真正的零拷贝只是减少了一次拷贝
1)mmap通过内核映射, 将文件映射到内核缓冲区,同时,用户空间可以共享内核空间资源。这样,在进行网络传输时,就可以减少内核口空间到用户空间的拷贝次数
sendFile优化:拷贝减少为3次,状态减少为2次
1)Linux2.1版本提供了sendFile函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到Socket Buffer,同时,由于和用户态完全无关,就减少了一次上下文切换
3) Linux2.4中,做了一些修改,避免了从内核缓冲区拷贝到Socket buffer的操作, 从而再一次减少了数据拷贝
直接从kenel buffer到协议栈
这里其实有一次CPU拷贝kenel buffer -> scoket buffer 但是拷贝信息很少,比如 length, offset,消耗低,可以忽略
NIO零拷贝案例
transferTo传一个大文件
在window下最多8M一次, linux下可以一次传输
零拷贝的再次理解
1)零拷贝是从操作系统角度来说的,因为内核缓冲区之间,没有数据时重复的(只有kenel buffer一份数据)
2)零拷贝不仅仅带来更少的数据复制,还能带来其他的性能优势,例如更少的上下文切换,更少的CPU缓存伪共享及无CPU校验和计算
零拷贝是进行网络数据传输的非常重要的优化手段
mmap 和 sendFile的区别
1)mmap 适合小数据读写,sendFile适合大文件传输
2) mmap 需要4次(也可以说3次从中间计算)上下文切换, 3次数据拷贝; sendFile 需要 3次上下文切换,最少2次数据拷贝
3)sendFile可以利用DMA方式,减少CPU拷贝, mmap则不能(必须从内核拷贝到Socket缓冲区)
Netty概述
原生NIO存在的问题
1)NIO的类库和API繁杂
Netty is an asynchronous event-driven network application framework
for rapid development of maintainable high performance protocol servers & clients.
Netty官网说明
1)Netty室友JBOSS提供的一个Java开源框架。Netty提供异步的、基于事件驱动的网络应用程序框架,用以快速开发高性能、高可靠的网络IO程序
2)Netty可以帮助你快速、简单的开发出一个网络应用,相当于简化和流程化NIO的开发过程
3)Netty是目前最流行的NIO框架,Netty在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,知名的Elasticsearch 、Dubbo框架内部都采用了Netty
线程模型基本介绍
1)目前存在的现成模型
传统阻塞 I/O服务模型
Reactor模式
2)根据Reactor的数量和处理资源池线程的数量不同,有3中典型的实现
单Reactor单线程
单Reactor多线程
主从 Reactor多线程
3)Netty线程模式(Netty主要基于主从Reactor多线程模型做了一定的改进,其中主从Reactor多线程模型有多个Reactor)
传统阻塞I/O服务模型
工作原理图
行色的框表示对象,蓝蓝色的框表示线程
白色的框表示方法(API)--->实线框
模型特点
1)采用阻塞IO模式获取输入的数据
2)每个连接都需要独立的线程完成数据的输入,业务处理,数据返回
问题分析
1)当并发数很大,就会创建大量的线程,占用很大的系统资源
2) 链接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在read操作上造成线程资源的浪费
Netty的核心组件
Bootstrap ServerBootstrap
ServerBootstrap的handle给bossGroup加处理器;childHandle给workerGroup加处理器
Future ChannelFuture
1)Netty中所有的IO操作都是异步的 可以通过Future和ChannelFuture注册监听
2)常见方法:
Channel channel() 返回当前正在进行IO操作的通道
ChannelFuture sync() 等待异步操作执行完毕, 把同步变成异步操作
Channel
NioServerSocketChannel
NioSocetChannel