Go中的 struct:
可將類型分為命名和未命名兩大類。命名類型包括 bool、int、string 等,而 array、slice、map 等和具體元素類型、長度等有關,屬于未命名類型。
具有相同聲明的未命名類型被視為同一類型。
~~~
? 具有相同基類型的指針。
? 具有相同元素類型和長度的 array。
? 具有相同元素類型的 slice。
? 具有相同鍵值類型的 map。
? 具有相同元素類型和傳送方向的 channel。
? 具有相同字段序列 (字段名、類型、標簽、順序) 的匿名 struct。
? 簽名相同 (參數和返回值,不包括參數名稱) 的 function。
? 方法集相同 ( 方法名、方法簽名相同,和次序無關) 的 interface。
~~~
struct 特點:
~~~
1. 用來自定義復雜數據結構
2. struct里面可以包含多個字段(屬性)
3. struct類型可以定義方法,注意和函數的區分
4. struct類型是值類型
5. struct類型可以嵌套
6. Go語言沒有class類型,只有struct類型
7. 結構體是用戶單獨定義的類型,不能和其他類型進行強制轉換
8. golang中的struct沒有構造函數,一般可以使用工廠模式來解決這個問題。
9. 我們可以為struct中的每個字段,寫上一個tag。這個tag可以通過反射的機制獲取到,最常用的場景就是json序列化和反序列化。
~~~
概述
與C語言 struct 一樣,與java/php等class類似,在Go中,用于擴展類型,面向對象編程(本部分暫未做詳細介紹)等
可用 type 在全局或函數內定義新類型。
新類型聲明格式:(是類型的組合)
~~~
package main
import (
"fmt"
"reflect"
)
type bigint int64
func main() {
var x bigint = 100
fmt.Printf("x 的值是:%v\n", x)
fmt.Printf("x 的類型是:%v\n", reflect.TypeOf(x))
type smallint int8
var y smallint = 1
fmt.Printf("y 的值是:%v\n", y)
fmt.Printf("y 的類型是:%v\n", reflect.TypeOf(y))
}
~~~
輸出結果:
~~~
x 的值是:100
x 的類型是:main.bigint
y 的值是:1
y 的類型是:main.smallint
~~~
新類型不是原類型的別名,除擁有相同數據存儲結構外,它們之間沒有任何關系,不會持有原類型任何信息。除非目標類型是未命名類型,否則必須顯式轉換。
~~~
package main
import (
"fmt"
"reflect"
)
type bigint int64
type myslice []int
func main() {
x := 1234
var b bigint = bigint(x) // 必須顯式轉換,除非是常量。
var b2 int64 = int64(b)
fmt.Printf("b2 的值是:%v , b2 的類型是:%v\n", b2, reflect.TypeOf(b2))
var s myslice = []int{1, 2, 3} // 未命名類型,隱式轉換。
var s2 []int = s
fmt.Printf("s2 的值是:%v , s2 的類型是:%v\n", s2, reflect.TypeOf(s2))
}
~~~
輸出結果:
~~~
b2 的值是:1234 , b2 的類型是:int64
s2 的值是:[1 2 3] , s2 的類型是:[]int
~~~
struct聲明及初始化:
聲明:
~~~
type typeName struct {
//...
}
~~~
~~~
package main
type global struct{}
func main() {
type local struct{}
}
~~~
初始化:
方法有幾種:
~~~
package main
import (
"fmt"
)
type Test struct {
int
string
}
var a Test
func main() {
b := new(Test) //同 var b *Test = new(Test)
c := Test{1, "c"}
d := Test{}
e := &Test{}
f := &Test{2, "f"} //同 var d *Test = &Test{2, "f"}
fmt.Println(a, b, c, d, e, f)
// 注: a b c d 返回 Test 類型變量;e f 返回 *Test 類型變量;若無初始化值,則默認為零值
}
~~~
輸出結果:
~~~
{0 } &{0 } {1 c} {0 } &{0 } &{2 f}
~~~
初始化值可以分為兩種:
~~~
a. 有序: typeName{value1, value2, ...} 必須一一對應
b. 無序: typeName{field1:value1, field2:value2, ...} 可初始化部分值
~~~
例:
~~~
package main
import (
"fmt"
)
func main() {
type Person struct {
name string
age int
}
p := Person{"James", 23} //有序
// 順序初始化必須包含全部字段,否則會出錯。
// p1 := Person{"James"} // Error: too few values in struct initializer
p2 := Person{age: 23} //無序
fmt.Println(p)
fmt.Println(p2)
}
~~~
輸出結果:
~~~
{James 23}
{ 23}
~~~
操作
~~~
聲明的struct與普通類型一樣
訪問結構體中的一個變量名, 用 "." 來連接:
varName.field 或 (*varName).field
如操作上面 Person 結構體中的 age : p.age = 35
也可以作為函數中的參數,返回值類型
~~~
代碼:
~~~
package main
import "fmt"
//1. 聲明一個自定義類型名為 Person 的結構體
type Person struct {
name string
age int
}
func main() {
//2. 初始化
var p1 Person
p2 := Person{}
p3 := Person{"James", 23}
p4 := Person{age: 23}
fmt.Println(p1, p2, p3, p4)
p5 := new(Person)
p6 := &Person{}
p7 := &Person{"James", 23}
p8 := &Person{age: 23}
fmt.Println(p5, p6, p7, p8)
//3. 操作
p1.age = 50
p2.age = 25
if compareAge(p1, p2) {
fmt.Println("p1 is older than p2")
} else {
fmt.Println("p2 is older than p1")
}
}
func compareAge(p1, p2 Person) bool {
if p1.age > p2.age {
return true
}
return false
}
~~~
輸出:
~~~
{ 0} { 0} {James 23} { 23}
&{ 0} &{ 0} &{James 23} &{ 23}
p1 is older than p2
~~~
struct的內存布局
struct中的所有字段在內存是連續的,布局如下:

匿名字段:
聲明一個 struct1 可以包含已經存在的 struct2 或者go語言中內置類型作為內置字段,稱為匿名字段,即只寫了 typeName,無 varName,但是 typeName 不能重復。
匿名字段與面向對象程序語言中的繼承
聲明及初始化:
~~~
package main
import (
"fmt"
)
type Person struct {
name string
age int
addr string
}
type Employee struct {
Person //匿名字段
salary int
int //用內置類型作為匿名字段
addr string //類似于重載
}
func main() {
em1 := Employee{Person{"rain", 23, "qingyangqu"}, 5000, 100, "gaoxingqu"}
fmt.Println(em1)
// var em2 Person = em1
// Error: cannot use em1 (type Employee) as type Person in assignment (沒有繼承, 然也不會有多態)
var em2 Person = em1.Person // 同類型拷貝。
fmt.Println(em2)
}
~~~
輸出結果:
~~~
{{Murphy 23 帝都} 5000 100 北京}
{Murphy 23 帝都}
~~~
操作
~~~
訪問方式也是通過 "." 來連接
相同字段采用最外層優先訪問,類似于重載
em1.addr 訪問的是 Employee 中最外層的 addr
em1.Person.addr 訪問的是 Employee 中 Person 中的 addr
~~~
~~~
package main
import "fmt"
type Person struct {
name string
age int
addr string
}
type Employee struct {
Person //匿名字段
salary int
int //用內置類型作為匿名字段
addr string //類似于重載
}
func main() {
/*
var em1 Employee = Employee{}
em1.Person = Person{"rain", 23, "帝都"}
em1.salary = 5000
em1.int = 100 //使用時注意其意義,此處無
em1.addr = "北京"
*/
//em1 := Employee{Person{"rain", 23, "帝都"}, 5000, 100, "北京"}
//初始化方式不一樣,但是結果一樣
em1 := Employee{Person: Person{"Murphy", 23, "帝都"}, salary: 5000, int: 100, addr: "北京"}
fmt.Println(em1)
fmt.Println("live addr(em1.addr) = ", em1.addr)
fmt.Println("work addr(em1.Person.addr) = ", em1.Person.addr)
em1.int = 200 //修改匿名字段的值
}
~~~
輸出:
~~~
{{Murphy 23 帝都} 5000 100 北京}
live addr(em1.addr) = 北京
work addr(em1.Person.addr) = 帝都
~~~
空結構 "節省" 內存, 如用來實現 set 數據結構,或者實現沒有 "狀態" 只有方法的 "靜態類"。
~~~
package main
func main() {
var null struct{}
set := make(map[string]struct{})
set["a"] = null
}
~~~
不能同時嵌入某一類型和其指針類型,因為它們名字相同。
~~~
package main
type Resource struct {
id int
}
type User struct {
*Resource
// Resource // Error: duplicate field Resource
name string
}
func main() {
u := User{
&Resource{1},
"Administrator",
}
println(u.id)
println(u.Resource.id)
}
~~~
輸出結果:
~~~
1
1
~~~
struct與tag應用
在處理json格式字符串的時候,經常會看到聲明struct結構的時候,屬性的右側還有小米點括起來的內容。形如:
~~~
type User struct {
UserId int `json:"user_id" bson:"user_id"`
UserName string `json:"user_name" bson:"user_name"`
}
~~~
這個小米點里的內容是用來干什么的呢?
struct成員變量標簽(Tag)說明
要比較詳細的了解這個,要先了解一下golang的基礎,在golang中,命名都是推薦都是用駝峰方式,并且在首字母大小寫有特殊的語法含義:包外無法引用。但是由經常需要和其它的系統進行數據交互,例如轉成json格式,存儲到mongodb啊等等。這個時候如果用屬性名來作為鍵值可能不一定會符合項目要求。
所以呢就多了小米點的內容,在golang中叫標簽(Tag),在轉換成其它數據格式的時候,會使用其中特定的字段作為鍵值。例如上例在轉成json格式:
~~~
package main
import (
"encoding/json"
"fmt"
)
type User struct {
UserId int
UserName string
}
type UserTag struct {
UserId int `json:"user_id" bson:"user_id"`
UserName string `json:"user_name" bson:"user_name"`
}
func main() {
u := &User{UserId: 1, UserName: "Murphy"}
j, _ := json.Marshal(u)
fmt.Printf("struct User echo : %v\n", string(j))
u_tag := &UserTag{UserId: 1, UserName: "Murphy"}
j_tag, _ := json.Marshal(u_tag)
fmt.Printf("struct UserTag echo : %v\n", string(j_tag))
}
~~~
輸出結果:
~~~
struct User echo : {"UserId":1,"UserName":"Murphy"}
struct UserTag echo : {"user_id":1,"user_name":"Murphy"}
~~~
可以看到直接用struct的屬性名做鍵值。
其中還有一個bson的聲明,這個是用在將數據存儲到mongodb使用的。
標簽是類型的組成部分。
~~~
package main
var a struct {
x int `a`
}
var b struct {
x int `ab`
}
func main() {
a = b
}
~~~
輸出結果:
~~~
./main.go:11:4: cannot use b (type struct { x int "ab" }) as type struct { x int "a" } in assignment
~~~
~~~
package main
var u1 struct {
name string "username"
}
var u2 struct{ name string }
func main() {
u1 = u2
}
~~~
輸出結果:
~~~
./main.go:9:5: cannot use u2 (type struct { name string }) as type struct { name string "username" } in assignment
~~~
struct成員變量標簽(Tag)獲取
那么當我們需要自己封裝一些操作,需要用到Tag中的內容時,咋樣去獲取呢?這邊可以使用反射包(reflect)中的方法來獲取:
~~~
t := reflect.TypeOf(u)
field := t.Elem().Field(0)
fmt.Println(field.Tag.Get("json"))
fmt.Println(field.Tag.Get("bson"))
~~~
完整代碼如下:
~~~
package main
import (
"encoding/json"
"fmt"
"reflect"
)
func main() {
type User struct {
//多個Key使用空格進行分開,然后使用Get方法獲取不同Key的值。
UserId int `json:"user_json_id" xml:"user_xml_id"`
UserName string `json:"user_json_name" xml:"user_xml_name"`
}
// 輸出json格式
u := &User{UserId: 1, UserName: "tony"}
j, _ := json.Marshal(u)
fmt.Println(string(j))
// 獲取tag中的內容
t := reflect.TypeOf(u)
fmt.Println(t)
field0 := t.Elem().Field(0)
fmt.Println(field0.Tag.Get("json"))
fmt.Println(field0.Tag.Get("xml"))
field1 := t.Elem().Field(1)
fmt.Println(field1.Tag.Get("json"))
fmt.Println(field1.Tag.Get("xml"))
}
~~~
輸出結果:
~~~
{"user_json_id":1,"user_json_name":"tony"}
*main.User
user_json_id
user_xml_id
user_json_name
user_xml_name
~~~
有意思的struct大小
我們定義一個struct,這個struct有3個字段,它們的類型有byte,int32以及int64,但是這三個字段的順序我們可以任意排列,那么根據順序的不同,一共有6種組合。
~~~
type user1 struct {
b byte
i int32
j int64
}
type user2 struct {
b byte
j int64
i int32
}
type user3 struct {
i int32
b byte
j int64
}
type user4 struct {
i int32
j int64
b byte
}
type user5 struct {
j int64
b byte
i int32
}
type user6 struct {
j int64
i int32
b byte
}
~~~
根據這6種組合,定義了6個struct,分別位user1,user2,…,user6,那么現在大家猜測一下,這6種類型的struct占用的內存是多少,就是unsafe.Sizeof()的值。
大家可能猜測1+4+8=13,因為byte的大小為1,int32大小為4,int64大小為8,而struct其實就是一個字段的組合,所以猜測struct大小為字段大小之和也很正常。
但是,但是,我可以明確的說,這是錯誤的。
為什么是錯誤的,因為有內存對齊存在,編譯器使用了內存對齊,那么最后的大小結果就不一樣了。現在我們正式驗證下,這幾種struct的值。
~~~
package main
import (
"fmt"
"unsafe"
)
type user1 struct {
b byte
i int32
j int64
}
type user2 struct {
b byte
j int64
i int32
}
type user3 struct {
i int32
b byte
j int64
}
type user4 struct {
i int32
j int64
b byte
}
type user5 struct {
j int64
b byte
i int32
}
type user6 struct {
j int64
i int32
b byte
}
func main() {
var u1 user1
var u2 user2
var u3 user3
var u4 user4
var u5 user5
var u6 user6
fmt.Println("u1 size is ", unsafe.Sizeof(u1))
fmt.Println("u2 size is ", unsafe.Sizeof(u2))
fmt.Println("u3 size is ", unsafe.Sizeof(u3))
fmt.Println("u4 size is ", unsafe.Sizeof(u4))
fmt.Println("u5 size is ", unsafe.Sizeof(u5))
fmt.Println("u6 size is ", unsafe.Sizeof(u6))
}
~~~
輸出結果:
~~~
u1 size is 16
u2 size is 24
u3 size is 16
u4 size is 24
u5 size is 16
u6 size is 16
~~~
結果出來了(我的電腦的結果,Mac64位,你的可能不一樣),4個16字節,2個24字節,既不一樣,又不相同,這說明:
內存對齊影響struct的大小
struct的字段順序影響struct的大小
綜合以上兩點,我們可以得知,不同的字段順序,最終決定struct的內存大小,所以有時候合理的字段順序可以減少內存的開銷。
內存對齊會影響struct的內存占用大小,現在我們就詳細分析下,為什么字段定義的順序不同會導致struct的內存占用不一樣。
在分析之前,我們先看下內存對齊的規則:
對于具體類型來說,對齊值=min(編譯器默認對齊值,類型大小Sizeof長度)。也就是在默認設置的對齊值和類型的內存占用大小之間,取最小值為該類型的對齊值。我的電腦默認是8,所以最大值不會超過8.
struct在每個字段都內存對齊之后,其本身也要進行對齊,對齊值=min(默認對齊值,字段最大類型長度)。這條也很好理解,struct的所有字段中,最大的那個類型的長度以及默認對齊值之間,取最小的那個。
以上這兩條規則要好好理解,理解明白了才可以分析下面的struct結構體。在這里再次提醒,對齊值也叫對齊系數、對齊倍數,對齊模數。這就是說,每個字段在內存中的偏移量是對齊值的倍數即可。
我們知道byte,int32,int64的對齊值分別為1,4,8,占用內存大小也是1,4,8。那么對于第一個structuser1,它的字段順序是byte、int32、int64,我們先使用第1條內存對齊規則進行內存對齊,其內存結構如下。
bxxx|iiii|jjjj|jjjj
user1類型,第1個字段byte,對齊值1,大小1,所以放在內存布局中的第1位。
第2個字段int32,對齊值4,大小4,所以它的內存偏移值必須是4的倍數,在當前的user1中,就不能從第2位開始了,必須從第5位開始,也就是偏移量為4。第2,3,4位由編譯器進行填充,一般為值0,也稱之為內存空洞。所以第5位到第8位為第2個字段i。
第3字段,對齊值為8,大小也是8。因為user1前兩個字段已經排到了第8位,所以下一位的偏移量正好是8,是第3個字段對齊值的倍數,不用填充,可以直接排列第3個字段,也就是從第9位到第16位為第3個字段j。
現在第一條內存對齊規則后,內存長度已經為16個字節,我們開始使用內存的第2條規則進行對齊。根據第二條規則,默認對齊值8,字段中最大類型長度也是8,所以求出結構體的對齊值位8,我們目前的內存長度為16,是8的倍數,已經實現了對齊。
所以到此為止,結構體user1的內存占用大小為16字節。
現在我們再分析一個user2類型,它的大小是24,只是調換了一下字段i和j的順序,就多占用了8個字節,我們看看為什么?還是先使用我們的內存第1條規則分析。
bxxx|xxxx|jjjj|jjjj|iiii
按對齊值和其占用的大小,第1個字段b偏移量為0,占用1個字節,放在第1位。
第2個字段j,是int64,對齊值和大小都是8,所以要從偏移量8開始,也就是第9到16位為j,這也就意味著第2到8位被編譯器填充。
目前整個內存布局已經偏移了16位,正好是第3個字段i的對齊值4的倍數,所以不用填充,可以直接排列,第17到20位為i。
現在所有字段對齊好了,整個內存大小為1+7+8+4=20個字節,我們開始使用內存對齊的第2條規則,也就是結構體的對齊,通過默認對齊值和最大的字段大小,求出結構體的對齊值為8。
現在我們的整個內存布局大小為20,不是8的倍數,所以我們需要進行內存填充,補足到8的倍數,最小的就是24,所以對齊后整個內存布局為
bxxx|xxxx|jjjj|jjjj|iiii|xxxx
所以這也是為什么我們最終獲得的user2的大小為24的原因。
基于以上辦法,我們可以得出其他幾個struct的內存布局。
user3
iiii|bxxx|jjjj|jjjj
user4
iiii|xxxx|jjjj|jjjj|bxxx|xxxx
user5
jjjj|jjjj|bxxx|iiii
user6
jjjj|jjjj|iiii|bxxx
以上給出了答案,推到過程大家可以參考user1和user2試試。
- 序言
- 目錄
- 環境搭建
- Linux搭建golang環境
- Windows搭建golang環境
- Mac搭建golang環境
- Go 環境變量
- 編輯器
- vs code
- Mac 安裝vs code
- Windows 安裝vs code
- vim編輯器
- 介紹
- 1.Go語言的主要特征
- 2.golang內置類型和函數
- 3.init函數和main函數
- 4.包
- 1.工作空間
- 2.源文件
- 3.包結構
- 4.文檔
- 5.編寫 Hello World
- 6.Go語言 “ _ ”(下劃線)
- 7.運算符
- 8.命令
- 類型
- 1.變量
- 2.常量
- 3.基本類型
- 1.基本類型介紹
- 2.字符串String
- 3.數組Array
- 4.類型轉換
- 4.引用類型
- 1.引用類型介紹
- 2.切片Slice
- 3.容器Map
- 4.管道Channel
- 5.指針
- 6.自定義類型Struct
- 流程控制
- 1.條件語句(if)
- 2.條件語句 (switch)
- 3.條件語句 (select)
- 4.循環語句 (for)
- 5.循環語句 (range)
- 6.循環控制Goto、Break、Continue
- 函數
- 1.函數定義
- 2.參數
- 3.返回值
- 4.匿名函數
- 5.閉包、遞歸
- 6.延遲調用 (defer)
- 7.異常處理
- 8.單元測試
- 壓力測試
- 方法
- 1.方法定義
- 2.匿名字段
- 3.方法集
- 4.表達式
- 5.自定義error
- 接口
- 1.接口定義
- 2.執行機制
- 3.接口轉換
- 4.接口技巧
- 面向對象特性
- 并發
- 1.并發介紹
- 2.Goroutine
- 3.Chan
- 4.WaitGroup
- 5.Context
- 應用
- 反射reflection
- 1.獲取基本類型
- 2.獲取結構體
- 3.Elem反射操作基本類型
- 4.反射調用結構體方法
- 5.Elem反射操作結構體
- 6.Elem反射獲取tag
- 7.應用
- json協議
- 1.結構體轉json
- 2.map轉json
- 3.int轉json
- 4.slice轉json
- 5.json反序列化為結構體
- 6.json反序列化為map
- 終端讀取
- 1.鍵盤(控制臺)輸入fmt
- 2.命令行參數os.Args
- 3.命令行參數flag
- 文件操作
- 1.文件創建
- 2.文件寫入
- 3.文件讀取
- 4.文件刪除
- 5.壓縮文件讀寫
- 6.判斷文件或文件夾是否存在
- 7.從一個文件拷貝到另一個文件
- 8.寫入內容到Excel
- 9.日志(log)文件
- server服務
- 1.服務端
- 2.客戶端
- 3.tcp獲取網頁數據
- 4.http初識-瀏覽器訪問服務器
- 5.客戶端訪問服務器
- 6.訪問延遲處理
- 7.form表單提交
- web模板
- 1.渲染終端
- 2.渲染瀏覽器
- 3.渲染存儲文件
- 4.自定義io.Writer渲染
- 5.模板語法
- 時間處理
- 1.格式化
- 2.運行時間
- 3.定時器
- 鎖機制
- 互斥鎖
- 讀寫鎖
- 性能比較
- sync.Map
- 原子操作
- 1.原子增(減)值
- 2.比較并交換
- 3.導入、導出、交換
- 加密解密
- 1.md5
- 2.base64
- 3.sha
- 4.hmac
- 常用算法
- 1.冒泡排序
- 2.選擇排序
- 3.快速排序
- 4.插入排序
- 5.睡眠排序
- 限流器
- 日志包
- 日志框架logrus
- 隨機數驗證碼
- 生成指定位數的隨機數
- 生成圖形驗證碼
- 編碼格式轉換
- UTF-8與GBK
- 解決中文亂碼
- 設計模式
- 創建型模式
- 單例模式
- singleton.go
- singleton_test.go
- 抽象工廠模式
- abstractfactory.go
- abstractfactory_test.go
- 工廠方法模式
- factorymethod.go
- factorymethod_test.go
- 原型模式
- prototype.go
- prototype_test.go
- 生成器模式
- builder.go
- builder_test.go
- 結構型模式
- 適配器模式
- adapter.go
- adapter_test.go
- 橋接模式
- bridge.go
- bridge_test.go
- 合成/組合模式
- composite.go
- composite_test.go
- 裝飾模式
- decoretor.go
- decorator_test.go
- 外觀模式
- facade.go
- facade_test.go
- 享元模式
- flyweight.go
- flyweight_test.go
- 代理模式
- proxy.go
- proxy_test.go
- 行為型模式
- 職責鏈模式
- chainofresponsibility.go
- chainofresponsibility_test.go
- 命令模式
- command.go
- command_test.go
- 解釋器模式
- interpreter.go
- interperter_test.go
- 迭代器模式
- iterator.go
- iterator_test.go
- 中介者模式
- mediator.go
- mediator_test.go
- 備忘錄模式
- memento.go
- memento_test.go
- 觀察者模式
- observer.go
- observer_test.go
- 狀態模式
- state.go
- state_test.go
- 策略模式
- strategy.go
- strategy_test.go
- 模板模式
- templatemethod.go
- templatemethod_test.go
- 訪問者模式
- visitor.go
- visitor_test.go
- 數據庫操作
- golang操作MySQL
- 1.mysql使用
- 2.insert操作
- 3.select 操作
- 4.update 操作
- 5.delete 操作
- 6.MySQL事務
- golang操作Redis
- 1.redis介紹
- 2.golang鏈接redis
- 3.String類型 Set、Get操作
- 4.String 批量操作
- 5.設置過期時間
- 6.list隊列操作
- 7.Hash表
- 8.Redis連接池
- 其它Redis包
- go-redis/redis包
- 安裝介紹
- String 操作
- List操作
- Set操作
- Hash操作
- golang操作ETCD
- 1.etcd介紹
- 2.鏈接etcd
- 3.etcd存取
- 4.etcd監聽Watch
- golang操作kafka
- 1.kafka介紹
- 2.寫入kafka
- 3.kafka消費
- golang操作ElasticSearch
- 1.ElasticSearch介紹
- 2.kibana介紹
- 3.寫入ElasticSearch
- NSQ
- 安裝
- 生產者
- 消費者
- zookeeper
- 基本操作測試
- 簡單的分布式server
- Zookeeper命令行使用
- GORM
- gorm介紹
- gorm查詢
- gorm更新
- gorm刪除
- gorm錯誤處理
- gorm事務
- sql構建
- gorm 用法介紹
- Go操作memcached
- beego框架
- 1.beego框架環境搭建
- 2.參數配置
- 1.默認參數
- 2.自定義配置
- 3.config包使用
- 3.路由設置
- 1.自動匹配
- 2.固定路由
- 3.正則路由
- 4.注解路由
- 5.namespace
- 4.多種數據格式輸出
- 1.直接輸出字符串
- 2.模板數據輸出
- 3.json格式數據輸出
- 4.xml格式數據輸出
- 5.jsonp調用
- 5.模板處理
- 1.模板語法
- 2.基本函數
- 3.模板函數
- 6.請求處理
- 1.GET請求
- 2.POST請求
- 3.文件上傳
- 7.表單驗證
- 1.表單驗證
- 2.定制錯誤信息
- 3.struct tag 驗證
- 4.XSRF過濾
- 8.靜態文件處理
- 1.layout設計
- 9.日志處理
- 1.日志處理
- 2.logs 模塊
- 10.會話控制
- 1.會話控制
- 2.session 包使用
- 11.ORM 使用
- 1.鏈接數據庫
- 2. CRUD 操作
- 3.原生 SQL 操作
- 4.構造查詢
- 5.事務處理
- 6.自動建表
- 12.beego 驗證碼
- 1.驗證碼插件
- 2.驗證碼使用
- beego admin
- 1.admin安裝
- 2.admin開發
- beego 熱升級
- beego實現https
- gin框架
- 安裝使用
- 路由設置
- 模板處理
- 文件上傳
- gin框架中文文檔
- gin錯誤總結
- 項目
- 秒殺項目
- 日志收集
- 面試題
- 面試題一
- 面試題二
- 錯題集
- Go語言陷阱和常見錯誤
- 常見語法錯誤
- 初級
- 中級
- 高級
- Go高級應用
- goim
- goim 啟動流程
- goim 工作流程
- goim 結構體
- gopush
- gopush工作流程
- gopush啟動流程
- gopush業務流程
- gopush應用
- gopush新添功能
- gopush壓力測試
- 壓測注意事項
- rpc
- HTTP RPC
- TCP RPC
- JSON RPC
- 常見RPC開源框架
- pprof
- pprof介紹
- pprof應用
- 使用pprof及Go 程序的性能優化
- 封裝 websocket
- cgo
- Golang GC
- 查看程序運行過程中的GC信息
- 定位gc問題所在
- Go語言 demo
- 用Go語言計算一個人的年齡,生肖,星座
- 超簡易Go語言實現的留言板代碼
- 信號處理模塊,可用于在線加載配置,配置動態加載的信號為SIGHUP
- 陽歷和陰歷相互轉化的工具類 golang版本
- 錯誤總結
- 網絡編程
- 網絡編程http
- 網絡編程tcp
- Http請求
- Go語言必知的90個知識點
- 第三方庫應用
- cli應用
- Cobra
- 圖表庫
- go-echarts
- 開源IM
- im_service
- 機器學習庫
- Tensorflow
- 生成二維碼
- skip2/go-qrcode生成二維碼
- boombuler/barcode生成二維碼
- tuotoo/qrcode識別二維碼
- 日志庫
- 定時任務
- robfig/cron
- jasonlvhit/gocron
- 拼多多開放平臺 SDK
- Go編譯
- 跨平臺交叉編譯
- 一問一答
- 一問一答(一)
- 為什么 Go 標準庫中有些函數只有簽名,沒有函數體?
- Go開發的應用
- etcd
- k8s
- Caddy
- nsq
- Docker
- web框架