怎麼讓伺服器絲般順滑的重啟?

go是編譯型的語言,在伺服器開發上,不能像其他腳本語言那樣直接覆蓋個腳本文件就能實現功能的替換,得先把進程結束,替換bin文件,運行,這期間如果有用戶訪問的話則可能會受到影響。那麼問題就來了,大家是如何解決這個問題的?不要說凌晨5點去升級,那會我在睡覺呢。。。


前面加個反向代理 / 負載均衡之類的,後面開多個服務進程,重啟時一個個重啟就行,不影響業務。這是常見的做法。

或者用 plugin 機制。由 plugin 提供實現了 http.Handler 的函數,每次請求都動態地獲取一個,這樣就能動態地更新。

例如這樣:

package main

import (
"fmt"
"io/ioutil"
"net/http"
"plugin"
"sync/atomic"
)

var httpHandler atomic.Value

func main() {
handlerReady := make(chan struct{})
go startPluginManager(handlerReady)

&<-handlerReady fmt.Printf("handler ready, start server ") mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { handler := httpHandler.Load().(http.Handler) handler.ServeHTTP(w, req) }) server := http.Server{ Addr: ":8080", Handler: mux, } if err := server.ListenAndServe(); err != nil { panic(err) } } func startPluginManager(ready chan struct{}) { nUpdates := 0 updateServer := http.Server{ Addr: "localhost:28000", Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { filePath, err := ioutil.ReadAll(req.Body) if err != nil { panic(err) } handlerPlugin, err := plugin.Open(string(filePath)) if err != nil { panic(err) } sym, err := handlerPlugin.Lookup("Handler") if err != nil { panic(err) } handler, ok := sym.(func(http.ResponseWriter, *http.Request)) if !ok { panic(fmt.Errorf("Handler is not http.Handler")) } httpHandler.Store(http.HandlerFunc(handler)) if nUpdates == 0 { close(ready) } nUpdates++ }), } fmt.Printf("feed plugin path to %s ", updateServer.Addr) updateServer.ListenAndServe() }

處理請求的插件另外實現,例如:

package main

import "net/http"

func Handler(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("hello"))
}

編譯插件:

go build -buildmode=plugin -ldflags "-pluginpath=p-01" -o p-01.so p.g

把編譯出來的 .so 文件的路徑告訴 http 伺服器:

curl http://localhost:28000 -d "/home/reus/p-01.so"

然後就能正常工作了:

curl http://localhost:8080
hello?

更新的話,就只改插件代碼,然後重新編譯出 .so 文件:

go build -buildmode=plugin -ldflags "-pluginpath=p-02" -o p-02.so p.go

更新:

curl http://localhost:28000 -d "/home/reus/p-02.so"

新的請求就會用新的代碼了。


http://zhanqun.cheshirex.com


用服務副本吧,A,B同版本同時在服務,在替換A服務的時候,B舊版還能提供服務,A更新完後啟動服務,同時B shutdown,進行更替。

另外單實例服務在現實中也是不可靠的。


Reverse proxy + lame duck mode

https://landing.google.com/sre/book/chapters/load-balancing-datacenter.html

Google SRE Book chapter 20

升級 backend A 之前,A 進入 lame duck mode,新 client 全部給 B。升級 A 之後,A 開始接收新 xlient,B 進入 lame duck mode 並升級。


推薦閱讀:

通過哪些技術手段可以找回丟失的筆記本電腦?
請問各位,斐訊k2路由器真的有後門嗎?
Xbox 除了用來玩遊戲,還能用來做什麼?
生物數據為什麼是一種很好的大數據?

TAG:伺服器 | 計算機 | Go語言 |