深入理解tomcat中的BIO、NIO、AIO、ARP
tomcat作為springboot中默認(rèn)的web容器,了解tomcat的運(yùn)轉(zhuǎn)可以幫助我們更好的去調(diào)整tomcat的參數(shù)達(dá)到更好的性能
1. 前置知識(shí)
- I/O就是Input/Output,收別人的數(shù)據(jù)到本機(jī)叫Input,本級(jí)發(fā)數(shù)據(jù)出去叫Output
- 網(wǎng)絡(luò)I/O請(qǐng)求會(huì)先到網(wǎng)卡然后到內(nèi)核態(tài)再到用戶(hù)態(tài)
- CPU比內(nèi)存快、內(nèi)存比硬盤(pán)、網(wǎng)卡等外設(shè)快
- 所有I/O操作需要被加載到用戶(hù)態(tài)內(nèi)存,用戶(hù)態(tài)程序才能直接操作
- 想要效果高,必須讓所有的資源都不閑置
- tomcat不處理請(qǐng)求,會(huì)接受請(qǐng)求,轉(zhuǎn)發(fā)到具體的容器中
- 一個(gè)socket連接代表一個(gè)客戶(hù)端,一個(gè)socket可以發(fā)送多份請(qǐng)求不斷開(kāi)
2. scoket測(cè)試工具
啟動(dòng)程序是jar包,必須要有jre環(huán)境
鏈接:https://sockettest.sourceforge.net
3. BIO 同步阻塞IO
每一個(gè)socket連接后,tomcat都會(huì)有一個(gè)線程去全程去陪伴,把請(qǐng)求轉(zhuǎn)發(fā)到具體的容器中后,這個(gè)線程還在阻塞,等待容器返回?cái)?shù)據(jù),只有socket連接斷開(kāi)了,才會(huì)回收這個(gè)線程。tomcat7或以下默認(rèn),比較簡(jiǎn)單、穩(wěn)定,適合連接數(shù)比較少的
模擬代碼如下:
public class BioServer { static ExecutorService executorService = Executors.newCachedThreadPool(); public static void main(String[] args) { try { // 啟動(dòng)服務(wù),綁定8080端口 ServerSocket serverSocket = new ServerSocket(); serverSocket.bind(new InetSocketAddress(8080)); System.out.println("開(kāi)啟服務(wù)"); while (true){ System.out.println("等待客戶(hù)端建立連接"); // 監(jiān)聽(tīng)8080端口,獲取客戶(hù)端連接 Socket socket = serverSocket.accept(); //阻塞 System.out.println("建立連接:"+socket); executorService.submit(()->{ //業(yè)務(wù)處理 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("等待讀取數(shù)據(jù)"); int read = socket.getInputStream().read(bytes); // 阻塞 if(read !=-1) { System.out.println("讀取客戶(hù)端發(fā)送的數(shù)據(jù):" + new String(bytes, 0, read)); } } }
4. NIO 同步非阻塞
一個(gè)socket連接過(guò)來(lái),會(huì)經(jīng)歷以下步驟
- LimitLatch:連接控制器,負(fù)責(zé)維護(hù)連接數(shù)計(jì)算,連接數(shù)默認(rèn)是 8192,達(dá)到這個(gè)閥值后,就會(huì)拒絕連接請(qǐng)求。如果要調(diào)整修改配置文件server.tomcat.max-connections屬性
- Acceptor:Acceptor 跑在一個(gè)單獨(dú)的線程里,它在一個(gè)死循環(huán)里調(diào)用 accept 方法來(lái)接收新連接,一旦有新的連接請(qǐng)求到來(lái),accept 方法返回一個(gè) Channel 對(duì)象,接著把 Channel 對(duì)象交給 Poller 去處理
- Poller:Poller 的本質(zhì)是一個(gè) Selector,也跑在單獨(dú)線程里。Poller 在內(nèi)部維護(hù)一個(gè) Channel 數(shù)組,它在一個(gè)死循環(huán)里不斷檢測(cè) Channel 的數(shù)據(jù)就緒狀態(tài),一旦有 Channel 可讀,就生成一個(gè) SocketProcessor 任務(wù)對(duì)象扔給Executor 去處理
- Executor: Executor 就是線程池,負(fù)責(zé)運(yùn)行 SocketProcessor 任務(wù)類(lèi),SocketProcessor 的 run 方法會(huì)調(diào)用Http11Processor 來(lái)讀取和解析請(qǐng)求數(shù)據(jù)。Http11Processor 是應(yīng)用層協(xié)議的封裝,它會(huì)調(diào)用容器獲得響應(yīng),再把響應(yīng)通過(guò) Channel 寫(xiě)出
tomcat8及以上默認(rèn), springboot2.3.12.RELEASE內(nèi)嵌tomcat是9.0.46版本默認(rèn)也是這個(gè)
模擬代碼:
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)聽(tīng)8080 serverSocketChannel.bind(new InetSocketAddress(8080)); // channel非阻塞 serverSocketChannel.configureBlocking(false); System.out.println("NioServer 啟動(dòng)...."); while (true){ // 非阻塞 SocketChannel socketChannel = serverSocketChannel.accept(); Thread.sleep(1000); if(socketChannel == null){ System.out.println("沒(méi)有新的客戶(hù)端建立連接"); }else { System.out.println("新的客戶(hù)端建立連接"); // 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("讀取客戶(hù)端發(fā)送的數(shù)據(jù):" +new String(byteBuffer.array(),0,read)); byteBuffer.clear(); } } } } catch (Exception e) { e.printStackTrace(); } } }
5. AIO異步非阻塞
NIO 和 AIO(NIO2) 最大的區(qū)別是,一個(gè)是同步一個(gè)是異步。異步最大的特點(diǎn)是,應(yīng)用程序不需要自己去觸發(fā)數(shù)據(jù)從內(nèi)核空間到用戶(hù)空間的拷貝。
沒(méi)有 Poller 組件,也就是沒(méi)有 Selector。在異步 I/O 模式下,Selector 的工作交給
內(nèi)核來(lái)做了。
Linux 內(nèi)核沒(méi)有很完善地支持異步 I/O 模型,因此 JVM 并沒(méi)有采用原生的 Linux 異步 I/O,而是在應(yīng)用層面通過(guò) epoll 模擬了異步 I/O 模型。因此在 Linux 平臺(tái)上,Java NIO 和 Java NIO2 底層都是通過(guò) epoll 來(lái)實(shí)現(xiàn)的,但是 Java NIO 更加簡(jiǎn)單高效。如果你的 Tomcat 跑在 Linux 平臺(tái)上,建議不使用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)建一個(gè)線程池 ExecutorService es = Executors.newCachedThreadPool(); //2. 創(chuàng)建異步通道群組 AsynchronousChannelGroup acg = AsynchronousChannelGroup.withCachedThreadPool(es, 1); //3. 創(chuàng)建服務(wù)端異步通道 serverSocketChannel = AsynchronousServerSocketChannel.open(acg); //4. 綁定監(jiān)聽(tīng)端口 serverSocketChannel.bind(new InetSocketAddress(8080)); System.out.println("AioServer 啟動(dòng)...."); //5. 監(jiān)聽(tīng)連接,傳入回調(diào)類(lèi)處理連接請(qǐng)求 serverSocketChannel.accept(this, new CompletionHandler<AsynchronousSocketChannel, AioServer>() { // // //具體處理連接請(qǐng)求的就是completed方法,它有兩個(gè)參數(shù):第一個(gè)是異步通道,第二個(gè)就是上面?zhèn)魅氲腁ioServer對(duì)象 @Override public void completed(AsynchronousSocketChannel socketChannel, AioServer attachment) { try { if (socketChannel.isOpen()) { System.out.println("接收到新的客戶(hù)端的連接,地址:" + socketChannel.getRemoteAddress()); final ByteBuffer byteBuffer = ByteBuffer.allocate(1024); //調(diào)用 read 函數(shù)讀取客戶(hù)端發(fā)送的數(shù)據(jù) socketChannel.read(byteBuffer, socketChannel, new CompletionHandler<Integer, AsynchronousSocketChannel>() { @Override public void completed(Integer result, AsynchronousSocketChannel attachment) { try { //讀取請(qǐng)求,處理客戶(hù)端發(fā)送的數(shù)據(jù) byteBuffer.flip(); String content = Charset.defaultCharset() .newDecoder().decode(byteBuffer).toString(); System.out.println("服務(wù)端接受到客戶(hù)端發(fā)來(lái)的數(shù)據(jù):" + 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 { //當(dāng)有新的客戶(hù)端接入的時(shí)候,直接調(diào)用accept的方法 attachment.serverSocketChannel.accept(attachment, this); } } @Override public void failed(Throwable exc, AioServer attachment) { exc.printStackTrace(); } }); } }
6. APR異步非阻塞
APR方式全名叫Apache Portable Runtime,需要額外去下載安裝配置,NIO2是調(diào)用java庫(kù)去實(shí)現(xiàn)異步的,而ARP是直接通過(guò)JNI (Java Native Interface)去操作系統(tǒng)是實(shí)現(xiàn)異步,APR 能夠使用高級(jí) IO 功能 (如sendfile, epoll, OpenSSL),sendfile主要是對(duì)靜態(tài)文件提升很大,換APR也主要是這個(gè)原因其他的提升也不是特別大
附上對(duì)比圖
springboot配置apr教程:http://www.dbjr.com.cn/program/339686ch2.htm
到此這篇關(guān)于理解tomcat中的BIO、NIO、AIO、ARP的文章就介紹到這了,更多相關(guān)tomcat BIO、NIO、AIO、ARP內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Web項(xiàng)目打成war包部署Tomcat時(shí)運(yùn)行startup.bat直接閃退部署失敗的快速解決方案
這篇文章主要介紹了Web項(xiàng)目打成war包部署Tomcat時(shí)運(yùn)行startup.bat直接閃退部署失敗解決方案,需要的朋友可以參考下2018-01-01詳解Windows下調(diào)整Tomcat啟動(dòng)參數(shù)的實(shí)現(xiàn)方法
這篇文章主要介紹了詳解Windows下調(diào)整Tomcat啟動(dòng)參數(shù)的實(shí)現(xiàn)方法的相關(guān)資料,希望通過(guò)本文大家能夠修改Tomcat啟動(dòng)參數(shù)來(lái)實(shí)現(xiàn)自己想要的效果,需要的朋友可以參考下2017-09-09Tomcat中連接器(Connector)的實(shí)現(xiàn)
Tomcat中的連接器負(fù)責(zé)處理客戶(hù)端通信,支持HTTP、HTTPS和AJP協(xié)議,通過(guò)配置連接器,可以滿(mǎn)足不同的部署需求,包括端口、協(xié)議和SSL參數(shù),正確配置連接器是確保Tomcat服務(wù)器高效運(yùn)行和安全的關(guān)鍵2024-11-11阿里云服務(wù)器安裝配置tomcat 添加外網(wǎng)訪問(wèn)端口的教程
這篇文章主要介紹了阿里云服務(wù)器安裝配置tomcat 添加外網(wǎng)訪問(wèn)端口,需要的朋友可以參考下2019-11-11Idea部署tomcat服務(wù)實(shí)現(xiàn)過(guò)程圖解
這篇文章主要介紹了Idea部署tomcat服務(wù)實(shí)現(xiàn)過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08解決spring boot + jar打包部署tomcat 404錯(cuò)誤問(wèn)題
這篇文章主要介紹了spring boot + jar打包部署tomcat 404錯(cuò)誤問(wèn)題解決方案,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Tomcat簡(jiǎn)單網(wǎng)站部署的三種方式小結(jié)
本文主要介紹了Tomcat簡(jiǎn)單網(wǎng)站部署的三種方式小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05