12.23 一篇文章帶你看懂各類網關接口(GI)

前幾天和同事討論起CGI的性能、CGI為什麼是一項落後的技術的時候,發現其實很多人對CGI等的網關技術並不熟悉。這裡相信很多同學也都聽過CGI、fastCGI、WSGI等的詞語,但是應該很多同學都分辨不清它們的區別和聯繫,本篇文章,就和大家一起探討這些技術的發展簡史。而本篇文章標題的GI,指的就是Gateway Interface(網關接口)的縮寫。

瞭解網關接口之前,我們需要先弄清楚兩個東西,Web服務器和Web應用程序。什麼是Web服務器?什麼是Web應用程序?雖然在我的Django小程序課程裡面,有對這兩者的區別展開來介紹,但是這裡考慮到很多同學並沒有學習這門課,所以我們先把這兩個概念弄清楚,否則在後面討論起網關接口,也只會一知半解。

Web服務器

首先,什麼是Web服務器。Web服務器是服務於網站後臺的一個軟件,是常駐於物理服務器的一個計算機程序。Web服務器可以向瀏覽器等Web客戶端提供文檔,也可以放置網站文件,讓全世界瀏覽;可以放置數據文件,讓全世界下載。目前最主流的三個Web服務器是Apache、 Nginx 、IIS。

Apache是世界使用排名第一的Web服務器軟件。它可以運行在幾乎所有廣泛使用的計算機平臺上,由於其跨平臺和安全性被廣泛使用,是最流行的Web服務器端軟件之一。它快速、可靠並且可通過簡單的API擴充,將Perl/Python等解釋器編譯到服務器中。

Nginx是一款輕量級的Web服務器/反向代理服務器及電子郵件(IMAP/POP3)代理服務器,在BSD-like 協議下發行。其特點是佔有內存少,併發能力強,事實上Nginx的併發能力在同類型的網頁服務器中表現較好,中國大陸使用Nginx網站用戶有:百度、京東、新浪、網易、騰訊、淘寶等。

這裡先簡單討論一下Nginx和Apache的區別,在資源上,Nginx佔用的內存更少;在性能上,Nginx的併發能力更強;在社區方面,Nginx和Apache都擁有廣泛的使用者,Apache的插件會比Nginx豐富一些。另外Nginx的功能比Apache要豐富一些,Nginx除了作為Web服務器以外,也經常作為反向代理服務器等使用,而Apache則主要作為Web服務器去使用。

還有IIS,IIS是微軟公司在Windows Server提供的一個Web服務器,一般部署在Windows系統上,也有一定的使用者。

Web應用程序

瞭解了Web服務器,我們接著瞭解一下Web應用程序。

Web應用程序一般指的是完成業務邏輯的程序,比如熟悉Python語言的,經常會用Django、Flask、Tornado等框架來完成業務邏輯的處理,接受請求,返回結果;熟悉Java語言的,則經常會使用Spring、Spring Boot來完成業務邏輯處理;熟悉PHP語言的,則常用ThinkPHP、Laravel、CI等等框架來完成Web後臺的開發;還有Golang、Node.js等等,都會有相應的框架來完成Web請求的處理。

這些就是定義為Web應用程序的東西,也就是我們常說的Web框架,比如一個Pythoner問另外一個Pythoner,你一般使用什麼Web框架呀?這個就是Web應用程序。

那麼Web服務器和Web應用程序有啥區別?其實在前面的描述裡面,我已經說的比較清楚了,Web應用程序主要是完成業務邏輯的處理,Web服務器則主要是進行外部請求的接收和轉發。這裡需要注意的是,我們一般不應該把業務邏輯放在Web服務器上去處理,雖然說Web服務器支持廣泛的腳本來進行拓展,但是我們一般情況下還是不應該把業務邏輯放在Web服務器去處理,比如攔截請求、鑑權處理等等的操作。

看到這裡可能有些人又會有疑問了,在開發Web應用的時候,我們直接就可以把應用跑起來接受請求了呀,為啥還需要Web服務器的存在。比如說使用Django的時候,直接輸入命令python manage.py runserver,就可以接收請求了,為啥還需要Web服務器呢。

這主要是從性能上去考慮的,Web應用雖然在開發階段就可以通過一些命令來啟動服務,但是這些功能主要是提供調試所使用的,真正部署上線的時候,如果流量較大,性能是不行的,所以需要有Web服務器的存在。Web服務器是專門用於接收外部請求並處理的軟件,所以Web服務器在性能上會比Web應用更佳,畢竟術業有專攻。Web服務器處理靜態資源請求(CSS、JS、HTML、圖片)等,性能會比Web應用更好。另外對於一些非靜態資源的請求,則需要轉發到後臺Web應用。

到這裡,我們基本分清楚Web服務器和Web應用的區別了,這裡面我們注意一個關鍵詞“轉發”,當Web服務器接受請求的時候,會把請求轉發到後臺Web應用去處理,我們本篇文章的主角,GI就工作在這個轉發的過程中。

CGI

終於輪到主角“網關接口”。前面介紹了GI工作的地方,接下來我們就來一起探討一下現在有哪些GI,他們有哪些特點和區別。

首先是CGI,CGI是Common Gateway Interface (通用網關接口)的縮寫。CGI是一項古老的技術。最初是在1993年由美國國家超級電腦應用中心(NCSA)為NCSA HTTPd Web服務器開發的。CGI的原理非常簡單粗暴,在支持CGI的Web服務器下,只需要寫一個簡單的腳本,然後在腳本中輸出內容,這些內容就會返回給前臺。舉個簡單的例子,使用Python實現一個hello.py:

<code>#!/usr/bin/env python

print 'Hello, World! Hello CGI.'
/<code>

然後把他改名為hello.cgi,部署到Web服務器下,當請求到達的時候,打印的字符串就會通過Web服務器返回到頁面。我們常常稱這種應用為console application (也可以稱作command-line interface programs) 的程序,這樣的腳本稱為CGI腳本。

CGI工作完整的原理是這樣的:

當用戶訪問我們的 Web 應用時,會發起一個 HTTP 請求。最終 Web 服務器接收到這個請求。Web 服務器創建一個新的 CGI 進程。在這個進程中,將 HTTP 請求數據已一定格式解析出來,並通過標準輸入和環境變量傳入到 URL 指定的 CGI 程序。Web 應用程序處理完成後將返回數據寫入到標準輸出中,Web 服務器進程則從標準輸出流中讀取到響應,並採用 HTTP 協議返回給用戶響應。

一句話就是 Web 服務器中的 CGI 進程將接收到的 HTTP 請求數據讀取到環境變量中,通過標準輸入轉發給CGI程序;當CGI程序處理完成後,Web 服務器中的CGI進程從標準輸出中讀取返回數據,並轉換回 HTTP 響應消息格式,最終將頁面呈現給用戶。然後 Web 服務器關閉掉這個 CGI 進程。

可以說 CGI 協議特別擅長處理 Web 服務器和 Web 應用的通信問題。然而,它有一個嚴重缺陷,對於每個請求都需要重新 fork 出一個 CGI 進程,處理完成後立即關閉。當請求量比較大時,會有嚴重的性能問題,因為頻繁的創建進程和回收進程,會佔用非常多的服務器資源。因為CGI的性能問題,於是新一代的CGI技術,FastCGI應運而生。

FastCGI

快速通用網關接口(Fast Common Gateway Interface/FastCGI)是一種讓交互程序與Web服務器通信的協議。FastCGI是早期通用網關接口(CGI)的增強版本。

FastCGI致力於減少網頁服務器與CGI程序之間交互的開銷,從而使服務器可以同時處理更多的網頁請求。

而FastCGI的工作原理是這樣的:

FastCGI 進程管理器啟動時會創建一個 主(Master) 進程和多個 CGI 解釋器進程(Worker 進程),然後等待 Web 服務器的連接。Web 服務器接收 HTTP 請求後,將 CGI 報文通過 套接字(UNIX 或 TCP Socket)進行通信,將環境變量和請求數據寫入標準輸入,轉發到 CGI 解釋器進程。CGI 解釋器進程完成處理後將標準輸出和錯誤信息從同一連接返回給 Web 服務器。CGI 解釋器進程等待下一個 HTTP 請求的到來。

所以FastCGI可以看作是解決原始CGI性能問題的一個升級版本。

WSGI

WSGI:全稱是Web Server Gateway Interface,Web服務器網關接口,WSGI不是服務器,Python模塊,框架,API或者任何軟件,只是一種規範,描述前面Web服務器如何與Web應用通信的規範。

前面介紹網關接口的時候介紹了CGI、FastCGI等,那這裡的WSGI主要是Python為Web服務器和Web應用程序之間通信而設計的。在Python中,常見的Web框架Tornado、Django、Flask都支持使用WSGI和Web服務器進行通信,而在採用WSGI協議的時候,則常常安裝uWSGI模塊來完成通信。

ASGI

與WSGI類似的還有一個ASGI,ASGI也是由Python實現的一類網關接口,這裡的A指的是Async,異步的意思。ASGI是WSGI的擴展,支持除了原來的HTTP以外的WebSocket等的網絡協議。

Servlet、Tomcat

終於來到Java陣營了,在前面的CGI、FastCGI、WSGI等等,主要都是PHP、C++、Python等語言實現Web應用程序和Web服務器通信的網關接口,尤其是CGI、FastCGI,在以前C++、PHP大行其道的時候,它們擁有廣泛的用戶基礎,像騰訊等大企業,由於歷史的關係,內部系統中還有很多保留著CGI、FastCGI等技術。那麼在Java的陣營下,一般使用什麼技術作為Web服務器和Web應用框架之間的通信呢?

Servlet,談到Java不得不談的一項技術,全稱Server Applet,是用Java編寫的服務器端程序,可以看做是前面我們介紹的Web應用程序。其主要功能在於交互式地瀏覽和修改數據,生成動態Web內容。Servlet最初誕生在1997年,最新的一個版本Servlet4.0於2017年9月份推出,Servlet擁有廣泛的用戶群體。

如果說Servlet是應用程序,那Servlet容器就是包含應用程序的Web服務器,常見的Servlet容器就有我們熟知的湯姆貓(Tomcat)、Jetty等,Servlet容器管理著Servlet應用,我們的Servlet程序一般通過Servlet容器對外進行服務。

隨著Docker容器技術和K8S容器編排技術的成熟,今天微服務技術也是非常熱門,而微服務離不開的Spring Boot、Spring Cloud的其中,就有Servlet、Servlet容器的身影。

我們說說Spring Boot,對於Spring Boot,大家都被他快捷的開發和部署方式所吸引,Spring Boot是由早期的Spring框架發展而來的,其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。在我們開發使用Spring Boot開發Web後臺的時候,我們在寫完邏輯代碼的時候,在本地只需要點擊運行,就能通過瀏覽器進行調試,頗為方便;這和前面我們使用PHP、Python開發Web程序非常類似,我們在使用Flask、Tornado等框架開發Web程序的時候,也可以通過簡單的啟動,就可以在本地通過瀏覽器去調試。但是這裡是有一些區別的,我們說Flask、Tornado等框架是Web應用框架,沒有說他們是Web服務器,他們通過前面介紹的網關接口(GI)通信,但是在使用Spring Boot的時候,卻很少說需要把它部署在Web服務器Apache、Nginx之下,一般部署Spring Boot應用的時候,我們直接把它打包成jar包,就可以在服務器跑起來對外服務了。這是為什麼呢?

其實Spring Boot內嵌了Servlet容器Tomcat,也就是Spring Boot既充當了Web應用程序的角色,也充當了Web服務器的角色,在開發Spring Boot應用程序的過程中,我們是先通過代碼實現了Web應用程序的邏輯,處理請求和應答,而Servlet容器的部分,完全不需要我們去配置,這就是Spring Boot簡單、便捷,高效的開發效率的原因,所以現在很多同學都喜歡使用Spring Boot去開發Web的後臺。Spring Boot提供了簡單的開發模式,但是也帶來一些問題,比如說很多同學根本無法分清楚Web應用程序和Web服務器的區別等等。

最後簡單的回顧一下今天這篇文章,我們主要是介紹了現在Web後端各種網關接口,包括CGI、FastCGI、WSGI等,在理解這些之前,我們需要弄懂Web服務器和Web應用程序,而網關接口,就是主要工作在Web服務器和Web應用程序之間的。最後我們還介紹了Java陣營的技術,在Java現在大行其道的Spring Boot框架裡面,我們不再特意強調Web服務器和Web應用程序,Spring Boot框架是把兩個融合在一起的一個框架,Spring Boot可以便捷的開發和部署Web程序,就是因為內部包含了一個叫Tomcat的Servlet容器,也就是Web服務器。

最後希望這篇文章對大家捋清楚Web後臺技術和網關接口,都能有一定的幫助。