golang 安全的tcp server_Go 語言使用 TCP_NODELAY 控制發包流量

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

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 main

import (
"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 %s\n", 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 main

import (
"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("GOPHER\n"))
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 .......@...Yaq."
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 .....V.@...o..4D
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_QUICKACKTCP_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/

推薦閱讀

  • 架構系列:高并發架構的CDN知識介紹

  • Go語言中如何開啟 TCP keepalive?


喜歡本文的朋友,歡迎關注“Go語言中文網”:

7877f69240927a55c2177fa84b23a08e.png

Go語言中文網啟用微信學習交流群,歡迎加微信:274768166

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/534671.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/534671.shtml
英文地址,請注明出處:http://en.pswp.cn/news/534671.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

react取消所有請求_react 組件關閉后怎么消除還在進行中的ajax

把你的請求做成可以取消的&#xff0c; 這里的取消不是取消發送。 因為請求已經發送了&#xff0c;沒有辦法終止的。所謂的取消其實就是取消回調函數&#xff0c; react官方給出了一種最佳實踐。const makeCancelable (promise) > { let hasCanceled_ false; const wrap…

css 漣漪,CSS3水波漣漪動畫定位樣式如何制作

CSS3水波漣漪動畫定位樣式如何制作寶劍鋒從磨礪出&#xff0c;梅花香自苦寒來。以下是小編為大家搜索整理的CSS3水波漣漪動畫定位樣式如何制作&#xff0c;希望能給大家帶來幫助!更多精彩內容請及時關注我們應屆畢業生考試網!先上效果圖:教程本動畫需要用到的主要屬性:animatio…

python基礎實訓_python基礎實踐(三)

-*-列表是新手可直接使用的最強大的python功能之一&#xff0c;它融合了眾多重要的編程概念。-*-# -*- coding:utf-8 -*-# Author:sweeping-monkQuestion_1 "什么是列表&#xff1f;"print(Question_1)smg "列表由一系列按特定順序排列的元素組成。你可以創建…

python axis 0 1_python pandas 中axis值0 1怎么分行還是列

axis的重點在于方向&#xff0c;而不是行和列。1表示橫軸&#xff0c;方向從左到右&#xff1b;0表示縱軸&#xff0c;方向從上到下。即axis1為橫向&#xff0c;axis0為縱向&#xff0c;而不是行和列&#xff0c;具體到各種用法而言也是如此。當axis1時&#xff0c;如果是求平均…

ajax php接收不到數據庫,PHP更新MySQL數據庫與AJAX調用沒有做任何事情

我已經測試過&#xff0c;發現正確的數據被發送&#xff0c;但PHP更新數據庫中的字段即處理更新無法正常工作。發生的一切就是我在條件中得到了else響應。我需要根據用戶輸入是什么來更新數據庫。就像我說的&#xff0c;我得到的回應是else回應。$youruname $_POST[youruname]…

就業技術書文件表格_公路工程全套資料—開工施工檢驗等表格范本,及監理內業常用資料...

關鍵詞&#xff1a;開工報告、 路基、排水、小橋、涵洞、水泥、瀝青、混凝土、施工檢驗、監理、資料整理、基層&#xff0c;內業資料&#xff0c;監理資料&#xff0c;施工試驗報告等。公路工程在管理、監理、施工過程中需及時、準確、完整地收集整理項目建設中各種檔案資料&am…

龍神契約為什么顯示服務器錯誤,龍神契約連服BOSS玩法介紹

龍神契約游戲中連服戰場的游戲中的一個非常重要的部分&#xff0c;而其中的boss尤為重要。擊殺boss玩家可以獲得非常多的獎勵&#xff0c;一般一下稀有材料都是可以獲得的&#xff0c;所以打boss是重中之重的。今天小編簡單給大家介紹一下。有興趣的小伙伴千萬不要錯過。和小編…

python批量新建文件_python批量處理

python opencv圖像二值化批量處理from skimage import data_dir,io,transform,color,filtersimport numpy as npimport cv2def convert_gray(f):rgbio.imread(f) #依次讀取rgb圖片#grayfilters.gaussian(rgb, sigma1, outputNone, modenearest, cval0, multichannelNone, prese…

dns電腦服務器發生故障怎么修復,電腦dns服務器發生故障怎么解決

一、DNS簡介&#xff1a;DNS(Domain Name System&#xff0c;域名系統)&#xff0c;因特網上作為域名和IP地址相互映射的一個分布式數據庫&#xff0c;能夠使用戶更方便的訪問互聯網&#xff0c;而不用去記住能夠被機器直接讀取的IP數串。通過主機名&#xff0c;最終得到該主機…

button設置disabled屬性不生效_jQuery屬性節點

發現了很好玩的表情可以插入嘻嘻嘻嘻嘻嘻嘻 1.attr屬性操作 在jQuery中&#xff0c;可以通過attr()方法操作屬性&#xff0c;可以是固有屬性&#xff0c;也可以是自定義屬性。1.1 設置屬性值// 設置單個屬性 $("div").attr("title", "我是一個div&quo…

server_u文件服務器已停止,Serv-U停止服務怎么解決

近期很多朋友在使用Serv-U來架設FTP服務器時候&#xff0c;總會出現自動停止服務的現象&#xff0c;一旦停止就不能保證工作的正常運作&#xff0c;那么Serv-U停止服務怎么解決&#xff0c;下面跟隨愛站技術頻道小編來看看吧&#xff01;因為這個ftp服務很重要&#xff0c;要保…

如何知道電腦服務器操作系統,電腦如何查看服務器操作系統

電腦如何查看服務器操作系統 內容精選換一換北京時間1月3日&#xff0c;Intel處理器芯片被曝出存在嚴重的Meltdown和Spectre安全漏洞&#xff0c;漏洞詳情如下&#xff1a;漏洞名稱&#xff1a;Intel處理器存在嚴重芯片級漏洞漏洞編號&#xff1a;CVE-2017-5753、CVE-2017-5715…

哈哈機器人送到冰雪小鎮_從小鎮到上海:兩代人的接力洄游 | 活動回顧

10月24日&#xff0c;在上海思南公館&#xff0c;作家路明圍繞著自己的新書《出小鎮記》&#xff0c;和蘇更生、景蠻蠻一起&#xff0c;向現場觀眾講述了小鎮和上海的故事。路明&#xff1a;我媽媽是69屆的初中生&#xff0c;他們69屆那些人走的時候&#xff0c;69屆是一片紅&a…

云服務器php版本修改,云服務器 更改php版本

云服務器 更改php版本 內容精選換一換本節操作介紹在管理控制臺創建啟動模板的操作步驟。每個賬號在每個區域最多可創建30個啟動模板。創建啟動模板時&#xff0c;所有配置項均為可選。但如果缺失了創建實例的必要參數&#xff0c;例如規格、鏡像類型&#xff0c;那么在使用該模…

opencv獲得圖片的像素寬度_使用OpenCV實現攝像頭測距

原文鏈接&#xff1a;Find distance from camera to object using Python and OpenCV?www.pyimagesearch.com攝像頭測距就是計算照片中的目標物體到相機的距離。可以使用相似三角形&#xff08;triangle similarity&#xff09;方法實現&#xff0c;或者使用更復雜但更準確的相…

ios下js復制到粘貼板_EXCEL被你忽視的粘貼板-11

這一節我們來聊聊粘貼板的用途&#xff0c;什么?你居然沒用過粘貼板&#xff1f;平時只是ctrlc&#xff0c;然后ctrlv&#xff0c;好吧&#xff0c;那我們看看粘貼板到底能干些啥。1、提取區域內的內容有時我們選擇一塊區域發給同事&#xff0c;但當他需要編輯時發現居然是圖片…

機器人 林州重機_林州重機募資11億布局油氣和機器人項目

OFweek工控網訊&#xff1a;林州重機7月31日晚間公布非公開發行股票預案&#xff0c;公司計劃向包括公司控股股東郭現生、股東、原董事宋全啟在內的不超過10名的特定對象非公開發行股票數量為不超過15000萬股&#xff0c;發行價格不低于7.42元/股&#xff0c;募集資金總額不超過…

ros構建機器人運動學模型_ROS入門學習之八機器人綜合應用

1.ROS機器人實例介紹(PR2,Turtlebot,HRMRP,Kungfu Arm)1).PR2:造就了ROS的機器人平臺&#xff0c;完全基于ROS開發&#xff0c;功能豐富、強大2).Turtlebot:ROS社區中最流行的高性價比機器人平臺&#xff0c;前后工發布三代3).Universal Robot:工業領域的協作機器人定義者4).HR…

微人事項目實戰的數據庫腳本_EMP微前端實戰之cocos2d線上項目

團隊原文&#xff1a;efoxTeam/emp?github.com一.背景目前cocos2d游戲最主要的開發方式是通過官方提供的GUI圖形界面工具——creator&#xff0c;通過 creator 開發者無需關注構建本身&#xff0c;只需通過界面操作即可對游戲代碼進行構建打包。但是這樣也存在著以下幾個問題&…

線粒體和葉綠體的基因組特點_如何組裝植物葉綠體基因組

可能出現的問題&#xff1a;*個人電腦上遇到不能collect memery的情況&#xff0c;是電腦內存較少&#xff0c;建議分成用2G左右的數據進行組裝。* Seed.fasta #用于起始組裝的種子序列&#xff0c;NOVOPlasty安裝軟件目錄下有這個文件&#xff0c;就叫這個名字&#xff0c;作者…