記錄最近遇到的幾個問題
Gin 中間件沒有使用next會是什么反應?
周末老王提了一個問題,如果Gin中間件里面如果我忘記寫context.Next了會有什么結果呢?
我第一個反應是直接不會執行后面的handler了唄。我印象中gin的middleware也是個handler,然后維護一個handler鏈條,使用next進行調用傳遞。
事實證明我錯了,如果某個middleware里面忘記寫c.Next(), 那么它還是會進行后續調用的。只是不會再回到這個middleware中了。
這塊代碼又加深了一些理解:
func (c *Context) Next() {
c.index++
for s := int8(len(c.handlers)); c.index < s; c.index++ {
c.handlers[c.index](c)
}
}
每個請求進來的時候,都已經創建了c.handlers數組,當第一個Next函數啟動的時候,會進入到這里的for循環,在這個循環中,默認就是會調用所有的handler的。所以這里就回答了之前的問題,如果沒有寫next的話,就順勢進入到下一個排序的handler。
如果調用了Next的話呢,實際上就不會調度for循環里面的c.index++了,就進入了第一行的c.index++,並且調用下一個handler,由下一個handler里面的next進入第一行的c.index++。
這個設計確實有點反直覺。
但是看了這個帖子 https://github.com/gin-gonic/gin/issues/287 也就說了,c.Next其實不是必要的,它的必要性就是為了能執行Next函數后面的代碼而已。
如何給Gorm 每個sql請求日志增加一個上下文的traceId
我的需求是一個請求用一個traceId進行串下來,不管是sql日志,還是請求日志。
這個想了老久了,最后結論:做不到
gorm是啟動的時候就創建連接,然后每個請求進來的時候,去連接池獲取連接,進行請求。它的logger接口里面沒有帶上context,導致上下文丟失。
關鍵的代碼在jinzhu/gorm/logger.go
type LogWriter interface {
Println(v ...interface{})
}
// Logger default logger
type Logger struct {
LogWriter
}
// Print format & print log
func (logger Logger) Print(values ...interface{}) {
logger.Println(LogFormatter(values...)...)
}
這里的LogWriter並沒有使用上上下文。這個可能也只是由於gorm創建的時候還沒有到go1.7。貌似又很多人也發現這個問題,希望gorm加上context,https://github.com/jinzhu/gorm/issues/1231 但是至少現在還未加上去。
后來腦洞了一下,其實還有可能有一種做法,https://github.com/huandu/go-tls 像這種把context存儲在goroutine作用域存儲里面,然后創建一個自定義的Logger,在Print的時候,去這個goroutine作用域存儲里面獲取context。
但是這個創建goroutine作用域存儲本身就是golang官網不提倡的。於是便作罷。。。
當然還有另外一種辦法,就是你自己在每次sql請求之后自己使用logger記錄一下sql請求和結果。不過過於丑陋了。
