尚硅谷 学习视频
一、第一章 Netty介绍和应用场景
1、Netty介绍
1)Netty是一个异步的、基于事件驱动的网络应用框架。
2)Netty 主要针对在TCP协议下,面向clients端的高并发应用。
2、Netty应用场景
1)互联网行业
2)游戏行业
3)大数据
4)推荐书籍:Netty In Action
3、I/O模型
1)Java BIO:同步并阻塞(传统阻塞型)。
服务启实现模式:一个链接一个线程。
适用于:连接数目较少且固定的架构,对服务器资源要求比较高,并发局限于应用中。
2)Java NIO:同步非阻塞。
服务启实现模式:一个线程处理多个请求。
适用于:连接数目多且连接比较短。
3)Java AIO:异步非阻塞。
适用于:连接数目多且连接比较长。
二、BIO
安装 homebrew
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
查看homebrew 是否安装成功
brew help
使用 homebrew 安装telnet
brew install telnet
查看是否安装成功
telnet
telnet使用
telnet www.baidu.com 80
总结
三、NIO
1、介绍
1)NIO相关类被放在java.nio包
2)三大核心部分:Channel(通道)、Buffer(缓冲区)、Selector(选择区)
每个channel对应一个Buffer
Selector对应一个线程
一个线程对应多个channel(连接)
程序切换到哪个channnel是由事件决定的。Event是一个重要概念。
Selector会根据不同的事件,在各个通道上切换。
数据的读写是通过Buffer。注意BIO中要么输入流,要么输出流。NIO的Buffer是双向的,但是需要flip切换。
channel是双向的
3)NIO是面向缓冲区或面向块的编程。
2、缓冲区(Buffer)
1、基本介绍
缓冲区本质是一个读写数据的内存块。可以理解成一个容器对象(含数组)
2、Buffer 类及其子类
private int mark = -1;//标记,(一般不会主动修改)
private int position = 0;//位置,下一个被读或者写的元素的索引,每次读写缓存区时都会改变数值为下次读写作准备。
private int limit;//表示缓冲区的当前重点,不能对缓冲区超过极限的位置进行读写操作。且极限是可以修改的。
private int capacity;//容量,即可以容纳的最大数量;在缓冲区创建时被设定并且不能改变。
3、Buffer 常用方法
///JDK1.4引入
public final int capacity() {return capacity;}//返回此缓冲区容量
public final int position() {return position;}//返回此缓冲区位置
public final Buffer position(int newPosition){...}//设置此缓冲区位置
public final int limit() {return limit;}//返回此缓冲区的限制
public final Buffer limit(int newLimit){...}//设置此缓冲区限制
public final Buffer mark(){...}//在此缓冲区的位置设置标记
public final Buffer reset(){...}//将此缓冲区的位置重置为以前标记的位置
public final Buffer clear(){...}//清楚此缓冲区,即将各个标记恢复到初始状态,但是数据并没有真是擦除
public final Buffer flip(){...}//将此缓冲区反转
public final Buffer rewind(){...}//重绕此缓冲区
public final int remaining(){...}//返回当前位置于限制之间的元素数
public final boolean hasRemaining(){...}//告知当前位置和限制之间是否有元素
public abstract boolean isReadOnly(){...}//告知此缓冲区是否为只读缓冲区
///JDK1.6引入
public abstract boolean hasArray(){...}//告知此缓冲区是否具有可访问的底层实现数组
public abstract Object array(){...}//返回此缓冲区的底层实现数组
public abstract int arrayOffset();//返回此缓冲区的底层实现数组中第一缓冲区元素的偏移量
public abstract boolean isDirect();//告知此缓冲区是否为直接缓冲区
final int nextGetIndex(){...}
final int nextGetIndex(int nb){...}
final int nextPutIndex() {...}
final int nextPutIndex(int nb){...}
final int checkIndex(int i) {...}
final int checkIndex(int i, int nb){...}
final int markValue(){...}
final void truncate(){...}
final void discardMark(){...}
static void checkBounds(int off, int len, int size){...}
4、ByteBuffer 使用最多 常用方法
public static ByteBuffer allocateDirect(int capacity){...}//创建直接缓冲区
public static ByteBuffer allocate(int capacity){...}//设置缓冲区初始容量
public static ByteBuffer wrap(byte[] array,int offset, int length){...}//把一个数组放到缓冲区中使用构造初始化位置offset和上界length的缓冲区
public static ByteBuffer wrap(byte[] array){...}//缓存区存取相关API
public abstract byte get();//从当前位置position上get,get之后,position会自动+1
public abstract byte get(int index);//从绝对位置get
public abstract ByteBuffer put(byte b);//从当前位置上put,put后,position会自动+1
public abstract ByteBuffer put(int index, byte b);//从绝对位置上put
....
3、通道(Channel)
1、基本介绍和常用实现类
| NIO | BIO | |
|---|---|---|
| 读写 | 同时进行 | 只能读或者只能写 |
| 异步读写 | ||
| 缓冲区 | 可以从缓冲区读写数据 |
实现类
2、FileChannel 类
主要方法
public final long read(ByteBuffer[] dsts)///从通道中读取数据并放到缓冲区
public final long write(ByteBuffer[] srcs)//把缓冲区的数据写到通道
public abstract long transferFrom(ReadableByteChannel src,long position, long count)//从目标通道中复制数据到当前通道
public abstract long transferTo(long position, long count,WritableByteChannel target)//把数据从当前通道复制给目标通道
4、关于Buffer和Channel的注意事项和细节
1、ByteBuffer支持类型的put和get,put放入的是什么类型,get就应该相应的类型去获取。否则报BufferUnderflowException异常。
2、可以将普通buffer转换成只读buffer。ByteBuffer readonlyBuffer = byteBuffer.asReadOnlyBuffer();readonlyBuffer成为只读buffer。 如果执行readonlyBuffer.get();报异常ReadOnlyBufferException。
3、NIO还提供了MappedByteVuffer,可以让文件直接在内存(堆外的内存)中进行修改。
5、Buffer的分散、聚集
Scattering:将数据写入到buffer时,可以采用buffer数组,依次写入
Gathering: 从buffer读取数据时,可以采用buffer数组,依次读取
4、selector
1、基本介绍
Selector能够检测多个注册通道上是否有事件发生。
2、特点
1、Netty的IO线程NioEventLoop聚合了Slector,可以同时并发处理成百上千个客户端连接。
2、若线程从客户端Socket读写数据时,若没有数据可用时,该线程可以进行其他的任务。
3、线程通常将非阻塞的IO空闲时间用于其他通道上执行IO操作,所以单线程可以管理多个输入和输出通道。
4、由于读写都是非阻塞的,这个可以充分提升IO线程的运行效率,避免频繁IO阻塞导致的线程挂起。
3、相关方法
public static Selector open()//得到一个selector对象
public int select(Long timeout)//获取有io操作对应的SelectionKey加入到集合中并返回
public abstract Set<SelectionKey> selectedKeys();//从内部集合种获取所有SelectionKey
selector.select()//阻塞
selector.select(1000)//阻塞1000毫秒
selector.wakeup()//唤醒
selector.selectNow()//不阻塞,立刻返回
4、NIO非阻塞网络编程原理分析
1、当客户端连接时,会通过ServerSocketChannel得到SocketChannel。
2、Selector进行监听select方法,返回事件发生的通道个数。
3、将SocketChannel注册到Selector上,register(Selector sel, int ops)可以注册多个SocketChannel。
4、注册后返回一个SelectionKey,会和该Selector关联(集合)。
5、进一步得到各个SelectionKey(有事件发生)
6、子通过SelectionKey反向获取Sockethannel,方法channel()。
7、通过channel完成业务处理。
5、SelectionKey的相关方法
public abstract Selector selector();//得到与之关联的selector对象
public abstract SelectableChannel channel();//得到与之关联的通道对象
public final Object attachment();//得到与之关联的共享数据
public abstract int interestOps();//设置或者改变监听事件
public final boolean isAcceptable()
public final boolean isWritable()
public final boolean isReadable()
6、ServerSocketChannel
在服务器监听客户端socket连接
public static ServerSocketChannel open()//得到一个通道
public final ServerSocketChannel bind(SocketAddress local)//设置服务器端口
public abstract SocketChannel accept() //接受一个连接,返回通道对应的对象
public final SelectableChannel configureBlocking(boolean block)//设置是否阻塞,false非阻塞
public final SelectionKey register(Selector sel, int ops,Object att)//注册选择器并设置监听事件
7、SocketChannel
网络IO通道,具体负责读写操作。NIO把缓冲区数据写入通道,或者把通道数据读取到缓冲区。
public static SocketChannel open()
public final SelectableChannel configureBlocking(boolean block)
public abstract boolean connect(SocketAddress remote)
public abstract boolean finishConnect()
public abstract int read(ByteBuffer dst)
public abstract int write(ByteBuffer src)
四、NIO与0拷贝
mmap优化
小数据
sendFile优化
大数据