最近基于gin嘗試寫了一套內(nèi)部使用的框架(如下)驴一,過程中有一些總結(jié)筆記在此記錄。
1灶壶、打開連接如MySQL肝断,redis要記得close,否則容易內(nèi)存泄漏驰凛;
2胸懈、for循環(huán)的時(shí)候 要注意 最好顯式close。其實(shí)當(dāng)我們通過for循環(huán)迭代數(shù)據(jù)庫的時(shí)候恰响,當(dāng)?shù)阶詈笠粯訑?shù)據(jù)的時(shí)候趣钱,會(huì)出發(fā)一個(gè)io.EOF的信號(hào),引發(fā)一個(gè)錯(cuò)誤胚宦,同時(shí)go會(huì)自動(dòng)調(diào)用rows.Close方法釋放連接首有,然后返回false,此時(shí)循環(huán)將會(huì)結(jié)束退出枢劝。通常你會(huì)正常迭代完數(shù)據(jù)然后退出循環(huán)井联。可是如果并沒有正常的循環(huán)而因其他錯(cuò)誤導(dǎo)致退出了循環(huán)您旁。此時(shí)rows.Next處理結(jié)果集的過程并沒有完成低矮,歸屬于rows的連接不會(huì)被釋放回到連接池。因此十分有必要正確的處理rows.Close事件被冒。如果沒有關(guān)閉rows連接军掂,將導(dǎo)致大量的連接并且不會(huì)被其他函數(shù)重用,就像溢出了一樣昨悼。最終將導(dǎo)致數(shù)據(jù)庫無法使用蝗锥。所以為了避免這種情況的發(fā)生,最好的辦法就是顯示的調(diào)用rows.Close方法率触,確保連接釋放终议,又或者使用defer指令在函數(shù)退出的時(shí)候釋放連接,即使連接已經(jīng)釋放了,rows.Close仍然可以調(diào)用多次穴张,是無害的细燎。rows.Next循環(huán)迭代的時(shí)候,因?yàn)橛|發(fā)了io.EOF而退出循環(huán)皂甘。為了檢查是否是迭代正常退出還是異常退出玻驻,需要檢查rows.Err。
3偿枕、切片可以理解為不定長數(shù)組 璧瞬,map可以理解為 key:value 對(duì)格式;map聲明后不能直接進(jìn)行賦值渐夸,只有初始化后才能進(jìn)行賦值操作嗤锉。
4、gin中件間攔截請(qǐng)求墓塌,總在路由請(qǐng)求之前執(zhí)行,即 http攔截器瘟忱,再到路由邏輯。在注冊(cè)中間件代碼之后的路由才生效中間件規(guī)則苫幢;
5懈玻、全局變量或者 路由之外(main函數(shù))的東西批狱,都會(huì)一直保存值不消失(常駐內(nèi)存)输涕,要特別注意內(nèi)存泄露埂淮。啟動(dòng)后就不會(huì)重新賦值棒拂,而不是每次請(qǐng)求(生命周期) 會(huì)重新賦值伞梯,注意。實(shí)踐證明:不是每次請(qǐng)求都重新清空賦值的帚屉,特別是全局變量谜诫;比如DB全局句柄實(shí)例的close不同請(qǐng)求會(huì)互相影響。還有一個(gè)指針取地址要特別小心共享問題攻旦。
6喻旷、結(jié)構(gòu)體傳地址和傳值的區(qū)別,在于函數(shù)內(nèi)改變形參值牢屋,會(huì)不會(huì)影響外部的實(shí)參且预。定義指針和形參 用*,函數(shù)內(nèi)部則用&? 代表取地址烙无。指針賦值锋谐,這點(diǎn)原理和C指針相通。
7截酷、只有大結(jié)構(gòu)體涮拗,全局變量適合用指針傳值(指針變量其實(shí)就是個(gè)指向變量的地址)。普通對(duì)象指針用多了會(huì)發(fā)生內(nèi)存逃逸,導(dǎo)致gc壓力過大三热。 go官方一些規(guī)范里面鼓择,也是推薦一般情況下使用傳值。
8就漾、真實(shí)內(nèi)存占用量就是物理內(nèi)存占用量減去buffer和cache的占用量呐能。
9、MySQL庫自帶連接池从藤,使用方不需自行實(shí)現(xiàn)催跪。*sql.DB 線程安全,開箱即用夷野,屏弊了底層創(chuàng)建連接的實(shí)現(xiàn)
Open 只是創(chuàng)建類懊蒸,調(diào)用一次即可,使用前需要 Ping 確保連接正常悯搔。
一定要設(shè)置連接池的兩個(gè)參數(shù) MaxIdle, MaxOpen骑丸,否則在極端情況會(huì)把 DB 連接打滿(未加索引,大事務(wù)阻塞)妒貌⊥ㄎ#可選 MaxLifetime,需咨詢 DBA灌曙,一般 DB 默認(rèn)8小時(shí)菊碟,無需設(shè)置,如果很短要視情況而定
事務(wù)會(huì)占用一個(gè)連接在刺,盡可能減小事務(wù)耗時(shí)逆害,打散大事務(wù),否則會(huì)將 DB 連接數(shù)打滿
prepare 會(huì)占用一個(gè)連接蚣驼,每次使用完后魄幕,一定要 close ,否則同樣會(huì)將連接數(shù)打滿
DSN 需要指定時(shí)區(qū)和對(duì)時(shí)間字段的支持颖杏,否則會(huì)出現(xiàn)時(shí)間提前8小時(shí)的問題
Query, Prepare, Exec 無需業(yè)務(wù)層重試纯陨,底層已經(jīng)實(shí)現(xiàn)
使用var != nil判斷實(shí)現(xiàn)的懶漢式單例并不是線程安全的,注意GO的單例有多種實(shí)現(xiàn)方式留储。
10翼抠、init函數(shù)在加載包的時(shí)候也就是編譯服務(wù)啟動(dòng)的時(shí)候就已經(jīng)執(zhí)行了而不是等執(zhí)行HTTP請(qǐng)求的時(shí)候執(zhí)行到路由才執(zhí)行。所有init函數(shù)是接收不到query參數(shù)來初始化的获讳。
11机久、redigo? 連接池pool:TCP 連接保持, 客戶端和服務(wù)端各自都保持不斷開赔嚎,可以重用TCP膘盖。
12胧弛、關(guān)于接口:
接口可以等于任何一個(gè)實(shí)現(xiàn)它的 結(jié)構(gòu)體的實(shí)例,然后調(diào)用實(shí)例 中的 接口方法侠畔。比如定義cache接口结缚,用Redis和M實(shí)例來各自實(shí)現(xiàn)接口方法。比如 redigo包软棺『旖撸空接口= 沒有方法的接口,接口變量可以等于任何東西喘落,任何東西默認(rèn)都實(shí)現(xiàn)了空接口茵宪。已經(jīng)基礎(chǔ)入門。
13瘦棋、閉包就是對(duì) 變量有個(gè)全局的 作用稀火。解決? 變量作用域問題。
14赌朋、go build? 打包命令
go build -o test example/example.go 命令啥意思
go build主要用于編譯代碼
打包命令凰狞,格式:go build [-o output] [-i] [build flags] [packages]
? ? 其中:-o 指定打包后輸出的文件名.exe,文件名后面跟的是需要打包的.go文件
打包方式1:
? ? 切換到項(xiàng)目目錄下沛慢,執(zhí)行打包命令:
? ? cd $GOPATH/src/myfirstgo
? ? go build
? ? 此時(shí)生成的打包文件名為項(xiàng)目目錄名,這里為myfirstgo
? ? 也可以指定輸出包名赡若,示例: go build -o abc
? ? ? ? 或者 go build -o abc main.go
打包方式2:
? ? 在其他目錄執(zhí)行以下命令:
? ? ? ? go build myfirstgo 此時(shí)在當(dāng)前執(zhí)行命令目錄生成myfirstgo可執(zhí)行文件
? ? 指定輸出包名,示例: go build -o xyz myfirstgo 此時(shí)在當(dāng)前執(zhí)行命令目錄生成xyz可執(zhí)行文件
? ? 在非go項(xiàng)目目錄下執(zhí)行”go build 路徑+項(xiàng)目名“命令查找的項(xiàng)目目錄從$GOPATH/src/開始
15团甲、注意if寫法或者遍歷時(shí)逾冬,變量的作用域。
比如如下寫法是編譯不通過的:
func JsonStr2Map(str string)(map[string]interface{},error){
var dat map[string]interface{}
? if err :=json.Unmarshal([]byte(str), &dat);err !=nil {
return nil,err
? }
return dat,err
}
正確寫法:
func JsonStr2Map(str string)(map[string]interface{},error){
var dat map[string]interface{}
? if err :=json.Unmarshal([]byte(str), &dat);err !=nil {
return nil,err
? }else {
return dat,err//注意err作用域
? }
}
16躺苦、json轉(zhuǎn)結(jié)構(gòu)體索引問題
type Email struct {
Email string `json:"email_address"`
}
type EmailsList struct {
IsSchemaConforming bool `json:"isSchemaConforming"`
? SchemaVersion? ? ? int? `json:"schemaVersion"`
? Emails []Email `json:"unknown.0"`
}
jsonStr := `{"isSchemaConforming":true,"schemaVersion":0,"unknown.0":[{"email_address":"test1@uber.com"},{"email_address":"test2@uber.com"}]}`
emails := EmailsList{}
json.Unmarshal([]byte(jsonStr), &emails)
email0? := emails.Emails[0].Email //注意這里emails.Emails["0"].Email?和emails.Emails['0'].Email 都不正確身腻,注意它們的區(qū)別
17、注意if語句代碼塊中變量的作用域?yàn)榫植康?/p>
以下兩種寫法的區(qū)別:
v,ok :=sActivityId.(string)
if !ok {
return nil,errors.New("type err")
}
return v
以下代碼編譯錯(cuò)誤:
if v,ok :=sActivityId.(string);!ok{
return nil,errors.New("type err")
}
return v
18圾另、斷言的語法是接口類型的霸株,其他類型無法使用斷言語法雕沉。數(shù)組值為接口類型的字符串集乔,strconv.ParseInt(v,10,64)? string轉(zhuǎn)int行傳參時(shí)必須先將接口類型斷言為string。
19坡椒、golang中new和make的區(qū)別
make 僅用來分配及初始化類型為 slice扰路、map、chan 的數(shù)據(jù)倔叼。new 可分配任意類型的數(shù)據(jù)(包括結(jié)構(gòu)體).
new 分配返回的是指針汗唱,即類型 *Type。make 返回引用丈攒,即 Type.
new 分配的空間被清零, make 分配空間后哩罪,會(huì)進(jìn)行初始化.
總結(jié)
new 的作用是初始化一個(gè)指向類型的指針(*T)授霸,make 的作用是為 slice,map 或 chan 初始化并返回引用(T)际插。
func new(Type) *Type? ? 自己實(shí)現(xiàn)一個(gè)類似 new 的功能:
func newInt() *int {?
var i int?
return &i?
}
someInt := newInt()
我們這個(gè)函數(shù)的功能跟 someInt := new(int) 一模一樣碘耳。所以在我們自己定義 new 開頭的函數(shù)時(shí),出于約定也應(yīng)該返回類型的指針框弛。
func make(Type, size... IntegerType) Type? ? ?內(nèi)建函數(shù) make 用來為 slice辛辨,map 或 chan 類型分配內(nèi)存和初始化一個(gè)對(duì)象(注意:只能用在這三種類型上),跟 new 類似瑟枫,第一個(gè)參數(shù)也是一個(gè)類型而不是一個(gè)值斗搞,跟 new 不同的是,make 返回類型的引用而不是指針慷妙,而返回值也依賴于具體傳入的類型僻焚,具體說明如下:
Slice: 第二個(gè)參數(shù) size 指定了它的長度,它的容量和長度相同景殷。?
你可以傳入第三個(gè)參數(shù)來指定不同的容量值溅呢,但必須不能比長度值小。?
比如 make([]int, 0, 10)
Map: 根據(jù) size 大小來初始化分配內(nèi)存猿挚,不過分配后的 map 長度為 0咐旧,如果 size 被忽略了,那么會(huì)在初始化分配內(nèi)存時(shí)分配一個(gè)小尺寸的內(nèi)存
Channel: 管道緩沖區(qū)依據(jù)緩沖區(qū)容量被初始化绩蜻。如果容量為 0 或者忽略容量铣墨,管道是沒有緩沖區(qū)的
20、初始化結(jié)構(gòu)體的三種方法
a:=new(user)
b:=&user{}
c:=user{}
type?user?struct?{
????id?int?`1123`
}
func?main()?{
????a?:=?&user{}
????a.id?=?111
????b?:=?user{}
????b.id?=?222
????c?:=?new(user)
????c.id?=?333
????fmt.Println(a,?b,?c)
}
?運(yùn)行后結(jié)果如下
&{111}?{222}?&{333}
a:=new(user) <== 創(chuàng)建user變量办绝,并且清零 b:=&user{} <== 返回的是user變量的指針 c:=user{} <== 創(chuàng)建user變量伊约,三種寫法的效果完全一樣,區(qū)別只是 語法糖 的差異而已孕蝉。
小馬不茍同上面這句話屡律,雖然從上面結(jié)果看三種寫法效果是一樣的,但是本質(zhì)有區(qū)別降淮。a?:=?&user{}是取地址超埋,c?:=?new(user)是取地址并清零,b?:=?user{}是拷貝佳鳖。