自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Go 如何利用 Linux 內(nèi)核的負(fù)載均衡能力

系統(tǒng) Linux
socket 編程是每位程序員都應(yīng)該掌握的基礎(chǔ)知識(shí)。因此,大家應(yīng)該知道,socket 連接通過(guò)五元組唯一標(biāo)識(shí)。任意兩條連接,它的五元組不能完全相同。

[[420233]]

在測(cè)試 HTTP 服務(wù)時(shí),如果該進(jìn)程我們忘記關(guān)閉,而重新嘗試啟動(dòng)一個(gè)新的服務(wù)進(jìn)程,那么將會(huì)遇到類似以下的錯(cuò)誤信息:

  1. $ go run main.go 
  2. listen tcp :8000: bind: address already in use 

這是由于默認(rèn)情況下,操作系統(tǒng)不允許我們打開具有相同源地址和端口的套接字 socket。但如果我們想開啟多個(gè)服務(wù)進(jìn)程去監(jiān)聽同一個(gè)端口,這可以嗎?如果可以,這又能給我們帶來(lái)什么?

socket 五元組

socket 編程是每位程序員都應(yīng)該掌握的基礎(chǔ)知識(shí)。因此,大家應(yīng)該知道,socket 連接通過(guò)五元組唯一標(biāo)識(shí)。任意兩條連接,它的五元組不能完全相同。

  1. {<protocol>, <src addr>, <src port>, <dest addr>, <dest port>} 

protocol 指的是傳輸層 TCP/UDP 協(xié)議,它在 socket 被創(chuàng)建時(shí)就已經(jīng)確定。src addr/port 與 dest addr/port 分別標(biāo)識(shí)著請(qǐng)求方與服務(wù)方的地址信息。

因此,只要請(qǐng)求方的 dest addr/port 信息不相同,那么服務(wù)方即使是同樣的 src addr/port ,它仍然可以標(biāo)識(shí)唯一的 socket 連接。

基于這個(gè)理論基礎(chǔ),那實(shí)際上,我們可以在同一個(gè)網(wǎng)絡(luò)主機(jī)復(fù)用相同的 IP 地址和端口號(hào)。

Linux SO_REUSEPORT

為了滿足復(fù)用端口的需求,Linux 3.9 內(nèi)核引入了 SO_REUSEPORT選項(xiàng)(實(shí)際在此之前有一個(gè)類似的選項(xiàng) SO_REUSEADDR,但它沒(méi)有做到真正的端口復(fù)用,詳細(xì)可見參考鏈接1)。

SO_REUSEPORT 支持多個(gè)進(jìn)程或者線程綁定到同一端口,用于提高服務(wù)器程序的性能。它的特性包含以下幾點(diǎn):

  • 允許多個(gè)套接字 bind 同一個(gè)TCP/UDP 端口
    • 每一個(gè)線程擁有自己的服務(wù)器套接字
    • 在服務(wù)器套接字上沒(méi)有了鎖的競(jìng)爭(zhēng)
  • 內(nèi)核層面實(shí)現(xiàn)負(fù)載均衡
  • 安全層面,監(jiān)聽同一個(gè)端口的套接字只能位于同一個(gè)用戶下(same effective UID)

有了 SO_RESUEPORT 后,每個(gè)進(jìn)程可以 bind 相同的地址和端口,各自是獨(dú)立平等的。

讓多進(jìn)程監(jiān)聽同一個(gè)端口,各個(gè)進(jìn)程中 accept socket fd 不一樣,有新連接建立時(shí),內(nèi)核只會(huì)調(diào)度一個(gè)進(jìn)程來(lái) accept,并且保證調(diào)度的均衡性。

其工作示意圖如下

有了 SO_REUSEADDR 的支持,我們不僅可以創(chuàng)建多個(gè)具有相同 IP:PORT 的套接字能力,而且我們還得到了一種內(nèi)核模式下的負(fù)載均衡能力。

Go 如何設(shè)置 SO_REUSEPORT

Linux 經(jīng)典的設(shè)計(jì)哲學(xué):一切皆文件。當(dāng)然,socket 也不例外,它也是一種文件。

如果我們想在 Go 程序中,利用上 linux 的 SO_REUSEPORT 選項(xiàng),那就需要有修改內(nèi)核 socket 連接選項(xiàng)的接口,而這可以依賴于 golang.org/x/sys/unix 庫(kù)來(lái)實(shí)現(xiàn),具體就在以下這個(gè)方法。

  1. import “"golang.org/x/sys/unix"” 
  2. ... 
  3. unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) 

因此,一個(gè)持有 SO_REUSEPORT 特性的完整 Go 服務(wù)代碼如下

  1. package main 
  2.  
  3. import ( 
  4.  "context" 
  5.  "fmt" 
  6.  "net" 
  7.  "net/http" 
  8.  "os" 
  9.  "syscall" 
  10.  
  11.  "golang.org/x/sys/unix" 
  12.  
  13. var lc = net.ListenConfig{ 
  14.  Control: func(network, address string, c syscall.RawConn) error { 
  15.   var opErr error 
  16.   if err := c.Control(func(fd uintptr) { 
  17.    opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) 
  18.   }); err != nil { 
  19.    return err 
  20.   } 
  21.   return opErr 
  22.  }, 
  23.  
  24. func main() { 
  25.  pid := os.Getpid() 
  26.  l, err := lc.Listen(context.Background(), "tcp""127.0.0.1:8000"
  27.  if err != nil { 
  28.   panic(err) 
  29.  } 
  30.  server := &http.Server{} 
  31.  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 
  32.   w.WriteHeader(http.StatusOK) 
  33.   fmt.Fprintf(w, "Client [%s] Received msg from Server PID: [%d] \n", r.RemoteAddr, pid) 
  34.  }) 
  35.  fmt.Printf("Server with PID: [%d] is running \n", pid) 
  36.  _ = server.Serve(l) 

我們將其編譯為 linux 可執(zhí)行文件 main

  1. $ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go 

在 linux 主機(jī)上開啟三個(gè)同時(shí)監(jiān)聽 8000 端口的進(jìn)程,我們可以看到三個(gè)服務(wù)進(jìn)程的 PID 分別是 32687 、32691 和 32697。

  1. ~ $ ./main 
  2. Server with PID: [32687] is running 
  3. ~ $ ./main 
  4. Server with PID: [32691] is running 
  5. ~ $ ./main 
  6. Server with PID: [32697] is running 

最后,通過(guò) curl 命令,模擬多次 http 客戶端請(qǐng)求

  1. ~ $ for i in {1..20}; do curl localhost:8000; done 
  2. Client [127.0.0.1:56876] Received msg from Server PID: [32697] 
  3. Client [127.0.0.1:56880] Received msg from Server PID: [32687] 
  4. Client [127.0.0.1:56884] Received msg from Server PID: [32687] 
  5. Client [127.0.0.1:56888] Received msg from Server PID: [32687] 
  6. Client [127.0.0.1:56892] Received msg from Server PID: [32691] 
  7. Client [127.0.0.1:56896] Received msg from Server PID: [32697] 
  8. Client [127.0.0.1:56900] Received msg from Server PID: [32691] 
  9. Client [127.0.0.1:56904] Received msg from Server PID: [32691] 
  10. Client [127.0.0.1:56908] Received msg from Server PID: [32697] 
  11. Client [127.0.0.1:56912] Received msg from Server PID: [32697] 
  12. Client [127.0.0.1:56916] Received msg from Server PID: [32687] 
  13. Client [127.0.0.1:56920] Received msg from Server PID: [32691] 
  14. Client [127.0.0.1:56924] Received msg from Server PID: [32697] 
  15. Client [127.0.0.1:56928] Received msg from Server PID: [32697] 
  16. Client [127.0.0.1:56932] Received msg from Server PID: [32691] 
  17. Client [127.0.0.1:56936] Received msg from Server PID: [32697] 
  18. Client [127.0.0.1:56940] Received msg from Server PID: [32687] 
  19. Client [127.0.0.1:56944] Received msg from Server PID: [32691] 
  20. Client [127.0.0.1:56948] Received msg from Server PID: [32687] 
  21. Client [127.0.0.1:56952] Received msg from Server PID: [32697] 

可以看到,20 個(gè)客戶端請(qǐng)求被均衡地打到了三個(gè)服務(wù)進(jìn)程上。

總結(jié)

linux 內(nèi)核自 3.9 提供的 SO_REUSEPORT 選項(xiàng),可以讓多進(jìn)程監(jiān)聽同一個(gè)端口。

這種機(jī)制帶來(lái)了什么:

提高服務(wù)器程序的吞吐性能:我們可以運(yùn)行多個(gè)應(yīng)用程序?qū)嵗?,充分利用多?CPU 資源,避免出現(xiàn)單核在處理數(shù)據(jù)包,其他核卻閑著的問(wèn)題。

內(nèi)核級(jí)負(fù)載均衡:我們不需要在多個(gè)實(shí)例前面添加一層服務(wù)代理,因?yàn)閮?nèi)核已經(jīng)提供了簡(jiǎn)單的負(fù)載均衡。

不停服更新:當(dāng)我們需要更新服務(wù)時(shí),可以啟動(dòng)新的服務(wù)實(shí)例來(lái)接受請(qǐng)求,再優(yōu)雅地關(guān)閉掉舊服務(wù)實(shí)例。

如果你們的 Go 項(xiàng)目,一到高峰期就有請(qǐng)求堆積問(wèn)題,這個(gè)時(shí)候就可以考慮采用 SO_REUSEPORT 選項(xiàng)。

參考鏈接:

【1. How do SO_REUSEADDR and SO_REUSEPORT differ?】https://stackoverflow.com/questions/14388706/how-do-so-reuseaddr-and-so-reuseport-differ

【2. SO_REUSEPORT 性能測(cè)試】 http://www.blogjava.net/yongboy/archive/2015/02/12/422893.html

 

【3. linux socket man-page】https://man7.org/linux/man-pages/man7/socket.7.html

 

責(zé)任編輯:武曉燕 來(lái)源: Golang技術(shù)分享
相關(guān)推薦

2019-04-10 13:43:19

Linux內(nèi)核進(jìn)程負(fù)載

2024-07-17 08:36:53

2010-05-05 21:39:29

linux負(fù)載均衡

2015-05-29 14:01:00

網(wǎng)絡(luò)優(yōu)化網(wǎng)絡(luò)性能

2017-07-03 08:08:25

負(fù)載均衡分類

2009-07-22 10:25:37

2009-10-23 19:11:32

linux集群

2022-12-27 07:42:12

2021-04-22 07:47:46

Linux進(jìn)程管理

2010-04-27 12:29:08

Linux負(fù)載均衡

2010-05-06 12:18:34

IP負(fù)載均衡

2013-02-20 09:59:49

負(fù)載均衡PuppetNginx

2022-12-20 08:01:20

全棧云負(fù)載均衡傳統(tǒng)硬件

2010-05-10 17:52:30

實(shí)現(xiàn)負(fù)載均衡

2021-05-17 18:28:36

Linux CFS負(fù)載均衡

2019-09-10 09:58:19

Dubbo負(fù)載均衡Hash

2011-12-02 22:51:46

Nginx負(fù)載均衡

2018-11-07 10:12:37

2021-04-21 14:56:28

負(fù)載均衡高并發(fā)優(yōu)化技術(shù)架構(gòu)

2014-10-09 14:35:44

HAProxy負(fù)載均衡
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)