本文目錄一覽:
- 1、【golang】內存逃逸常見情況和避免方式
- 2、golang net/http包 http請求的位元組碼讀取與解析。
- 3、【golang】高並發下TCP常見問題解決方案
- 4、golang中怎麼處理socket長連接
- 5、Go 實現 TLS 雙向認證
- 6、golang socket通信 怎麼把一個客戶端發送的數據推送給其他客戶端
【golang】內存逃逸常見情況和避免方式
因為如果變數的內存發生逃逸,它的生命周期就是不可知的,其會被分配到堆上,而堆上分配內存不能像棧一樣會自動釋放,為了解放程序員雙手,專註於業務的實現,go實現了gc垃圾回收機制,但gc會影響程序運行性能,所以要盡量減少程序的gc操作。
1、在方法內把局部變數指針返回,被外部引用,其生命周期大於棧,則溢出。
2、發送指針或帶有指針的值到channel,因為編譯時候無法知道那個goroutine會在channel接受數據,編譯器無法知道什麼時候釋放。
3、在一個切片上存儲指針或帶指針的值。比如[]*string,導致切片內容逃逸,其引用值一直在堆上。
4、因為切片的append導致超出容量,切片重新分配地址,切片背後的存儲基於運行時的數據進行擴充,就會在堆上分配。
5、在interface類型上調用方法,在Interface調用方法是動態調度的,只有在運行時才知道。
1、go語言的介面類型方法調用是動態,因此不能在編譯階段確定,所有類型結構轉換成介面的過程會涉及到內存逃逸發生,在頻次訪問較高的函數盡量調用介面。
2、不要盲目使用變數指針作為參數,雖然減少了複製,但變數逃逸的開銷更大。
3、預先設定好slice長度,避免頻繁超出容量,重新分配。
golang net/http包 http請求的位元組碼讀取與解析。
先配置Header最長讀取時間、req最長讀取時間、req最大讀取長度默認6M。
RFC7230禁止\r\n參數,Url中只允許包含英文字母(a-zA-Z)、數字(0-9)、-_.~4個特殊字元以及所有保留字元。但go net/http包放寬了這個要求。
先構建newTextprotoReader,由於緩衝區是對象復用的,用完後要defer put。共完以以下解析任務:
TextprotoReader數據結構,將位元組碼Reader轉成文本Reader。
第一步,從第一行解析出method uri prototype。
第二步解析URL。url.URL數據結構:
解析Scheme,協議前綴(小寫)。有查詢參數?,則配置url.ForceQuery url.RawQuery。有認證信息///…//,則解析url.User url.Host。最後配置url.Path和url.RawPath,如果Path==RawPath,則RawPath=””。
第三步解析MIMEHeader。
第四步readTransfer。重新配置如下參數:RequestMethod ProtoMajor ProtoMinor Header Trailer ContentLength Close。對於Body,如果encodings支持chunked,讀取流用chunkedReader包裹。默認情況用LimitedReader,無body賦空的struct{}。
以下情況返回非空err,示得到正確的請求:
最後配置req.ctx req.RemoteAddr req.TLS body.doEarlyClose = true。
構建Response:
其中closeNotifyCh必須在構建時初始化,沒有content所以先置contentLength為-1。
配置w.cw並被w.w包裹。w.cw緩衝默認大小2M。
獲取Request可能出現如下錯誤:
先上響應數據結構:
response欄位可以分類為:大對象、緩衝、KV對或bool型的狀態參數。
大對象有:
狀態欄位:
chunkWriter數據結構:
chunkWriter包裹了Response,功能之一是完成Header設置,包括Content-Type Content-Length chunk-header。bufio.Writer是chunkWriter是緩衝包裹。
handler將響應寫入到response.w。
調用w.w.Flush()將w寫入到cw,注意到Flush()操作,如果未刷空緩存並報錯,觸發拷貝操作。報錯不會退回已寫出的數據。
進而調用cw.Write(),根據cw.chunking參數。
putBufioWriter(w.w)清空resp.w緩衝,如果池化放回sync.pool。
根據chunkWriter的定義,w.cw.close()負責cw的結束工作:寫入換行符和resp.trailers數據。
最後刷新TCP緩衝w.conn.bufw.Flush(),完成響應包發送。並正確關閉request。
【golang】高並發下TCP常見問題解決方案
首先,看一下TCP握手簡單描繪過程:
其握手過程原理,就不必說了,有很多詳細文章進行敘述,本文只關注研究重點。
在第三次握手過程中,如果伺服器收到ACK,就會與客戶端建立連接,此時內核會把連接從半連接隊列移除,然後創建新的連接,並將其添加到全連接隊列,等待進程調用。
如果伺服器繁忙,來不及調用連接導致全連接隊列溢出,伺服器就會放棄當前握手連接,發送RST給客戶端,即connection reset by peer。
在linux平台上,客戶端在進行高並發TCP連接處理時,最高並發數量都要受系統對用戶單一進程同時打開文件數量的限制(這是因為系統每個TCP都是SOCKET句柄,每個soker句柄都是一個文件),當打開連接超過限制,就會出現too many open files。
使用下指令查看最大句柄數量:
增加句柄解決方案
golang中怎麼處理socket長連接
實際上需要 3 個 goroutine,一個 read,一個 send,還有一個 handle。
read goroutine 讀,然後寫入 recevice chan。
write goroutine 把 send chan 的東西寫。
handle goroutine 是 conn 的主要處理邏輯,負責把 recevice chan 的東西讀出來 call 業務邏輯。
業務邏輯中要寫數據就直接寫入 send chan。
這樣就可以保證,業務邏輯的讀寫都是在 handle goroutine 上處理,而避免 race 產生。
如果需要定時任務(比如心跳),就在 handle goroutine 上加上一個 timer.C;
如果需要 goroutine 下發任務,在 handle goroutine 增加一個 task chan,hanlde 收到 task 後處理業務;
如果需要輸出結果,那就增加 result chan,業務邏輯把數據輸出即可。
—————————-
還有,如果我開2個goroutine的話,client斷開連接了,假設recv goroutine先發生err並且close(fd),那在send goroutine中該如何處理呢?有可能不應該這樣處理,那應該怎麼處理呢?
如果 net.Conn Close() 了,不論 Read() 阻塞還是 Write() 阻塞都會立即收到 err 返回。
一般來說,Write() 是不可能主動知道連接斷開的,除非是 SetDeadline() 猜測對方斷掉了,指定時間內沒有寫成功就認為是斷開。Read() 是可以主動收到對方發來的斷開(TCP FIN),但也沒辦法知道異常的斷開(當然也可以設置超時)。
無論是誰,是確實收到 FIN 還是 Deadline 猜測斷開,只要 Close() 大家就知道連接斷開了。
handle goroutine 還有一個用處就是:你的程序主動結束的時候,能正確的 close conn,讓對方知道你是真的斷開了,而不用去猜。
Go 實現 TLS 雙向認證
將會在 config 文件夾中生成 ca.key 和 ca.crt 文件
將會在 config 文件夾中生成 server.key 、 server.csr 和 server.crt 文件
簽名方式: SHA-256 ,默認的 SHA-1 簽名演算法安全性不夠高,Go 中會出現警告。
將會在 config 文件夾中生成 client.key 、 client.csr 和 client.crt 文件
簽名方式: SHA-256 ,默認的 SHA-1 簽名演算法安全性不夠高,Go 中會出現警告。
wireshark 截圖如下:
golang socket通信 怎麼把一個客戶端發送的數據推送給其他客戶端
Socket通信的原理還是比較簡單的, 它大致分為以下幾個步驟。 伺服器端的步驟如下。 (1)建立伺服器端的Socket,開始偵聽整個網路中的連接請求。 (2)當檢測到來自客戶端的連接請求時,向客戶端發送收到連接請求的信息,並建立與客戶端之間的…
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/275945.html