【雜談】從CGI到Servlet
訪問服務器的靜態頁面
每個Web服務器都運行著一個HTTP服務軟件,用於響應web瀏覽器的請求,返回客戶想要的頁面。HTTP服務器都會有一個文件夾用於放置相關的頁面文件,默認是 /user/local/apache/htdocs 。例如,服務器的域名叫 example.com。那客戶端瀏覽器訪問http://example.com/index.html 就是訪問服務器上的HTTP服務器(URL如果沒有指定端口,就是訪問80端口,80端口綁定的是HTTP服務,80端口是默認對外開啟的),那HTTP服務器就會重定向到 /user/local/apache/htdocs 文件夾,並查詢是否有index.html 文件,有則返回,沒有則返回錯誤報文。
訪問服務器的動態頁面
服務器只提供靜態頁面還不夠,客戶端想要更多的服務,比如動態的頁面(頁面上顯示當前使用者信息等等),或者修改數據庫內容。這時候,服務端就不能單純地映射並返回文件來實現了,這時候就需要有可執行的腳本程序來對頁面進行更改,或者其他操作。就拿動態頁面來說,客戶端瀏覽器訪問 http://example.com/home?name=randy 這裏用戶帶上了自己的用戶名,希望訪問的主頁右上角能顯示自己的名字。這裏 home 通過映射到一個CGI程序,並執行。該程序通過標準I/O流,解析HTTP報文,獲取到了請求參數,並通過程序生成對應的HTML文件,在通過標準I/O流,將文件返回給請求客戶。
註:CGI (Common Gateway Interface)通用網關接口,CGI程序常見的由Perl,Python,C,C++ 或者任何可利用標準I/O流的語言。
Java的一種想法
一開始,人們想最理想的方式就是完全基於Java的實現方案, servlets <=> applets 。即
客戶端用applet校驗並發送數據,
服務端用servlet接收並處理數據。
就連這名稱都是一對。
但是,並非所有瀏覽器都支持Java。這樣就帶來一個問題,那就是你搭建的網站,只有部分瀏覽能夠訪問,而且需要安裝對應程序。後來,就把所有的操作都交給服務器處理,然後把最終生成的網頁發給客戶端瀏覽器。這樣,只要有瀏覽器,就可以訪問對應網站,且不需要安裝任何程序。
Servlet的引入
Servlet是基於Java的CGI程序框架。首先,因為它是一個框架,所以就可以省去很多底層編寫的工作。又因為是用Java實現的,Java程序運行於Java虛擬機,跨平臺性好,便於網站遷移。
Servlet的體系架構
Servlet容器,如Tomcat,接收所有客戶端請求,如 http://example.com:8080/project/oneServlet,根據"project"找到對應的項目(tomcat的server.xml保存項目映射信息),再根據"oneServlet"找到對應的Servlet(Web項目的web.xml保存Servlet映射信息),然後調用該Servlet對象的service()方法。service方法會根據請求的方式,委派給doGet、doPost、doDelete等方法進行處理。
註:Servlet的多數內容由容器實現,如Session的保存和獲取,Tomcat實現內容在其子目錄lib下的catalina相關包內。
Servlet的持久化
Servlet會保存其變量域的值,使其在多個請求之間保留相應變量值。Servlet容器會持久化Servlet對象。至於什麽時候刷新,尚未考察,應該是在每次service()方法返回之後。
如果是CGI程序,則需要自己手動將數據寫入到磁盤,這也是Servlet的優點之一。
Servlet的頁面生成與JSP
如果單純用Servlet生成頁面,那麽將會非常痛苦,因為這樣代碼裏就會摻雜大量的 換行符、轉義標識、“+”符號,字符串的構建以及代碼閱讀都非常困難。後來就有了JSP文件,整合了HTML和Java代碼。使得文件內可以直接抒寫HTML代碼,而不需要用字符串拼接。也可以用有特殊標簽包圍的Java代碼。(實際上JSP就是Servlet,它會被容器內會被轉換成Servlet的.java文件,然後在編譯成.class文件)但是,JSP還有問題沒解決,那就是,負責維護JSP的開發人員必須既懂HTML又懂Java。而且兩種語言混合在一起(一種程序語言,一種標記語言),在加上各種標簽庫,看起來比較吃力。
偏題內容:頁面渲染的位置
早期是服務器把網頁完全渲染好後,然後發給客戶端瀏覽器,瀏覽器負責顯示就可以了。但是這樣的話,服務器的壓力就比較大,而且響應時間會比較長,客戶端要等到服務端把頁面完全渲染好後才能看見頁面。後來的做法,是這樣的,服務器先把有基本內容的網頁文件發給客戶端瀏覽器,然後瀏覽器再異步地請求(如通過Ajax)獲取數據,然後根據獲取到的數據對網頁進行渲染(例如獲取到一個數組對象,然後根據數組生成一個表格,顯示數據)。這樣,服務器的壓力就小多了,而且客戶端也能較早地看見頁面(尚不完整,部分數據還未加載)。
那瀏覽器有是怎麽就知道發異步請求呢?是這樣的,用javascript寫全局代碼,或者覆蓋Vue對象中的created函數,這樣瀏覽器一收到網頁文件並加載的時候,這些JS代碼就會被觸發執行。
Servlet與多線程
Servlet容器維護有一個線程池用於分派處理客戶端的請求。所以,多個請求可以並發,甚至並行處理。
Servlet處理會話(Session)—— 利用Cookie
HTTP是無狀態協議,多次請求中,單純靠HTTP協議,我們並不能辨別其是否是同一個人。但是,有處理會話的需求(例如購物車的實現需求),這時候就要追蹤會話。追蹤會話的方式有很多,最常見的是持久化Cookie。
Session-Cookie機制:Cookie是服務器發給客戶端的一小段信息(包含Session的唯一標識符),瀏覽器收到Cookie後,把它寫入到磁盤持久化。無論何時,如果訪問與某個Cookie相關聯的URL時,請求中就會帶上這個Cookie。Servlet如果想要獲取本次請求的會話,就調用HttpServletRequest.getSession()獲取。getSession()方法獲取Session的實現方式尚未考察,但猜測應該是根據請求中的Cookie,從Servlet容器內檢索對應的Session。
Session的存活時間
Session即會話的存活時間默認是30min,但是具體情況,得看對應的Servlet容器。每個容器的會話存活時間都不盡相同。
Session的用途
Session對象的作用就是捕捉客戶端與服務端交互的有用數據,如前面提到的購物車信息,還有就是Session對象可以用來保存認證信息(即是否已登錄),這樣多次請求中,Servlet只要確認"是否登錄"狀態即可,而不需要用戶每次請求都登錄。
【雜談】從CGI到Servlet