日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

以下文章來源于非常程序員 ,作者yoko

編寫健壯且高性能的網絡服務需要付出大量的努力。提高服務性能的方式有很多種,比如優化應用層的代碼,更進一步,還可以看看垃圾回收器,操作系統,網絡傳輸,以及部署我們服務的硬件是否有優化空間。

TCP/IP 協議棧中的一些算法會影響到服務性能。本文將簡單介紹其中的 Nagle 算法,與 Nagle 算法相關的 socket 選項TCP_NODELAY,以及在 Go 語言中如何使用它。

理論

大部分平臺上的 TCP 實現都提供了 socket 選項,用于控制連接生命周期,流量控制等算法。

其中一個會對網絡傳輸性能造成影響的算法是 Nagle 算法,它在 linux,macOS,windows 平臺默認都是打開的。

Nagle 算法的做法是:將要發送的小包合并,并延緩發送。延緩后的發送策略是,收到前一個發送出去的包的 ACK 確認包,或者一定時間后,收集了足夠數量的小數據包。

Nagle 算法的目的是減少發送小包的數量,從而減小帶寬,并提高網絡吞吐量,付出的代價是有時會增加服務的延時。(譯者 yoko 注:補充解釋一下為什么減少小包的數量可以減小帶寬。因為每個 TCP 包,除了包體中包含的應用層數據外,外層還要套上 TCP 包頭和 IP 包頭。由于應用層要發送的業務數據量是固定的,所以包數量越多,包頭占用的帶寬也越多)

引入的延時通常在毫秒級別,但是對于延遲敏感的服務來說,減少一些毫秒數的延遲也是值得的。

Nagle 算法所對應的 TCP socket 選項是TCP_NODELAY。開啟TCP_NODELAY可以禁用 Nagle 算法。禁用 Nagle 算法后,數據將盡可能快的被發送出去。

另外,我們也可以在應用層對數據進行緩存合并發送來達到 Nagle 算法的目的(譯者 yoko 注:在 Go 語言中即使用bufio.Writer。個人認為,使用bufio.Writer還有一個好處,就是減少了調用 write 系統調用的次數,但是相應的,增加了數據拷貝的開銷)。

在 Go 語言中,TCP_NODELAY默認是開啟的,并且標準庫提供了net.SetNodelay(bool)方法來控制它。

實驗

我們通過一個小實驗來觀察TCP_NODELAY打開和關閉時底層 TCP 包的變化。

代碼邏輯十分簡單,client 端連續調用 5 次conn.Write函數向 server 端發送相同的字符串GOPHER。

服務端代碼(server.go):

package mainimport (    "bufio"    "fmt"    "log"    "net"    "strings")func main() {    port := ":" + "8000"    // 創建監聽    l, err := net.Listen("tcp", port)    if err != nil {        log.Fatal(err)    }    defer l.Close()    for {        // 接收新的連接        c, err := l.Accept()        if err != nil {            log.Println(err)            return        }        // 處理新的連接        go handleConnection(c)    }}func handleConnection(c net.Conn) {    fmt.Printf("Serving %sn", c.RemoteAddr().String())    for {        // 讀取數據        netData, err := bufio.NewReader(c).ReadString('n')        if err != nil {            log.Println(err)            return        }        cdata := strings.TrimSpace(netData)        if cdata == "GOPHER" {            c.Write([]byte("GopherAcademy Advent 2019!"))        }        if cdata == "EXIT" {            break        }    }    c.Close()}

客戶端代碼(client.go):

package mainimport (    "fmt"    "log"    "net")func main() {    target := "localhost:8000"    raddr, err := net.ResolveTCPAddr("tcp", target)    if err != nil {        log.Fatal(err)    }    // 和服務端建立連接    conn, err := net.DialTCP("tcp", nil, raddr)    if err != nil {        log.Fatal(err)    }    // conn.SetNoDelay(false) // 如果打開這行代碼,則禁用TCP_NODELAY,打開Nagle算法    fmt.Println("Sending Gophers down the pipe...")    for i := 0; i < 5; i++ {        // 發送數據        _, err = conn.Write([]byte("GOPHERn"))        if err != nil {            log.Fatal(err)        }    }}

為了觀察 TCP 包,首先開啟抓包程序 tcpdump。為了簡單,兩個程序都在本機運行。我環境的內網環路網卡為lo0,不同機器上可能不同:

$sudo tcpdump -X  -i lo0 'port 8000'

然后,再打開兩個終端窗口,先執行服務端程序,再執行客戶端程序:

$go run server.go
$go run client.go

觀察抓包結果,我們會發現每次調用 write 函數發送"GOPHER",對應的都是一個獨立的 TCP 包被發送??偣灿?5 個 TCP 包。以下是抓包結果,為了簡單,我只貼出兩個包:

....14:03:11.057782 IP localhost.58030 > localhost.irdmi: Flags [P.], seq 15:22, ack 1, win 6379, options [nop,nop,TS val 744132314 ecr 744132314], length 7        0x0000:  4500 003b 0000 4000 4006 0000 7f00 0001  E..;..@.@.......        0x0010:  7f00 0001 e2ae 1f40 80c5 9759 6171 9822  [email protected]."        0x0020:  8018 18eb fe2f 0000 0101 080a 2c5a 8eda  ...../......,Z..        0x0030:  2c5a 8eda 474f 5048 4552 0a              ,Z..GOPHER.14:03:11.057787 IP localhost.58030 > localhost.irdmi: Flags [P.], seq 22:29, ack 1, win 6379, options [nop,nop,TS val 744132314 ecr 744132314], length 7        0x0000:  4500 003b 0000 4000 4006 0000 7f00 0001  E..;..@.@.......        0x0010:  7f00 0001 e2ae 1f40 80c5 9760 6171 9822  .......@...`aq."        0x0020:  8018 18eb fe2f 0000 0101 080a 2c5a 8eda  ...../......,Z..        0x0030:  2c5a 8eda 474f 5048 4552 0a              ,Z..GOPHER....

如果我們打開客戶端中被注釋掉的conn.SetNoDelay(false)這行代碼,也即禁用掉TCP_NODELAY,開啟 Nagle 算法,再次抓包,結果如下:

14:27:20.120673 IP localhost.64086 > localhost.irdmi: Flags [P.], seq 8:36, ack 1, win 6379, options [nop,nop,TS val 745574362 ecr 745574362], length 28        0x0000:  4500 0050 0000 4000 4006 0000 7f00 0001  E..P..@.@.......        0x0010:  7f00 0001 fa56 1f40 07c9 d46f a115 3444  [email protected]        0x0020:  8018 18eb fe44 0000 0101 080a 2c70 8fda  .....D......,p..        0x0030:  2c70 8fda 474f 5048 4552 0a47 4f50 4845  ,p..GOPHER.GOPHE        0x0040:  520a 474f 5048 4552 0a47 4f50 4845 520a  R.GOPHER.GOPHER.

可以看到,有四個"GOPHER"被合并到了一個 TCP 包中。

結論

TCP_NODELAY并不是萬能的,有好處有壞處,需要根據實際業務場景決定打開還是關閉。但是,在使用具體語言編寫網絡服務時,我們需要知道它是否被默認開啟。

還有其他一些類似的 socket 選項,比如TCP_QUICKACK和TCP_CORK等。但是由于有些 socket 選項是平臺相關的,因此 Go 沒有提供和TCP_NODELAY相同的方式來控制這些 socket 選項。我們可以通過一些平臺相關的包來實現這一點。比如說,在類 unix 系統下,我們可以使用golang.org/x/sys/unix包中的SetsockoptInt方法。

舉例:

err = unix.SetsockoptInt(fd, unix.IPPROTO_TCP, unix.TCP_QUICKACK, 1)if err != nil {  return os.NewSyscallError("setsockopt", err)}

最后,如果你想學習更多和 Nagle 算法相關的知識,可以看看這篇英文博客[1]

英文原文鏈接:Control packet flow with TCP_NODELAY in Go[2]

參考資料

[1]

英文博客: https://www.extrahop.com/company/blog/2016/tcp-nodelay-nagle-quickack-best-practices/

[2]

Control packet flow with TCP_NODELAY in Go: https://blog.gopheracademy.com/advent-2019/control-packetflow-tcp-nodelay/

分享到:
標簽:語言
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定