Java并發(fā)編程基礎(chǔ)線程應(yīng)用實(shí)戰(zhàn)-基于線程池的簡(jiǎn)單web服務(wù)器

      網(wǎng)友投稿 932 2022-05-29

      目前瀏覽器作為web服務(wù)的客戶端訪問(wèn)者,都支持并發(fā)多線程的訪問(wèn)。例如在瀏覽器訪問(wèn)一個(gè)web服務(wù)器上的HTML頁(yè)面,此時(shí)HTML頁(yè)面中的各種資源(圖片、樣式)會(huì)被瀏覽器并發(fā)的獲取,這種并發(fā)訪問(wèn)使得用戶不至于等待圖片加載的同時(shí)也不能看到文字內(nèi)容。

      客戶端既然是多線程并發(fā)訪問(wèn),那么如果服務(wù)端僅僅是單線程處理客戶端的請(qǐng)求,那么客戶端的并發(fā)訪問(wèn)將會(huì)變得毫無(wú)意義。因此,大部分的web服務(wù)器也是支持并發(fā)訪問(wèn)的。常見(jiàn)的Java web服務(wù)器有Tomcat\Netty等等。

      接下來(lái)我們通過(guò)結(jié)合線程池來(lái)寫一個(gè)簡(jiǎn)單的web服務(wù)器,支持訪問(wèn)html(文本、圖片)資源。

      線程池接口定義

      線程池具體實(shí)現(xiàn):

      其主要功能和實(shí)現(xiàn)如下

      服務(wù)端監(jiān)聽客戶端的socket連接

      接收到的socket連接封裝到HttpRequestHandler線程中,當(dāng)成任務(wù)提交給線程池去調(diào)度執(zhí)行

      HttpRequestHandler線程的run方法主要包含靜態(tài)資源jpg圖片的讀取和輸出(字節(jié)流),HTML文本讀取和輸出(字符流),關(guān)流等操作

      package com.lizba.p3.http; import com.lizba.p3.threadpool.DefaultThreadPool; import com.lizba.p3.threadpool.ThreadPool; import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** *

      * 簡(jiǎn)單HTTP服務(wù)器 *

      * * @Author: Liziba * @Date: 2021/6/18 11:47 */ public class SimplerHttpServer { /** 設(shè)置線程池的默認(rèn)大小 */ private static ThreadPool pool = new DefaultThreadPool<>(1); /** SimplerHttpServer根路徑 */ private static String basePath; /** 端口 */ private static int port = 8888; /** serverSocket */ private static ServerSocket serverSocket; public SimplerHttpServer(int port) { if (port < 0) { return; } SimplerHttpServer.port = port; } /** * 設(shè)置資源根路徑 * * @param basePath */ public static void setBasePath(String basePath) { if (basePath == null || "".equals(basePath)) { return; } if (new File(basePath).exists() && new File(basePath).isDirectory()) { SimplerHttpServer.basePath = basePath; } } /** * 啟動(dòng)web服務(wù) * * @throws IOException */ public static void start() throws IOException { serverSocket = new ServerSocket(port); Socket socket = null; while ((socket = serverSocket.accept()) != null) { pool.execute(new HttpRequestHandler(socket)); } serverSocket.close(); } /** * 將socket請(qǐng)求封裝成一個(gè)HttpRequestHandler線程任務(wù),將任務(wù)提交給線程池 * */ static class HttpRequestHandler implements Runnable { private Socket socket; public HttpRequestHandler(Socket socket) { this.socket = socket; } @Override public void run() { BufferedReader reader = null; BufferedReader br = null; PrintWriter out = null; InputStream in = null; String line; try { reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String header = reader.readLine(); // 計(jì)算絕對(duì)路徑 String absolutePath = basePath + header.split(" ")[1]; out = new PrintWriter(socket.getOutputStream()); // 圖片資源處理,此處只支持jpg if (absolutePath.endsWith("jpg")) { in = new FileInputStream(absolutePath); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int len = 0; while ((len = in.read()) != -1) { baos.write(len); } byte[] array = baos.toByteArray(); out.println("HTTP/1.1 200 OK"); out.println("Server: Liziba"); out.println("Content-Type: image/jpeg"); out.println("Content-Length: " + array.length); out.println(""); socket.getOutputStream().write(array, 0, array.length); } else { // 其他資源例如HTML文本等資源(此處僅支持HTML文本資源) br = new BufferedReader(new InputStreamReader(new FileInputStream(absolutePath))); out = new PrintWriter(socket.getOutputStream()); out.println("HTTP/1.1 200 OK"); out.println("Server: Liziba"); out.println("Content-Type: text/html; charset=UTF-8"); out.println(""); while ((line = br.readLine()) != null) { out.println(line); } } out.flush(); } catch (IOException e) { // 錯(cuò)誤提示 out.println("HTTP/1.1 500"); out.println(""); out.flush(); } finally { close(br, in, reader, out, socket); } } /** * 關(guān)閉流 * @param closeables */ private static void close(Closeable... closeables) { if (closeables != null) { for (Closeable c : closeables) { try { if (c != null) c.close(); } catch (IOException e) { e.printStackTrace(); } } } } } }

      在啟動(dòng)服務(wù)前我們需要在本地提前準(zhǔn)備資源,并將資源根目錄指給SimplerHttpServer服務(wù),我在D盤放了2張圖和一個(gè)html文件。

      HTML代碼

      web服務(wù)測(cè)試頁(yè)面

      圖片1

      圖片2

      瀏覽器訪問(wèn)效果(后來(lái)?yè)Q成了純文字,內(nèi)容比較多,ab請(qǐng)求對(duì)HTML中的圖片不加載,瀏覽器是可以加載的)

      文件大概250KB

      啟動(dòng)服務(wù)代碼

      package com.lizba.p3.http; import java.io.IOException; /** *

      * 啟動(dòng)服務(wù) *

      * * @Author: Liziba * @Date: 2021/6/18 21:40 */ public class TestHttpServer { public static void main(String[] args) throws IOException { SimplerHttpServer.setBasePath("D:\\test"); SimplerHttpServer.start(); } }

      測(cè)試工具

      Apache HTTP server benchmarking tool(ab),簡(jiǎn)單說(shuō)明一下這個(gè)測(cè)試工具。ab是一個(gè)Apache Http服務(wù)器基準(zhǔn)測(cè)試工具。它可以測(cè)試HTTP服務(wù)器每秒最多可以處理多少請(qǐng)求。如果測(cè)試的是web應(yīng)用服務(wù),這個(gè)結(jié)果可以裝換成整個(gè)應(yīng)用每秒可以滿足多少請(qǐng)求。它的缺點(diǎn)是用途比較有限,只能針對(duì)單個(gè)URL進(jìn)行盡可能快的壓力測(cè)試。

      測(cè)試內(nèi)容

      使用ab分10個(gè)線程發(fā)起5000請(qǐng)求,每次測(cè)試結(jié)束后改變線程池的大小,初始大小為1,測(cè)試主要觀察的是SimplerHttpServer的響應(yīng)時(shí)間和每秒完成的查詢數(shù)量,筆者的機(jī)器(CPU(AMD Ryzen 5 3600 6-Core Processor),內(nèi)存16G)。

      請(qǐng)求指令(具體參數(shù)說(shuō)明請(qǐng)看我的ab工具使用章節(jié))

      ab -c 10 -n 10000 http://localhost:8888/test.html

      這個(gè)表示同時(shí)處理10個(gè)線程的并發(fā)請(qǐng)求,一共請(qǐng)求10000次

      測(cè)試結(jié)果

      通過(guò)修改線程池的大小,執(zhí)行相同的測(cè)試語(yǔ)句來(lái)測(cè)試,web服務(wù)器的響應(yīng)情況

      線程池線程數(shù)量

      1

      5

      10

      20

      響應(yīng)時(shí)間(ms)

      0.990

      0.297

      0.272

      0.290

      每秒查詢數(shù)量

      1010.08

      3367.34

      3677.18

      3442.95

      測(cè)試完成時(shí)間(s)

      9.900

      2.970

      2.719

      2.904

      總結(jié):

      在上述測(cè)試結(jié)果中,可以發(fā)現(xiàn)隨著線程池的線程數(shù)目的增加,SimpleHttpServer的吞吐量不斷增加,響應(yīng)時(shí)間不斷減小,因此線程池的實(shí)際作用是十分明顯的,但是我們看到線程池中的線程由10改變?yōu)?0的時(shí)候,SimpleHttpServer的響應(yīng)時(shí)間沒(méi)有減少反而有些變大了,因此線程池中的線程數(shù)目也不是越多也好的,線程池中的線程過(guò)多,反而會(huì)給系統(tǒng)增加無(wú)故開銷,適得其反。在實(shí)際開發(fā)中,我們要根據(jù)業(yè)務(wù)具體需求,硬件資源等情況來(lái)設(shè)置線程池的大小,必要的時(shí)候也可以實(shí)現(xiàn)線程池的動(dòng)態(tài)伸縮。

      Java web前端

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:【Unity3D日常開發(fā)】(三)實(shí)現(xiàn)角色移動(dòng)行走之跑酷游戲?qū)崿F(xiàn)
      下一篇:重學(xué)計(jì)算機(jī)組成原理(4)-還記得紙帶編程嗎?
      相關(guān)文章
      久久精品国产亚洲AV无码偷窥| 亚洲av日韩av高潮潮喷无码 | 久久久久久亚洲精品成人| 亚洲中文字幕无码一久久区| 亚洲乱码中文字幕手机在线 | 亚洲黄色免费在线观看| 亚洲国产成人久久综合碰碰动漫3d | 亚洲第一福利网站在线观看| 国产亚洲福利一区二区免费看| 亚洲大码熟女在线观看| 亚洲av乱码中文一区二区三区| 亚洲精品理论电影在线观看| 亚洲精品国产摄像头| 亚洲AV无码AV吞精久久| 国产精品亚洲综合一区在线观看 | 四虎亚洲国产成人久久精品| 亚洲Av无码乱码在线播放| 亚洲成A人片在线观看无码3D| 亚洲成av人在片观看| 亚洲日韩在线观看免费视频| 中文字幕亚洲激情| 国产亚洲精品a在线无码| 亚洲AV无码专区电影在线观看| 亚洲尹人九九大色香蕉网站| 亚洲日本乱码一区二区在线二产线 | 中文字幕精品无码亚洲字| 国产亚洲一区二区在线观看| 亚洲av日韩av不卡在线观看| 亚洲色图视频在线观看| 亚洲人成在线播放| 亚洲av成人中文无码专区| 亚洲第一黄色网址| 亚洲精品午夜无码专区| 亚洲综合精品一二三区在线| 亚洲欧洲精品视频在线观看| 77777午夜亚洲| 国产精品亚洲二区在线| 亚洲一区爱区精品无码| 亚洲视频.com| 最新亚洲卡一卡二卡三新区| 夜色阁亚洲一区二区三区|