lighttpd 整合 golang

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

lighttpd 整合 golang

Author:  nullchenEmail:   526624974@qq.com

簡介:

我們業務用fastcgi做接入,抽空研究了下fcgi如何運行在httpserver之上,主要是fcgi與httpserver的通訊,在這裡簡單的記錄一下。由於qzhttp是非開源的,這裡以lighttpd位對象,進行學習。本文分兩部分,第一部分簡單的分析lighttpd如何與fastcgi應用通訊,在此基礎上,第二部分位了對第一部分的結論進行驗證,簡單的將golang用fastcgi的方式整合到lighttpd中,讓lighttpd管理golang進程,從而實現 多進程+多線程+多協程 的模式。

第一部分

為了描述清楚,我們首先按照功能進行角色劃分如下:

fastcgi client:    fastcgi client的功能主要是將收到的http請求轉換成fastcgi請求發送給fastcgiserver,收到應答後轉換成http格式的應答並發送給http要求者。lighttpd具有fastcgi client的功能。fastcgi server:    fastcgi server的主要功能主要是進行進程的管理。並在各個進程中執行fastcgi application。從而使fastcgi application專註於商務邏輯。lighttpd具有fastcgi server的功能。fastcgi application:    該角色的主要功能是接受fastcgi server(lighttpd)發送過來的請求,按照fastcgi協議進行解碼(一般由庫提供,比如說我們寫fcgi是使用的庫),以及做商務邏輯的處理(商務邏輯處理部分一般指我們平時寫的fcgi代碼部分).

我們以lighttpd 的fastcgi模組和pyhton的flup模組為基礎來探索下python寫的fastcgi程式是如何運行在lighttpd中的。

我們先來看一個python實現的hello world fastcgi程式如下:

#!/usr/bin/pythonfrom flup.server.fcgi import WSGIServerdef app(environ, start_response):    start_response('200 OK', [('Content-Type', 'text/plain')])    return ['Hello World!\n']if __name__ == '__main__':    WSGIServer(app).run()

將上述代碼儲存為hello_world.fcgi 放在lighttpd中即可運行。顯然,與lighttpd的通訊是在flup.server.fcgi.WSGIServer這個模組中實現的,那麼我們進一步的跟進該模組,發現關鍵代碼如下(註:如下代碼摘抄自flup模組,為了閱讀方便進行了大量精簡):

FCGI_LISTENSOCK_FILENO = 0  --> 標準輸入sock = socket.fromfd(FCGI_LISTENSOCK_FILENO, socket.AF_INET,                                    socket.SOCK_STREAM)while self._keepGoing:    r, w, e = select.select([sock], [], [], timeout)    if r:        clientSock, addr = sock.accept()        # 經過上步驟就建立起來了一個串連,然後在該串連上對讀事件進行處理並按照fastcgi協議進行解碼後得到請求,然後用商務邏輯對該請求進行處理後得到應答,並將應答

其中_socket.fromfd_文檔如下(摘自python 標準庫手冊)

socket.fromfd(fd, family, type[, proto])

Duplicate the file descriptor fd (an integer as returned by a file object’s fileno() method) and build a socket object from the result Address family, socket type and protocol number are as for the socket() function above. The file descriptor should refer to a socket, but this is not checked — subsequent operations on the object may fail if the file descriptor is invalid. This function is
rarely needed, but can be used to get or set socket options on a
socket passed to a program as standard input or output (such as a
server started by the Unix inet daemon). The socket is assumed to
be in blocking mode.

可見,fastcgi application 是對標準輸入(fd=0)進行accept操作建立串連,然後進行讀寫的。然而,我們知道 標準輸入 是不支援accept操作的,因此可以猜測lighttpd在啟動 fastcgi application 的時候會把_fastcgi application_的標準輸入關聯到一個支援 accept操作的資料結構上。順著這個思路我們來把一把lighttpd的實現。在lighttpd的Mod_fastcgi.c中 fcgi_spawn_connection 找到如下代碼:

FCGI_LISTENSOCK_FILENO=0fcgi_fd = socket(socket_type, SOCK_STREAM, 0))setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))bind(fcgi_fd, fcgi_addr, servlen)listen(fcgi_fd, 1024)switch ((child = fork())) {    case 0: {            dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO)        close(fcgi_fd)        ......        execve(arg.ptr[0], arg.ptr, env.ptr);    ---->這裡執行_fastcgi application_ 程式。    }}

其中 *dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO) *

這行代碼是關鍵,將子進程的 標準輸入 指向到了一個處於 listen 狀態的unixsocket fd。因此在 fastcgi application 中才可能對 標準輸入 進行accept操作。

結論:由此我們可以得到這樣一個結論。fastcgi server(lighttpd)與fastcgi application 資料互動步驟如下:

*  fastcgi server bind,listen 一個unix socket 得到一個fd*  fastcgi server fork*  子進程 dup2(fd,stdin) -->將子進程的stdin指向一個處於listen狀態的unix socket*  子進程中執行 fastcgi application*  fcgi app中 accept stdin 等待串連到來並處理*  fcgi server connect 對應的unixsocket並發收資料

第二部分

根據第一部分的結論。由此得到如下的golang代碼,該代碼可以以fastcgi的方式運行在lighttpd中。

package mainimport (    "net"    "net/http"    "net/http/fcgi"    "os"    "time")type FastCGIApp struct{}func (s *FastCGIApp) ServeHTTP(resp http.ResponseWriter, req *http.Request) {    msg := req.FormValue("msg")    if msg == "sleep" {        time.Sleep(time.Second * 60)    }    resp.Write([]byte(msg))}func main() {    listener, _ := net.FileListener(os.Stdin)    app := new(FastCGIApp)    fcgi.Serve(listener, app)}

對上述代碼的處理:

0. 儲存為 try_fastcgi.go1. go build try_fastcgi.go 得到可執行檔 try_fastcgi2. mv try_fastcgi test.fcgi3. 將test.fcgi 放到lighttpd 對應的位置即可

lighttpd 配置如下:

server.modules += ( "mod_fastcgi" )fastcgi.server = (    ".fcgi" =>    ( "fcgi-num-procs" =>                 (                   "socket" => socket_dir + "/fastcgi-test.socket",                   "bin-path" => server_root + "/cgi-bin/test.fcgi",                   "max-procs" => 1,                   "min-procs" => 1,                   "broken-scriptfilename" => "enable",                 )    ),)
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.