孙哥分布式系列课程 --- Netty应用开发
1. 引言
1.1 什么是Netty
Netty是一个异步事件驱动的网络应用框架。 用于快速开发可维护的高性能协议服务器和客户端。
Netty是一个NIO客户服务器框架,它能够快速和容易地开发网络应用,如协议服务器和客户端。它大大简化和精简了网络编程,如TCP和UDP套接字服务器。
快速和简单 "并不意味着开发出来的应用程序会出现可维护性或性能问题。Netty的设计是经过精心设计的,其经验来自于许多协议的实施,如FTP、SMTP、HTTP以及各种基于二进制和文本的遗留协议。因此,Netty成功地找到了一种方法来实现开发的简易性、性能、稳定性和灵活性,而没有任何妥协。
1.2 为什么要学习Netty
markdown
1. Netty已经是行业内网络通信编程的标准,广泛应用于通信领域和很多其他中间件技术的底层。
2. 应用非常广泛
1. 游戏行业
2. 很多框架的通信底层,解决进程间通信。
Spring WebFlux、storm、rocketMQ、dubbo等,是分布式系统,通信的核心。
2. NIO编程
markdown
1. NIO全称成为None Blocking IO (非阻塞IO)。【JDK1.4】
2. 非阻塞 主要应用在网络通信中,能够合理利用资源,提高系统的并发效率。支持高并发的系统访问。
2.1 传统网络通信中的开发方式及问题
2.1.1 多线程版网络编程
多线程版本的网络编程:
- 每次处理请求都要创建一个线程
- 内存 占用高,不能无限制创建线程
- CPU使用率高
2.1.2 线程池版的网络编程
java
//核心线程数(corePoolSize=系统cpu核数,maxinumPollSize :最多创建线程的数量
// keepAliveTime: 最大等待时长,线程空闲时间超过这个就会销毁,
// unit : 单位 workQueue: 队列的最大容积
new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), 20, 120L, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(1000));
缺点:客户端请求过来了,建立了连接,但它阻塞了(比如在等客户端输入键盘,)这时候就阻塞了,这使得他这个线程Thread也得等着
2.2 NIO网络通信中的非阻塞编程
服务端还是引入线程池帮我们创建线程: 核心变化:不再使用io进行通信,而是通过管道的方式 Channel NIO帮我们引入了Seletor 服务器 ,可以帮我们监控客户端的Channel是否正常通信(建立连接,没有阻塞) 有着Seletor服务器帮会我们监控,如果一个客户端处于阻塞状态,他会帮我们把他连接的线程给释放出来
3.NIO的基本开发方式
3.1 Channel简介
markdown
1. IO通信的通道,类似于InputStream、OutputStream
2. Channel没有方向性
-
常见Channel
markdown1. 文件操作 FileChannel,读写文件中的数据。 2. 网络操作 SocketChannel,通过TCP读写网络中的数据。 ServerSockectChannel,监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。 DatagramChannel,通过UDP读写网络中的数据。
-
获得方式
markdown1. FileInputStreanm/FileOutputStream 2. RandomAccessFile 3. Socket 4. ServerSocket 5. DatagramSocket
3.2 Buffer简介
markdown
1. Channel读取或者写入的数据,都要写到Buffer中,才可以被程序操作。
2. 因为Channel没有方向性,所以Buffer为了区分读写,引入了读模式、写模式进行区分。
-
常见Buffer
markdown1. ByteBuffer 2. CharBuffer 3. DoubleBuffer 4. FloatBuffer 5. IntBuffer 6. LongBuffer 7. ShortBuffer 8. MappedByteBuffer..
-
获得方式
markdown1. ByteBuffer.allocate(10); 2. encode()
3.4 第一个NIO程序分析
java
public class TestNIO1 {
public static void main(String[] args) throws IOException {
//1 创建Channel通道 FileChannel
FileChannel channel = new FileInputStream("data.txt").getChannel();
//2 创建缓冲区
//1234567890
ByteBuffer buffer = ByteBuffer.allocate(10);
while (true) {
//3把通道内获取的文件数据,写入缓冲区
int read = channel.read(buffer);
if (read == -1) break;
//4.程序读取buffer内容,后续的操作。设置buffer为读模式。
buffer.flip();
//5.循环读取缓冲区中的数据
while (buffer.hasRemaining()) {
byte b = buffer.get();
System.out.println("(char)b = " + (char) b);
}
//6. 设置buffer的写模式
buffer.clear();
}
}
}
public class TestNIO2 {
public static void main(String[] args) {
//RadomAccessFile 异常处理
FileChannel channel = null;
try {
channel = new RandomAccessFile("data.txt", "rw").getChannel();
ByteBuffer buffer = ByteBuffer.allocate(10);
while (true) {
int read = channel.read(buffer);
if (read == -1) break;
buffer.flip();
while (buffer.hasRemaining()) {
byte b = buffer.get();
System.out.println("(char) b = " + (char) b);
}
buffer.clear();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
public class TestNIO3 {
public static void main(String[] args) {
try (FileChannel channel = FileChannel.open(Paths.get("data.txt"), StandardOpenOption.READ);) {
ByteBuffer buffer = ByteBuffer.allocate(10);
while (true) {
int read = channel.read(buffer);
if (read == -1) break;
buffer.flip();
while (buffer.hasRemaining()) {
byte b = buffer.get();
System.out.println("(char)b = " + (char) b);
}
buffer.clear();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.5 NIO开发的步骤总结
markdown
1. 获取Channel
2. 创建Buffer
3. 循环的从Channel中获取数据,读入到Buffer中。进行操作.
channel.read(buffer);
buffer.flip();//设置读模式
循环从buffer中获取数据。
buffer.get();
buffer.clear();//设置写模式