適當的抽象
Servlet API已經很好地支持HTTP / 2優化,並允許框架利用服務器推送。
Servlet如何展示HTTP / 2特點?
Servlet是RFC的正確抽象。您不希望編程幀和流,因此隱藏網絡層的高級API會很好。在Servlets層,你可以做服務器推出沒有做低層次的東西。
一個請求和多個響應
Servlet API中的一個變化是在HTTP 1中我們有一個請求和一個響應。在HTTP/2中,這不再是唯一。可以有一個請求,服務器可能決定推送多個資源,然后最終以最初請求的頁面進行響應。您同時有一個請求和多個響應,這對Servlet API來說是一個挑戰。
服務器推送
服務器推送是HTTP/2中出現在servlet API中的許多改進中最明顯的部分。HTTP/2中的所有新功能(包括服務器推送)都旨在提高Web瀏覽體驗的感知性能。
改進了瀏覽器感知的性能
啟用服務器推送可提高瀏覽器的性能,因為服務器比客戶更能了解請求可能要求的附加資產(如圖像,樣式表和JavaScript)。
例如,服務器可以知道,無論何時瀏覽器請求index.html頁面,它都會請求標識圖像,樣式表和菜單JavaScript等。由於服務器知道這一點,他們可以先發制人地開始發送這些資產處理index.html。
不是Web Sockets的替代品
它只是允許你填充瀏覽器緩存。預計構建在像JSF這樣的Servlet上的框架將使用這個框架,並且我們使用push builder API來解決這個問題。
典型的流程
瀏覽器請求索引頁面。服務器會注意到它需要style_1.css和javaScript_1.js 文件,所以我們從HTTP請求中獲取PushBuilder,並將路徑設置為style_1.css文件並調用push,然后將路徑設置為javaScript_1。 js文件並再次調用推送。
注意在這種情況下,CSS和JavaScript將首先返回到客戶端,然后index頁面返回。
從HTTP請求推送生成器
只需從HTTP Request對象中獲取推式生成器,並將路徑設置為資源並推送即可。
在這個序列圖中有兩件事要注意,
- 推構建器可以重用。在示例中,我使用推式構建器將兩個資源推送到CSS文件和JavaScript文件。
- 第二件事是index.html在推送資源后返回瀏覽器。
原因是,如果索引在推送資源之前返回,瀏覽器將分析它並看到它需要這兩個資源。它會查看緩存並查看它沒有這些資源,它會請求它們。此時,瀏覽器緩存將不會預填充。所以推送的資源必須在索引發送之前先返回。
推送答應
前面提到的其中一種框架類型是 RST_STREAM, 這就是客戶如何拒絕推送承諾。因此,如果服務器推送一個資源,並且瀏覽器已經將其存儲在緩存中,那么不是讓服務器發送文件,而是發送一個RST_STREAM 幀,表明它已經有文件文件,因此不發送它。
PushBuilder
要使用服務器推送,從HttpServletRequest獲取對PushBuilder 的引用,根據需要改變生成器,然后調用 push() 方法。
PubshBuilder pubshBuilder= request.getPubshBuilder();
這會根據從中獲取此構建器的HttpServletRequest構建推送請求
這會根據從中獲取此構建器的HttpServletRequest生成推送請求。
javax.servlet.http.PushBuilder類
推送請求由請求方法設置為GET 構建。有條件, 范圍, 期望, 授權和請求標題被刪除。只有在maxAge未過期的情況下才會添加Cookie 。請求標頭將被設置為請求URL和存在的任何查詢字符串。如果 If-Modified-Since 或 If-None-Match 中的任何一個出現,則 isConditional() 將被設置為true。
只有URI路徑必需
唯一需要的設置是要用於推送請求的URI路徑。這必須在每次調用push()之前調用 。如果路徑包含查詢字符串,則查詢字符串將附加到現有查詢字符串(如果有),並且不會發生重復數據刪除。
以'/'開頭的路徑被視為絕對路徑。所有其他路徑都視為相對於用於創建此構建器實例的請求的上下文路徑。該路徑可能包含查詢字符串。
通過 在pushBuilder實例上調用push()方法來推送資源 。
@WebServlet("/WelcomeServlet") public class WelcomeServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{ if(request.getRequestURI.equals("/index.html")&&reqeust.isPubshSupported()){ request.getPushBuilder().path("/images/logo.jpg").push(); } } }
過濾器和服務器推送
解決這個問題的另一種方法是在過濾器中實現服務器推送。Jetty 在 org.eclipse.jetty.servlets 包中有一個 PushCacheFilter。
import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import org.apache.catalina.servlet4preview.http.PushBuilder; @WebFilter(urlPatterns="/*") public class PushFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpServletRequest=(HttpServletRequest)request; String uri=httpServletRequest.getRequestURI(); switch (uri) { case "/index.html": PushBuilder pushBuilder=httpServletRequest.getPushBuilder(); pushBuilder.path("/styles.css").push(); pushBuilder.path("/logo.png").push(); break; default: break; } chain.doFilter(request, response);; } @Override public void destroy() { } }
框架案例是服務器推送最重要的用例之一,它完全依賴於服務器事先知道客戶在客戶請求之前要求的資源,服務器web框架可以充分利用服務器推送的優勢
禁用/拒絕服務器推送
客戶端可以通過發送SETTINGS_ENABLE_PUSH設置值0(零)來顯式禁用服務器推送。
除了允許客戶端使用SETTINGS_ENABLE_PUSH設置禁用服務器推送之外,servlet容器還必須尊重客戶端的請求,以便不通過引用推送流的流標識符的CANCEL或REFUSED_STREAM代碼在更細粒度的基礎上接收推送的響應。這種交互的一個常見用途是瀏覽器在其緩存中已有資源時。