在瀏覽器要請求伺服器時,會經過〈握手協議(Handshaking)〉建立一條 TCP 連線,預設情況下,該次連線進行一次 HTTP 請求與回應,而後關閉 TCP 連線。
因此,瀏覽器在某次 HTTP 請求得到了個 HTML 回應後,若 HTML 中需要 CSS 檔案,瀏覽器必須再度建立連線,發出 HTTP 請求取得 CSS 檔案,而後連線關閉,若 HTML 中還需要有 JavaScript,瀏覽器又要建立連線,發出 HTTP 請求得到回應之後關閉連線 … 此過程重複直到必要的資源都下載完成,每次的請求回應都需要一條連線,在需要對網站效能進行最佳化、對使用者介面的高回應性場合上,著實是很大的負擔。
雖然 HTTP/1.1 支援管線化(Pipelining),可以在一次的 TCP 連線中,多次對伺服端發出請求,而不用等待伺服端回應,然而,伺服端必須依請求的順序進行回應,如果有某個回應需時較久,之後的回應也就會被延遲,造成 HOL(Head of line)阻塞的問題。
為了加快網頁相關資源的下載,有許多減少請求的招式因應而生,像是合併圖片、CSS、JavaScript,直接將圖片編碼為 BASE64 內插至 HTML 之中,或者是 Domain Sharding 等…
HTTP/2 支援伺服器推送(Server Push),也就是在一次的請求中,允許伺服端主動推送必要的 CSS、JavaScript、圖片等資源到瀏覽器,不用瀏覽器後續再對資源發出請求。
Servlet 4.0 規範中制訂了對 HTTP/2 的支援,在伺服端推送上,提供了 PushBuilder
,讓 Servlet 在必要的時候可以主動推送資源。例如:
package cc.openhome;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Optional;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/push")
public class Push extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Optional.ofNullable(request.newPushBuilder())
.ifPresent(pushBuilder -> {
pushBuilder.path("avatars/caterpillar.jpg")
.addHeader("content-type", "image/jpg")
.push();
});
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<body>");
out.println("<img src='avatars/caterpillar.jpg'>");
out.println("</body>");
out.println("</html>");
}
}
你可以透過 HttpServletRequest
的 newPushBuilder()
取得 PushBuilder
實例,如果 HTTP/2 不可用(瀏覽器或伺服器不支援的情況),那麼 newPushBuilder()
會傳回 null
,若能取得 PushBuilder
,就可以使用 path()
、addHeader()
等方式,加入主動推送的資源,然後呼叫 push()
進行推送。
在 Tomcat 9 中,若要啟用 HTTP/2 支援,必須在 server.xml 中設定 Connector,你必須準備好憑證,找到 server.xml 中的這些註解:
<!-- Define a SSL/TLS HTTP/1.1 Connector on port 8443 with HTTP/2
This connector uses the APR/native implementation which always uses
OpenSSL for TLS.
Either JSSE or OpenSSL style configuration may be used. OpenSSL style
configuration is used below.
-->
<!--
<Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
maxThreads="150" SSLEnabled="true" >
<UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
<SSLHostConfig>
<Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
certificateFile="conf/localhost-rsa-cert.pem"
certificateChainFile="conf/localhost-rsa-chain.pem"
type="RSA" />
</SSLHostConfig>
</Connector>
-->
將 <Connetor>
的註解去除,設定好你的憑證,重新啟動 Tomcat,就可以用支援 HTTP/2 的瀏覽器測試看看是否可取得 PushBuilder
。