rceax.com Open in urlscan Pro
172.67.199.14  Public Scan

URL: https://rceax.com/
Submission: On December 11 via api from US — Scanned from NZ

Form analysis 0 forms found in the DOM

Text Content

編程沉思錄


 * 首頁
 * Golang源碼剖析
 * 分類11
 * 歸檔37
 * 標簽9
 * 開源
 * 關於
 * 內推
 * 搜索


 * 文章目錄
 * 站點概覽

cyhone

我們采集的隻是石頭,卻必須始終展望著未來的大教堂


掃碼關注公眾號【編程沉思錄】
第一時間收到文章推送


CONTEXT的錯誤使用引發PANIC的問題複盤

發表於 2024-05-06 更新於 2024-05-07 分類於 Golang 閱讀次數:

我們有這麽一段業務代碼,在 Gin 的 API Handler 中,開了一個子 goroutine 寫 DB,代碼大概是這樣:

package main

import (
    "github.com/gin-gonic/gin"
    "gorm.io/gorm"
)

var db *gorm.DB

func ServerHandler(c *gin.Context) {
    // 一些旁路邏輯,為了不影響接口耗時,在子goroutine中執行
    go func() {
       db.WithContext(c).Exec("update xxx")
    }()
    // 一些後置邏輯
}

代碼在測試階段一直沒啥問題,但是一上線立馬出現了大麵積的 panic。panic 的棧也非常奇怪,掛在了 mysql driver 裏麵:

panic: sync/atomic: store of nil value into Value
goroutine 357413 [running]:
sync/atomic.(*Value).Store(0xc004097ef0, {0x0,0x0})

/usr/local/go/src/sync/atomic/value.go:47 +0xeb
github.com/go-sql-driver/mysql.(*atomicError).Set(..)
/root/go/pkg/mod/github.com/go-sql-driver/mysql@v1.6.0/utils.go:831
github.com/go-sql-driver/mysql.(*mysqlConn).cancel(0xc004e6fc20, {0x0, 0x0})
/root/go/pkg/mod/github.com/go-sql-driver/mysql@v1.6.0/connection.go:435 +0x3d
github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1()
/root/go/pkg/mod/github.com/go-sql-driver/mysql@v1.6.0/connection.go:622 +0x192
created by github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher
/root/go/pkg/mod/github.com/go-sql-driver/mysql@v1.6.0/connection.go:611 +0x105

閱讀全文 »



GO 1.22 可能將改變 FOR 循環變量的語義

發表於 2023-11-29 分類於 Golang 閱讀次數:

幾乎世界上每個 Golang 程序員都踩過一遍 for 循環變量的坑,而這個坑的解決方案已經作為實驗特性加入到了 Go 1.21 中,並且有望在 Go 1.22
中完全開放。
舉個例子,有這麽段代碼:

var ids []*int
for i := 0; i < 10; i++ {
	ids = append(ids, &i)
}

for _, item := range ids {
	println(*item)
}

可以試著在 playgound 裏麵運行下:go.dev/play/p/O8MVGtueGAf

答案是:打印出來的全是 10。

這個結果實在離譜。原因是因為在目前 Go 的設計中,for 中循環變量的定義是 per loop 而非 per iteration。也就是整個 for
循環期間,變量 i 隻會有一個。以上代碼等價於:

var ids []*int
var i int
for i = 0; i < 10; i++ {
	ids = append(ids, &i)
}

同樣的問題在閉包使用循環變量時也存在,代碼如下:

閱讀全文 »



剖析GOLANG BIGCACHE的極致性能優化

發表於 2023-11-25 更新於 2023-11-28 分類於 Golang 閱讀次數:

本文屬於 《Golang源碼剖析係列》

Bigcache是用Golang實現的本地內存緩存的開源庫,主打的就是可緩存數據量大,查詢速度快。 在其官方的介紹文章《Writing a very fast
cache service with millions of entries in Go》一文中,明確提出了bigcache的設計目標:

 1. 多: 緩存的元素數量非常大,可以達到百萬級或千萬級。
 2. 快: 對延遲有非常高的要求,平均延遲要求在5毫秒以內。redis、memcached之類的就不考慮在內了,畢竟用Redis還要多走一遍網絡IO。
 3. 穩: 99.9分位延遲應在10毫秒左右,99.999分位延遲應在400毫秒左右。

目前有許多開源的cache庫,大部分都是基於map實現的,例如go-cache,ttl-cache等。bigcache明確指出,當數據量巨大時,直接基於map實現的cache庫將出現嚴重的性能問題,這也是他們設計了一個全新的cache庫的原因。

本文將通過分析bigcache v3.1.0的源碼,揭秘bigcache如何解決現有map庫的性能缺陷,以極致的性能優化,實現超高性能的緩存庫。

閱讀全文 »



解讀 GOLANG 標準庫裏的 VARINT 實現

發表於 2023-11-23 更新於 2023-11-30 分類於 Golang 閱讀次數:

本文屬於《Golang 源碼剖析係列》。

最近發現 Golang 標準庫竟然自帶了 varint 的實現,代碼位置在
encoding/binary/varint.go,這個跟protobuf裏麵的varint實現基本是一致的。剛好借助 golang 標準庫的 varint
源碼,我們來係統地學習和梳理下 varint。

熟悉 protobuf 的人肯定對 varint 不陌生,protobuf 裏麵除了帶 fix (如 fixed32、fixed64) 之外的整數類型, 都是
varint 編碼。

varint 的出現主要是為了解決兩個問題:

 1. 空間效率:以 uint64 類型為例,可以表示的最大值為 18446744073709551615。然而在實際業務場景中,我們通常處理的整數值遠小於
    uint64 的最大值。假設在我們的業務中,需要處理的整數值僅為 1,但在網絡傳輸過程中,我們卻需要使用 8
    個字節來表示這個值。這就導致了大量的空間浪費,因為大部分字節並沒有實際存儲有效的信息。varint
    編碼通過使用可變長度的字節序列來表示整數,使得小的整數可以用更少的字節表示,提高空間效率。
 2. 兼容性:varint 使得我們可以在不改變編碼 /
    解碼邏輯的情況下,處理不同大小的整數。這意味著我們可以在不破壞向後兼容性的情況下,將一個字段從較小的整數類型(如 uint32)升級到較大的整數類型(如
    uint64)

本文將通過分析 Golang 標準庫自帶的 varint 源碼實現,介紹 varint 的設計原理以及Golang標準庫是如何解決 varint
在編碼負數時遇到的問題。

閱讀全文 »



深度分析 GOLANG SYNC.POOL 底層原理

發表於 2021-07-18 更新於 2024-06-07 分類於 Golang 閱讀次數:

本文屬於《Golang 源碼剖析係列》

sync.Pool 是 Golang 內置的對象池技術,可用於緩存臨時對象,以緩解因頻繁建立臨時對象帶來的性能損耗以及對 GC 帶來的壓力。

在許多知名的開源庫中都可以看到 sync.Pool 的大量使用。例如,HTTP 框架 Gin 用 sync.Pool 來複用每個請求都會創建的
gin.Context 對象。 在 grpc-Go、kubernetes 等也都可以看到對 sync.Pool 的身影。

但需要注意的是,sync.Pool 緩存的對象隨時可能被無通知的清除,因此不能將 sync.Pool 用於存儲持久對象的場景。

sync.Pool 作為 goroutine 內置的官方庫,其設計非常精妙。sync.Pool 不僅是並發安全的,而且實現了 lock
free,裏麵有許多值得學習的知識點。

本文將基於 go-1.16 的源碼 對 sync.Pool 的底層實現一探究竟。

閱讀全文 »



OS.CHMOD 時到底用 777 還是 0777?

發表於 2021-06-20 分類於 Golang 閱讀次數:

問題是這樣的:我在代碼裏麵調用了 os.Chmod("test.txt", 777),希望把該文件的讀寫及執行權限對所有用戶開放。
執行完代碼,順手 ls 看了下。如下:

$ ls -l test.txt
-r----x--x  1 cyhone  1085706827  0 Jun 20 13:27 test.txt

結果出乎意料,不僅文件權限沒有按預期的變成 rwxrwxrwx。反而執行完後,當前用戶就隻剩可讀權限了,其他用戶就隻有可執行權限同時無讀寫權限。

因為這實在是一個簡單又愚蠢的錯誤,所以先直接給出結論:

 1. 在 C 語言和 Go 語言中,如果想要將文件權限形式修改為 rwxrwxrwx,需要寫成 0777,而非 777。
 2. 0777 是八進製格式,777 是十進製格式。在用 Go 語言表示此類權限的時候,如果要對標 chmod 命令的表示形式,用八進製表示更方便和準確點。
 3. 如果不是在代碼裏,而是在命令行直接調 chmod 的話,那 0777 和 777 都可以。

這個問題雖然非常簡單,但尷尬的是我還踩了坑,所以把這個問題及原因分享出來。

閱讀全文 »

12…7
豫ICP備18011028號
© 2015 – 2024 cyhone
107 107
由 Hexo & NexT.Gemini 強力驅動


Theme NexT works best with JavaScript enabled