一、問題描述
今日遇到了一個問題,要去擷取HTTP封包在請求和響應的時間,因為沒有原生的API可以調用,是以需要一定的技巧~
- 下面主體的架構和代碼,我使用了form表單去構造一個POST請求,然後在Servlet中重寫doPost()方法,然後實作擷取請求時間和響應時間的代碼邏輯
html複制代碼<form action="print" method="post">
<input type="submit" value="列印資訊">
</form>
java複制代碼@WebServlet("/print")
public class printServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//...
}
}
二、抓包觀察
我們可以先去觀察一下在發起POST請求後這個HTTP請求封包中是否在這個請求時間,如果有的其實可以把它通過一定手段擷取到
- 但遺憾的是,我并沒有發現任何與時間相關的東西
- 那再到請求封包中去看看的話,就發現了與時間相關的内容,不過對照現在的時間仔細一看的話,卻查了不少,網上一搜就發現GMT是格林尼治時間,要比現在的時候早上8個小時,但看到一個時間卻非常激動,想着如何拿到它
三、查找文檔
因為【HttpServletRequest】和【HttpServletResponse】給我們提供了許多原生API,是以我們可以去找找看有哪些API是我們可以用得上的,這裡推薦直接進官網 ——> 連結
- 可以看到,無論是前者還是後者都未我們提供了很多的方法,這也我也會在代碼中使用幾個,不過找了一圈我發現完全沒有擷取時間的那種API,于是就沒有再查找下去了
四、思考嘗試
再找了很久還是一無所獲,幹脆就想着自己去手動擷取一下這個時間
- 此時我就想到了Java裡面的Date類,可以擷取目前系統的時間,以及随之對應的是格式化解析SimpleDateFormat(),于是便立馬寫下了這幾句代碼
java複制代碼SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEEE");//格式化日期
Date currentTime = new Date();//擷取目前時間
String date = formatter.format(currentTime).toString();
但是要怎麼使用這個目前時間呢?去擷取哪段時間呢?
- 首先我就提出了上面這個問題,思考了一下從Tomcat解析HTTP請求到将其交給Servlet做處理,那麼能夠最早擷取到時間的地方就是一開始進入doPost()的時候,我們記錄下目前的時間,就可以将其近似于HTTP的請求時間
請求時間有了,那響應時間呢?也用這個去求嗎?
- 這個的話不一定,因為剛才我們看了HTTP的響應封包,也就是服務端在處理完資料後給浏覽器傳回的一個響應,裡面是存在【Date】這個時間的,你可以選擇使用getHeaderNames()先擷取到整個封包的頭部,因為他的傳回值是一個Collection集合,是以你可以通過周遊集合的方式與getHeader()相配合格式化地輸出裡面的内容,這裡我就不細說了
- 也是一樣,我們可以通過上面這種方式去擷取響應時間,何時擷取才是最正确的呢?那就是在resp.getWriter().write()這個發還響應之前去擷取時間,這是最後的機會了!
五、精益求精
通過傳回我們在後端代碼中擷取到的時間,将其返還給浏覽器時便看到這個【請求時間】和【響應時間】是一樣的,這是為什麼呢?
- 仔細觀察我設定的日期格式化,是精确到秒的,如果你有一點計算機常識的話就可以知道計算機的運作速度是很快的,基本是以納秒為機關,此時我們若是隻精确到秒 的話可能還看不太出來,應該再多精确幾位
java複制代碼yyyy-MM-dd HH:mm:ss
- 可以看到,此時我精确到了納秒的級别,此時再去觀察的話就會有所不同
java複制代碼yyyy-MM-dd HH:mm:ss.SSSSSSSSS
- 可以看到雖然精确了很多位數,但是二者之間的差距還是很小,畢竟計算機的處理速度也是非常快的
但是這麼去做的隻是鑽了一個牛角尖,後面我又想到了一個更聰明的辦法
- 那就是讓程式等待一段時間!
java複制代碼try {
Thread.sleep(500); //讓程式暫停0.5s
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
- 學習過Java多線程的同學一定馬上就能反應過來了,可以使用Thread類中的sleep()方法讓程式睡上幾秒,然後再去擷取時間。此時我們再去觀察【請求時間】和【響應時間】的話就會有些差距了。如果你想讓差距更加明顯的話可以讓程式多睡一會
六、源碼解說
這裡的話附上我們解決本體所用的源碼
前端:
html複制代碼<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>表單</title>
</head>
<body>
<form action="print" method="post">
<input type="submit" value="列印資訊">
</form>
</body>
</html>
後端:
java複制代碼@WebServlet("/print")
public class printServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
// 在doPost()方法開始時擷取一下請求時間
SimpleDateFormat formatter=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSSSSS");//格式化日期
Date currentTime=new Date();//擷取目前時間
String date=formatter.format(currentTime).toString();
StringBuilder stringBuilder = new StringBuilder();
System.out.println("-------------------------------------------------");
stringBuilder.append("請求的URL = " + req.getRequestURL()); //請求的URL
stringBuilder.append("<br>");
stringBuilder.append("請求方法 = " + req.getMethod()); //請求方法
stringBuilder.append("<br>");
stringBuilder.append("請求時間 = " + date); //請求時間
stringBuilder.append("<br><br>");
try {
Thread.sleep(500); //讓程式暫停0.5s
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 在doPost()方法快結束時擷取一下響應時間
currentTime = new Date();//擷取目前時間
String date2 = formatter.format(currentTime).toString();
stringBuilder.append("響應時間 = " + date2);
stringBuilder.append("<br>");
stringBuilder.append("響應狀态碼 = " + resp.getStatus());
stringBuilder.append("<br>");
resp.getWriter().write(stringBuilder.toString());
}
}
稍微來講一下後端的這塊邏輯
- 這邊我主要使用到的就是這個【StringBuilder】類,使用它構造出來的對象,我們需要在單線程的環境下連接配接多個字元串,此時用它最合适了,通過裡面的append()方法去進行連接配接,這裡我測試了Reques和Response兩個類中的方法,列印出了HTTP封包的一些内容
- 還要注意,有些同學在看我這句代碼的時候很疑惑,所我怎麼把前端的代碼寫到後端來了
java複制代碼stringBuilder.append("<br>");
- 這裡主要就是依賴于前面的這句代碼,我将目前body中的資料格式設定為了HTML,代表可以識别HTML代碼,是以這個<br>就會被當做換行了,若是不寫這個的話所有的内容都會擠在一起的
java複制代碼 resp.setContentType("text/html;charset=utf-8");
- 最後的這句話就是将拼接的StringBuilder類型字元串轉換為String類型并發還給浏覽器一個響應,将拼接的内容都列印展示出來
java複制代碼resp.getWriter().write(stringBuilder.toString());