在Go web服務器中實現prefork和affinity

Apache服務器可是使用prefork技術,啟動多個獨立的進程,每個進程獨立的處理http請求,不需要擔心線程安全的問題。

This Multi-Processing Module (MPM) implements a non-threaded, pre-forking web server that handles requests in a manner similar to Apache 1.3. It is appropriate for sites that need to avoid threading for compatibility with non-thread-safe libraries. It is also the best MPM for isolating each request, so that a problem with a single request will not affect any other.

盡管prefork在處理高并發的情況下并不高效,但是作為一個技術,倒是有啟發我們的地方。我最近在調研Go服務器的性能看到一段代碼,很優雅的實現了prefork和affinity的的功能,特地抄寫在本文中,看看他是怎么實現的。

代碼出處: WebFrameworkBenchmark go-fasthttp

packagemain

import(
"flag"
"fmt"
"io"
"log"
"net"
"os"
"os/exec"
"runtime"

"github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/reuseport"
)

var(
 addr = flag.String("addr",":8080","TCP address to listen to")
 prefork = flag.Bool("prefork",false,"use prefork")
 affinity = flag.Bool("affinity",false,"use affinity for prefork")
 child = flag.Bool("child",false,"is child proc")
)

funcmain() {
 flag.Parse()

 ln := getListener()

iferr := fasthttp.Serve(ln, requestHandler); err !=nil{
 log.Fatalf("Error in ListenAndServe: %s", err)
 }
}

funcrequestHandler(ctx *fasthttp.RequestCtx) {
 io.WriteString(ctx, "Hello World")
}

funcgetListener() net.Listener {
if!*prefork {
 ln, err := net.Listen("tcp4", *addr)
iferr !=nil{
 log.Fatal(err)
 }
returnln
 }

if!*child {
 children := make([]*exec.Cmd, runtime.NumCPU())
fori :=rangechildren {
if!*affinity {
 children[i] = exec.Command(os.Args[0],"-prefork","-child")
 } else{
 children[i] = exec.Command("taskset","-c", fmt.Sprintf("%d", i), os.Args[0],"-prefork","-child")
 }
 children[i].Stdout = os.Stdout
 children[i].Stderr = os.Stderr
iferr := children[i].Start(); err !=nil{
 log.Fatal(err)
 }
 }
for_, ch :=rangechildren {
iferr := ch.Wait(); err !=nil{
 log.Print(err)
 }
 }
 os.Exit(0)
panic("unreachable")
 }

 runtime.GOMAXPROCS(1)
 ln, err := reuseport.Listen("tcp4", *addr)
iferr !=nil{
 log.Fatal(err)
 }
returnln
}

這個程序使用fast-http簡單的實現了一個web服務器,簡單的返回一個 hello world 。

如果程序啟動的時候加上了 -prefork 參數,它會使用 exec.Command 啟動多個子進程,子進程的數量和CPU的核數相同(第51行)。

如果程序啟動的時候加上了 -prefork 參數和"-affinity"參數,它會將子進程綁定在其中的一個CPU核上,這樣這個子進程只會被這個CPU執行。

子進程限定了使用的原生線程為1: runtime.GOMAXPROCS(1) 。

因為程序使用了 reuseport ,所以不會導致多個IP地址和端口被占用的情況,多個子進程可以共用相同的IP地址+端口監聽。

需要注意的事, reuseport 并不是所有的操作系統都支持,比如目前windows就不支持,所以只可能在高版本的Linux中使用。

 

來自: http://colobu.com/2016/05/03/use-prefork-and-affinity-in-Go/

 本文由用戶 FrancescoB9 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!