avatar

目录
Netty基础

Netty :

一个用来做sokect网络编程的高性能框架;

一个替代JDK NIO的优秀框架;

是大多数高并发/分布式框架的底层基础;

目录:

[TOC]

参考:基础介绍 https://www.jianshu.com/p/a4e03835921a

传统IO编程如何做c/s通信

IOServer

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package mxx.netty.io;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
* 目标:客户端每隔两秒发送一个带有时间戳的"hello world"给服务端,服务端收到之后打印。
*/

/**
* 1、创建了一个serverSocket来监听8000端口
* 2、创建一个线程,线程里面不断调用阻塞方法 serversocket.accept();获取新的连接
* 3、当获取到新的连接之后,给每条连接创建一个新的线程,这个线程负责从该连接中读取数据
* 4、读取数据是以字节流的方式
*/

/**
* 同时开2个client效果:
*
* Thread-1-Mon Jul 22 14:15:29 CST 2019: hello world
* Thread-2-Mon Jul 22 14:15:29 CST 2019: hello world
* Thread-1-Mon Jul 22 14:15:31 CST 2019: hello world
* Thread-2-Mon Jul 22 14:15:31 CST 2019: hello world
* Thread-1-Mon Jul 22 14:15:33 CST 2019: hello world
* Thread-2-Mon Jul 22 14:15:33 CST 2019: hello world
* Thread-1-Mon Jul 22 14:15:35 CST 2019: hello world
* ...
*
*/
public class IOServer {
public static void main(String[] args) throws Exception {

ServerSocket serverSocket = new ServerSocket(8000);

// (1) 接收新连接线程
new Thread(() -> {
while (true) {
try {
// (1) 阻塞方法获取新的连接
Socket socket = serverSocket.accept();

// (2) 每一个新的连接都创建一个线程,负责读取数据
new Thread(() -> {
try {
String tName = Thread.currentThread().getName();

byte[] data = new byte[1024];
InputStream inputStream = socket.getInputStream();
while (true) {
int len;
// (3) 按字节流方式读取数据
// inputStream.read 是一个阻塞方法吗?
while ((len = inputStream.read(data)) != -1) {
System.out.println(tName + '-' +new String(data, 0, len));
}
}
} catch (IOException e) {
}
}).start();

} catch (IOException e) {
}

}
}).start();
}
}

IOClient

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package mxx.netty.io;

import java.io.IOException;
import java.net.Socket;
import java.util.Date;

/**
* 连接上服务端8000端口之后,每隔2秒,向服务端写一个带有时间戳的 "hello world"
*/
public class IOClient {

public static void main(String[] args) {
new Thread(() -> {
try {
Socket socket = new Socket("127.0.0.1", 8000);
while (true) {
try {
socket.getOutputStream().write((new Date() + ": hello world").getBytes());
socket.getOutputStream().flush();
Thread.sleep(2000);
} catch (Exception e) {
}
}
} catch (IOException e) {
}
}).start();
}
}

NIO编程 vs IO编程

IO编程问题:

1、1w个client连接对应server要开1w个线程去维护,继而1w个while死循环

2、线程资源受限:大量线程阻塞,操作系统耗不起

3、线程切换效率低下:要在1w个线程里来回切

4、数据读写是以字节流为单位,效率不高

如何解决以上问题的?

1、线程资源受限

新连接不再创建线程,而是批量绑定。

1357217-1c856423372e7d5a

一个循环如何监测1w个连接?

selector:新连接注册到selector上,批量监测出有数据可读的连接(同一时刻只有少量的连接有数据可读)

实际开发中,会开多个线程,每个线程都管理着一批连接,这样线程消耗大幅减少。

2、线程切换效率低下

NIO模型中线程数量大大降低,线程切换效率因此也大幅度提高

3、IO读写以字节为单位

NIO以字节块为单位读取,NIO维护一个缓冲区。

使用JDK原生NIO实现server

强烈不建议直接基于JDK原生NIO来进行网络开发

(看看注释就行了)

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package mxx.netty.m02_jdk_nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

/**
* 核心思路:
*
* 1、NIO模型中通常会有两个线程,每个线程绑定一个轮询器selector,在我们这个例子中serverSelector负责轮询是否有新的连接,clientSelector负责轮询连接是否有数据可读
* 2、服务端监测到新的连接之后,不再创建一个新的线程,而是直接将新连接绑定到clientSelector上,这样就不用IO模型中1w个while循环在死等,参见(1)
* 3、clientSelector被一个while死循环包裹着,如果在某一时刻有多条连接有数据可读,那么通过 clientSelector.select(1)方法可以轮询出来,进而批量处理,参见(2)
* 4、数据的读写以内存块为单位,参见(3)
*
*
* 强烈不建议直接基于JDK原生NIO来进行网络开发:
*
* 1、模型复杂,接口反人类
* 2、没有合适的线程模型,很多基本东西要自己实现
* 3、JDK的NIO底层是epoll, 有BUG, cpu飙升
* 4、自行实现的NIO很容易出现各类bug
*
* 所以,Netty来了
*
*/
public class NIOServer {
public static void main(String[] args) throws IOException {
Selector serverSelector = Selector.open();
Selector clientSelector = Selector.open();

new Thread(() -> {
try {
// 对应IO编程中服务端启动
ServerSocketChannel listenerChannel = ServerSocketChannel.open();
listenerChannel.socket().bind(new InetSocketAddress(8000));
listenerChannel.configureBlocking(false);
listenerChannel.register(serverSelector, SelectionKey.OP_ACCEPT);

while (true) {
// 监测是否有新的连接,这里的1指的是阻塞的时间为1ms
if (serverSelector.select(1) > 0) {
Set<SelectionKey> set = serverSelector.selectedKeys();
Iterator<SelectionKey> keyIterator = set.iterator();

while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();

if (key.isAcceptable()) {
try {
// (1) 每来一个新连接,不需要创建一个线程,而是直接注册到clientSelector
SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
clientChannel.configureBlocking(false);
clientChannel.register(clientSelector, SelectionKey.OP_READ);
} finally {
keyIterator.remove();
}
}

}
}
}
} catch (IOException ignored) {
}

}).start();


new Thread(() -> {
try {
while (true) {
// (2) 批量轮询是否有哪些连接有数据可读,这里的1指的是阻塞的时间为1ms
if (clientSelector.select(1) > 0) {
Set<SelectionKey> set = clientSelector.selectedKeys();
Iterator<SelectionKey> keyIterator = set.iterator();

// 有多条连接有数据可读
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();

if (key.isReadable()) {
try {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// (3) 读取数据以块为单位批量读取
clientChannel.read(byteBuffer);
byteBuffer.flip();
System.out.println(Charset.defaultCharset().newDecoder().decode(byteBuffer)
.toString());
} finally {
keyIterator.remove();
key.interestOps(SelectionKey.OP_READ);
}
}

}
}
}
} catch (IOException ignored) {
}
}).start();


}
}

Netty编程

概述:

1、Netty封装了JDK的NIO,用户友好

2、官话:”Netty是一个异步事件驱动的网络应用框架,用于快速开发可维护的高性能服务器和客户端。”

使用Netty不使用JDK原生NIO的原因:

1、JDK-NIO:使用复杂、BUG横飞

2、Netty底层IO模型随意切换(可以直接从NIO模型变身为IO模型)

3、简化开发,脱离细节,专注业务(Netty自带的拆包解包,异常检测等机制…)

4、Netty解决了JDK的很多BUG

5、Netty底层对线程,selector做了很多细小的优化

6、自带各种协议栈

7、Netty社区活跃

8、Netty已经历各大rpc框架,消息中间件,分布式通信中间件线上的广泛验证,健壮性无比强大


开始写代码…

maven

xml
1
2
3
4
5
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.6.Final</version>
</dependency>

END

下一步:一个Netty实战项目

文章作者: Machine
文章链接: https://machine4869.gitee.io/2019/07/22/20190722134143780/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 哑舍
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论