目录
分布式之Netty介绍与实战(一)--Java IO与NIO
分布式之Netty介绍与实战(二)--Netty编程实践
分布式之Netty介绍与实战(三)--Netty线程模型解析
分布式之Netty介绍与实战(四)--Netty编解码编程实战
等待数据准备好(Waiting for the data to be ready)
从内核向进程复制数据(Copying the data from the kernel to the process)
UNIX网络编程的经典之作
一次观察多个I/O进程,等待哪个进程好了就调用哪个进程
同步I/O操作:导致请求进程阻塞,直到I/O操作完成。
public class SingleThreadEchoServer {
private int port;
public SingleThreadEchoServer(int port) {
this.port = port;
}
public void startServer() {
ServerSocket echoServer = null;
int i = 0;
System.out.println("服务器在端口[" + this.port + "]等待客户请求......");
try {
echoServer = new ServerSocket(this.port);
while (true) {
Socket clientRequest = echoServer.accept();
handleRequest(clientRequest, i++);
}
} catch (IOException e) {
System.out.println(e);
}
}
private void handleRequest(Socket clientSocket, int clientNo) {
PrintStream os = null;
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
os = new PrintStream(clientSocket.getOutputStream());
String inputLine;
while ((inputLine = in.readLine()) != null) {
// 输入'Quit'退出
if (inputLine.equals("Quit")) {
System.out.println("关闭与客户端[" + clientNo + "]......" + clientNo);
os.close();
in.close();
clientSocket.close();
break;
} else {
System.out.println("来自客户端[" + clientNo + "]的输入: [" + inputLine + "]!");
os.println("来自服务器端的响应:" + inputLine);
}
}
} catch (IOException e) {
System.out.println("Stream closed");
}
}
public static void main(String[] args) throws IOException {
new SingleThreadEchoServer(8030).startServer();
}
}
public class MultiThreadedEchoServerV1 {
private int port;
public MultiThreadedEchoServerV1(int port) {
this.port = port;
}
public void startServer() {
ServerSocket echoServer = null;
int i = 0;
System.out.println("服务器在端口[" + this.port + "]等待客户请求......");
try {
echoServer = new ServerSocket(port);
while (true) {
Socket clientRequest = echoServer.accept();
new Thread(new ThreadedServerHandler(clientRequest, i++)).start();
}
} catch (IOException e) {
System.out.println(e);
}
}
public static void main(String[] args) throws IOException {
new MultiThreadedEchoServerV1(8030).startServer();
}
}
public class ThreadedServerHandler implements Runnable {
Socket clientSocket = null;
int clientNo = 0;
ThreadedServerHandler(Socket socket, int i) {
if (socket != null) {
clientSocket = socket;
clientNo = i;
System.out.println("创建线程为[" + clientNo + "]号客户服务...");
}
}
@Override
public void run() {
PrintStream os = null;
BufferedReader in = null;
try {
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
os = new PrintStream(clientSocket.getOutputStream());
String inputLine;
while ((inputLine = in.readLine()) != null) {
// 输入'Quit'退出
if (inputLine.equals("Quit")) {
System.out.println("关闭与客户端[" + clientNo + "]......" + clientNo);
os.close();
in.close();
clientSocket.close();
break;
} else {
System.out.println("来自客户端[" + clientNo + "]的输入: [" + inputLine + "]!");
os.println("来自服务器端的响应:" + inputLine);
}
}
} catch (IOException e) {
System.out.println("Stream closed");
}
}
}
public class MultiThreadedEchoServerV2 {
private int port;
public MultiThreadedEchoServerV2(int port) {
this.port = port;
}
public void startServer() {
ServerSocket echoServer = null;
Executor executor = Executors.newFixedThreadPool(5);
int i = 0;
System.out.println("服务器在端口[" + this.port + "]等待客户请求......");
try {
echoServer = new ServerSocket(8080);
while (true) {
Socket clientRequest = echoServer.accept();
executor.execute(new ThreadedServerHandler(clientRequest, i++));
}
} catch (IOException e) {
System.out.println(e);
}
}
public static void main(String[] args) throws IOException {
new MultiThreadedEchoServerV2(8080).startServer();
}
}
NIO 2: JSR203
Java NIO 主要由下面3部分核心组件组成:
Java NIO Buffer
Java NIO Buffer三大核心概念:position、limit、capacity
从写操作模式到读操作模式切换的时候(flip),position 都会归零,这样就可以从头开始读写了。
写操作模式下,limit 代表的是最大能写入的数据,这个时候 limit 等于 capacity。
写结束后,切换到读模式,此时的 limit 等于 Buffer 中实际的数据大小,因为 Buffer 不一定被写满了。
java.nio.buffer,缓冲区抽象
Direct ByteBuffer VS. non-direct ByteBuffer
HeapByteBuffer,标准的java类
维护一份byte[]在JVM堆上
底层存储在非JVM堆上,通过native代码操作
-XX:MaxDirectMemorySize=
|
DirectByteBuffer |
HeapByteBuffer |
创建开销 |
大 |
小
|
存储位置 |
Native heap |
Java heap
|
数据拷贝 |
无需临时缓冲区做拷贝 |
拷贝到临时DirectByteBuffer,但临时缓冲区使用缓存。 聚集写/发散读时没有缓存临时缓冲区。 |
GC影响 |
每次创建或者释放的时候 都调用一次System.gc() |
|
所有的 NIO 操作始于通道,通道是数据来源或数据写入的目的地,主要地, java.nio 包中主要实现的以下几个 Channel:
Selector是Java NIO中的一个组件,用于检查一个或多个NIO Channel的状态是否处于可读、可写
如此可以实现单线程管理多个channels,也就是可以管理多个网络链接
创建Selector(Creating a Selector)
Selector selector = Selector.open();
注册Channel到Selector上(Registering Channels with the Selector)
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
register的第二个参数,这个参数是一个“关注集合”,代表关注的channel状态,
有四种基础类型可供监听, 用SelectionKey中的常量表示如下:
SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE
从Selector中选择channel(Selecting Channels via a Selector)
select()方法的返回值是一个int,代表有多少channel处于就绪了。也就是自上一次select后有多少channel进入就绪。
selectedKeys()
在调用select并返回了有channel就绪之后,可以通过选中的key集合来获取channel,
这个操作通过调用selectedKeys()方法:
Set selectedKeys = selector.selectedKeys();
Iterator keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if(key.isAcceptable()) {
// a connection was accepted by a ServerSocketChannel.
} else if (key.isConnectable()) {
// a connection was established with a remote server.
} else if (key.isReadable()) {
// a channel is ready for reading
} else if (key.isWritable()) {
// a channel is ready for writing
}
keyIterator.remove();
}
事件驱动模型
非阻塞IO,IO读写不再阻塞,而是返回0
基于block的传输,通常比基于流的传输更高效
更高级的IO函数,zero-copy
IO多路复用大大提高了java网络应用的可伸缩性和实用性