友情提示:該結(jié)論當(dāng)前只在Golang http server上測(cè)試過,其他語(yǔ)言編寫的http server可以參考測(cè)試方法進(jìn)行驗(yàn)證。
測(cè)試:
- 使用golang寫一個(gè)支持優(yōu)雅關(guān)閉的http server,示例代碼如下:
package main
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
// handler 處理 HTTP 請(qǐng)求
func handler(w http.ResponseWriter, r *http.Request) {
time.Sleep(20 * time.Second)
fmt.Fprintf(w, "Hello, World!")
}
func main() {
// 創(chuàng)建 HTTP 服務(wù)器
mux := http.NewServeMux()
mux.HandleFunc("/", handler)
server := &http.Server{
Addr: ":8080",
Handler: mux,
}
// 創(chuàng)建一個(gè)通道來監(jiān)聽系統(tǒng)信號(hào)
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
// 啟動(dòng)服務(wù)器
go func() {
fmt.Println("Server is running on :8080")
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
fmt.Printf("ListenAndServe error: %v\n", err)
}
}()
// 阻塞等待系統(tǒng)信號(hào)
<-stop
fmt.Println("Shutting down server...")
// 創(chuàng)建超時(shí)上下文用于關(guān)閉服務(wù)器
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
fmt.Printf("Server forced to shutdown: %v\n", err)
} else {
fmt.Println("Server exited properly")
}
}
- 使用http客戶端請(qǐng)求
http://127.0.0.1:8080,客戶端不限,比如:curl、telnet、瀏覽器等等 - 使用wireshark抓包,查看服務(wù)端與客戶端之間的通信
結(jié)論:
- [http-client-1] 如果在http server Shutdown之前發(fā)起的請(qǐng)求,http server會(huì)繼續(xù)處理,如果在timeout時(shí)間內(nèi)處理完成則正常響應(yīng)(圖中未給出),如果timeout時(shí)間內(nèi)還未處理完成則http server強(qiáng)制使用FIN終止連接,應(yīng)用層的表現(xiàn),以curl工具為例:
$ curl -v http://127.0.0.1:8080/
* Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080
> GET / HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
* Empty reply from server
* Closing connection
curl: (52) Empty reply from server
- [http-client-2] 如果客戶端在http server Shutdown過程中發(fā)起請(qǐng)求,則http server不會(huì)處理該請(qǐng)求,直接以RST包終止連接,應(yīng)用層的表現(xiàn),以curl工具為例:
$ curl -v http://127.0.0.1:8080/
* Trying 127.0.0.1:8080...
* connect to 127.0.0.1 port 8080 from 127.0.0.1 port 61852 failed: Connection refused
* Failed to connect to 127.0.0.1 port 8080 after 0 ms: Couldn't connect to server
* Closing connection
curl: (7) Failed to connect to 127.0.0.1 port 8080 after 0 ms: Couldn't connect to server
- [http-client-3] 如果客戶端在http server Shutdown之前建立了連接,但是在Shutdown之前沒有發(fā)起http請(qǐng)求或者所有請(qǐng)求都已經(jīng)完成,那么http server會(huì)立即使用FIN包終止該連接。也就是說http server Shutdown時(shí),會(huì)關(guān)閉空閑連接
時(shí)序圖:更清晰的展示http server優(yōu)雅關(guān)閉的過程和行為

image.png