在Go web服务器中实现prefork和affinity

FrancescoB9 3年前
   <p>Apache服务器可是使用prefork技术,启动多个独立的进程,每个进程独立的处理http请求,不需要担心线程安全的问题。</p>    <p>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.</p>    <p>尽管prefork在处理高并发的情况下并不高效,但是作为一个技术,倒是有启发我们的地方。我最近在调研Go服务器的性能看到一段代码,很优雅的实现了prefork和affinity的的功能,特地抄写在本文中,看看他是怎么实现的。</p>    <p>代码出处: <a href="/misc/goto?guid=4959672242786633672" rel="nofollow,noindex">WebFrameworkBenchmark go-fasthttp</a> 。</p>    <pre>  <code class="language-go">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  }  </code></pre>    <p>这个程序使用fast-http简单的实现了一个web服务器,简单的返回一个 hello world 。</p>    <p>如果程序启动的时候加上了 -prefork 参数,它会使用 exec.Command 启动多个子进程,子进程的数量和CPU的核数相同(第51行)。</p>    <p>如果程序启动的时候加上了 -prefork 参数和"-affinity"参数,它会将子进程绑定在其中的一个CPU核上,这样这个子进程只会被这个CPU执行。</p>    <p>子进程限定了使用的原生线程为1: runtime.GOMAXPROCS(1) 。</p>    <p>因为程序使用了 reuseport ,所以不会导致多个IP地址和端口被占用的情况,多个子进程可以共用相同的IP地址+端口监听。</p>    <p>需要注意的事, reuseport 并不是所有的操作系统都支持,比如目前windows就不支持,所以只可能在高版本的Linux中使用。</p>    <p> </p>    <p>来自: <a href="/misc/goto?guid=4959672242867962077" rel="nofollow">http://colobu.com/2016/05/03/use-prefork-and-affinity-in-Go/</a></p>