先貼幾個鏈接
前三篇
Go實現FastCgi Proxy Client 系列(三)
Go實現FastCgi Proxy Client 系列(二)
Go實現FastCgi Proxy Client 系列(一)
靈感帖
TCP keepalive 和 http keep-alive
很感謝上面幾個文章,讓我突然想通了我之前一直沒搞懂的問題,雖然問題不是一種,但是也算是旁敲側擊的讓我茅塞頓開了,非常感謝!
分析
協議
麻省理工學院的文檔上是這麼描述的:
The flags component contains a bit that controls connection shutdown:
flags & FCGI_KEEP_CONN: If zero, the application closes the connection after responding to this request. If not zero, the application does not close the connection after responding to this request; the Web server retains responsibility for the connection.
好吧,高大上的描述。各種文檔裏對fastcgi協議裏沒怎麼講keep-alive的作用,就只是很簡單的說了,如果是開啓了,就能讓http連接進行復用。然而按照之前的知識(我沒跟上時代啊),http協議是無狀態的協議,死活我也是想不通如何連接複用。
然而我忘記了,http也是基於tcp協議的,tcp一般而言,如果你不手動進行斷開,或者網絡原因引起的話,是會保持一個長連接的(通常都會設置超時)。
盜張圖:
可以看出,假如,我們不進行最後的4次揮手操作,在超時範圍內,這個tcp連接是不會斷開的。
代碼
然後我再去看go官方對fastcgi協議的實現,我發現一個很重要的地方(源碼位置 net/http/fastcgi/child.go):
當用戶發起終止信號的時候,keep-alive起了作用,那就是說,我的proxy層只要不進行斷開連接,這個tcp連接就依然還是可用的。
case typeAbortRequest:
//......
if !req.keepConn {
// connection will close upon return
return errCloseConn
}
return nil
然後,我繼續閱讀源碼,此時,我要唱一下,“終於等到你,還好我沒忘記”。果然是這樣,如果鏈接不是keep-alive,會即時關閉tcp連接。
func (c *child) serveRequest(req *request, body io.ReadCloser) {
//......
if !req.keepConn {
c.conn.Close()
}
}
修改實現
在我們讀取數據的位置,我們進行相關操作即可,如果,請求不是keep-alive 自然,返回值會包含終止符 EOF,這時可以直接返回;反之,如果是keep-alive,則會每個請求都獲得到一個typeEndRequest(值爲3,具體請看第一篇)的標識
// recive untill EOF or FCGI_END_REQUEST
for {
err1 = rec.read(cgi.rwc)
//if !keep-alive the end has EOF
if err1 != nil {
if err1 != io.EOF {
err = err1
}
break
}
switch {
case rec.h.Type == typeStdout:
retout = append(retout, rec.content()...)
case rec.h.Type == typeStderr:
reterr = append(reterr, rec.content()...)
case rec.h.Type == typeEndRequest:
//if keep-alive
//It's had return
//But connection Not close
retout = append(retout, rec.content()...)
return
default:
//fallthrough
}
}
總結
個人總結
那,其實很簡單不是嘛?我之前只是理解錯了typeEndRequest這個類型,我把他當成了錯誤的時候纔會返回,報了一個fallthrough。更簡單的一句就是,複用的不是http,不是http,不是!複用的tcp!tcp!tcp!
然後,當遇到一個自己暫時無法理解的問題時候,可能你已經鑽入了死衚衕。這個時候,你可以放鬆自己精神,玩玩遊戲,運動運動都行(哈哈,這個問題我都忘記一個月了,感謝羣友的問題,雖然我沒幫他解決,但是他提示了我,我還要沒解決的問題)。
下一步
下一步,將會對超時進行設置。
spinx(小玩具)
一個實現對fastcgi協議的轉發小玩具。
Quick Start
go get github.com/lwl1989/spinx
cd $gopath/src/github.com/lwl1989/spinx
go build -o spinx main.go
Install
sudo ./spinx -c=config_path install
Remove
sudo ./spinx remove
Start
sudo ./spinx start
or
./spinx -d=false -c=config_path
Stop
sudo ./spinx stop