深入理解tomcat中的BIO、NIO、AIO、ARP
tomcat作為springboot中默認的web容器,了解tomcat的運轉可以幫助我們更好的去調整tomcat的參數達到更好的性能
1. 前置知識
- I/O就是Input/Output,收別人的數據到本機叫Input,本級發(fā)數據出去叫Output
- 網絡I/O請求會先到網卡然后到內核態(tài)再到用戶態(tài)
- CPU比內存快、內存比硬盤、網卡等外設快
- 所有I/O操作需要被加載到用戶態(tài)內存,用戶態(tài)程序才能直接操作
- 想要效果高,必須讓所有的資源都不閑置
- tomcat不處理請求,會接受請求,轉發(fā)到具體的容器中
- 一個socket連接代表一個客戶端,一個socket可以發(fā)送多份請求不斷開
2. scoket測試工具
啟動程序是jar包,必須要有jre環(huán)境
鏈接:https://sockettest.sourceforge.net

3. BIO 同步阻塞IO
每一個socket連接后,tomcat都會有一個線程去全程去陪伴,把請求轉發(fā)到具體的容器中后,這個線程還在阻塞,等待容器返回數據,只有socket連接斷開了,才會回收這個線程。tomcat7或以下默認,比較簡單、穩(wěn)定,適合連接數比較少的
模擬代碼如下:
public class BioServer {
static ExecutorService executorService = Executors.newCachedThreadPool();
public static void main(String[] args) {
try {
// 啟動服務,綁定8080端口
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(8080));
System.out.println("開啟服務");
while (true){
System.out.println("等待客戶端建立連接");
// 監(jiān)聽8080端口,獲取客戶端連接
Socket socket = serverSocket.accept(); //阻塞
System.out.println("建立連接:"+socket);
executorService.submit(()->{
//業(yè)務處理
try {
handler(socket);
} catch (IOException e) {
e.printStackTrace();
}
});
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//TODO 資源回收
}
}
private static void handler(Socket socket) throws IOException {
byte[] bytes = new byte[1024];
System.out.println("等待讀取數據");
int read = socket.getInputStream().read(bytes); // 阻塞
if(read !=-1) {
System.out.println("讀取客戶端發(fā)送的數據:" +
new String(bytes, 0, read));
}
}
}4. NIO 同步非阻塞
一個socket連接過來,會經歷以下步驟
- LimitLatch:連接控制器,負責維護連接數計算,連接數默認是 8192,達到這個閥值后,就會拒絕連接請求。如果要調整修改配置文件server.tomcat.max-connections屬性
- Acceptor:Acceptor 跑在一個單獨的線程里,它在一個死循環(huán)里調用 accept 方法來接收新連接,一旦有新的連接請求到來,accept 方法返回一個 Channel 對象,接著把 Channel 對象交給 Poller 去處理
- Poller:Poller 的本質是一個 Selector,也跑在單獨線程里。Poller 在內部維護一個 Channel 數組,它在一個死循環(huán)里不斷檢測 Channel 的數據就緒狀態(tài),一旦有 Channel 可讀,就生成一個 SocketProcessor 任務對象扔給Executor 去處理
- Executor: Executor 就是線程池,負責運行 SocketProcessor 任務類,SocketProcessor 的 run 方法會調用Http11Processor 來讀取和解析請求數據。Http11Processor 是應用層協議的封裝,它會調用容器獲得響應,再把響應通過 Channel 寫出

tomcat8及以上默認, springboot2.3.12.RELEASE內嵌tomcat是9.0.46版本默認也是這個
模擬代碼:
public class NioServer {
public static void main(String[] args) {
List<SocketChannel> list = new ArrayList<>(); // 緩存所有的socket
ByteBuffer byteBuffer = ByteBuffer.allocate(1024); // 緩存區(qū)的大小
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 監(jiān)聽8080
serverSocketChannel.bind(new InetSocketAddress(8080));
// channel非阻塞
serverSocketChannel.configureBlocking(false);
System.out.println("NioServer 啟動....");
while (true){
// 非阻塞
SocketChannel socketChannel = serverSocketChannel.accept();
Thread.sleep(1000);
if(socketChannel == null){
System.out.println("沒有新的客戶端建立連接");
}else {
System.out.println("新的客戶端建立連接");
// channel非阻塞
socketChannel.configureBlocking(false);
// 將新的socket添加到 list
list.add(socketChannel);
}
//遍歷所有的socket
for(SocketChannel channel:list){
//非阻塞
int read = channel.read(byteBuffer);
if(read >0) {
//讀模式
byteBuffer.flip();
System.out.println("讀取客戶端發(fā)送的數據:" +new String(byteBuffer.array(),0,read));
byteBuffer.clear();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}5. AIO異步非阻塞
NIO 和 AIO(NIO2) 最大的區(qū)別是,一個是同步一個是異步。異步最大的特點是,應用程序不需要自己去觸發(fā)數據從內核空間到用戶空間的拷貝。

沒有 Poller 組件,也就是沒有 Selector。在異步 I/O 模式下,Selector 的工作交給
內核來做了。
Linux 內核沒有很完善地支持異步 I/O 模型,因此 JVM 并沒有采用原生的 Linux 異步 I/O,而是在應用層面通過 epoll 模擬了異步 I/O 模型。因此在 Linux 平臺上,Java NIO 和 Java NIO2 底層都是通過 epoll 來實現的,但是 Java NIO 更加簡單高效。如果你的 Tomcat 跑在 Linux 平臺上,建議不使用NIO2
模擬代碼:
public class AioServer {
public AsynchronousServerSocketChannel serverSocketChannel;
public static void main(String[] args) throws Exception {
new AioServer().listen();
Thread.sleep(Integer.MAX_VALUE);
}
private void listen() throws IOException {
//1. 創(chuàng)建一個線程池
ExecutorService es = Executors.newCachedThreadPool();
//2. 創(chuàng)建異步通道群組
AsynchronousChannelGroup acg = AsynchronousChannelGroup.withCachedThreadPool(es, 1);
//3. 創(chuàng)建服務端異步通道
serverSocketChannel = AsynchronousServerSocketChannel.open(acg);
//4. 綁定監(jiān)聽端口
serverSocketChannel.bind(new InetSocketAddress(8080));
System.out.println("AioServer 啟動....");
//5. 監(jiān)聽連接,傳入回調類處理連接請求
serverSocketChannel.accept(this, new CompletionHandler<AsynchronousSocketChannel, AioServer>() {
//
// //具體處理連接請求的就是completed方法,它有兩個參數:第一個是異步通道,第二個就是上面?zhèn)魅氲腁ioServer對象
@Override
public void completed(AsynchronousSocketChannel socketChannel, AioServer attachment) {
try {
if (socketChannel.isOpen()) {
System.out.println("接收到新的客戶端的連接,地址:"
+ socketChannel.getRemoteAddress());
final ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//調用 read 函數讀取客戶端發(fā)送的數據
socketChannel.read(byteBuffer, socketChannel,
new CompletionHandler<Integer, AsynchronousSocketChannel>() {
@Override
public void completed(Integer result, AsynchronousSocketChannel attachment) {
try {
//讀取請求,處理客戶端發(fā)送的數據
byteBuffer.flip();
String content = Charset.defaultCharset()
.newDecoder().decode(byteBuffer).toString();
System.out.println("服務端接受到客戶端發(fā)來的數據:" + content);
} catch (CharacterCodingException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, AsynchronousSocketChannel attachment) {
exc.printStackTrace();
try {
attachment.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//當有新的客戶端接入的時候,直接調用accept的方法
attachment.serverSocketChannel.accept(attachment, this);
}
}
@Override
public void failed(Throwable exc, AioServer attachment) {
exc.printStackTrace();
}
});
}
}6. APR異步非阻塞
APR方式全名叫Apache Portable Runtime,需要額外去下載安裝配置,NIO2是調用java庫去實現異步的,而ARP是直接通過JNI (Java Native Interface)去操作系統(tǒng)是實現異步,APR 能夠使用高級 IO 功能 (如sendfile, epoll, OpenSSL),sendfile主要是對靜態(tài)文件提升很大,換APR也主要是這個原因其他的提升也不是特別大
附上對比圖

springboot配置apr教程:http://www.dhdzp.com/program/339686ch2.htm
到此這篇關于理解tomcat中的BIO、NIO、AIO、ARP的文章就介紹到這了,更多相關tomcat BIO、NIO、AIO、ARP內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Web項目打成war包部署Tomcat時運行startup.bat直接閃退部署失敗的快速解決方案
這篇文章主要介紹了Web項目打成war包部署Tomcat時運行startup.bat直接閃退部署失敗解決方案,需要的朋友可以參考下2018-01-01
解決spring boot + jar打包部署tomcat 404錯誤問題
這篇文章主要介紹了spring boot + jar打包部署tomcat 404錯誤問題解決方案,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12

