├── .gitignore ├── README.md ├── gin ├── README.md ├── main.go └── main_test.go ├── go.mod ├── go.sum ├── gorm ├── main.go └── sql │ ├── create.sql │ └── insert.sql ├── grammar ├── atomic │ └── atomic.go ├── channel │ └── channel.go ├── context │ ├── cancel │ │ └── main.go │ ├── done │ │ └── main.go │ └── inheritance │ │ └── main.go ├── goroutine │ └── goroutine.go ├── goroutine_recover │ └── main.go ├── interface │ ├── basic │ │ └── main.go │ ├── flow_restriction │ │ └── main.go │ ├── latency │ │ └── main.go │ └── timeout │ │ ├── README.md │ │ └── main.go ├── map │ └── map.go ├── mutex │ └── mutex.go ├── slice │ └── slice.go └── sync_once_singleton │ └── sync_once_singleton.go ├── grpc ├── api.pb.go ├── api.proto ├── api_grpc.pb.go ├── client │ └── client.go └── server │ └── server.go ├── interview └── deeproute.ai │ ├── concurrent_printout │ └── main.go │ ├── context-timeout-retry.go │ └── context_timeout_retry │ └── main.go ├── kafka ├── README.md └── main.go ├── multi_thread ├── async_get_age │ └── async_get_age.go └── async_get_age_with_timeout │ └── async_get_age_with_timeout.go ├── redis ├── basic_operation │ └── main.go ├── bloom_filter │ └── bloom_filter.go ├── hmset_get │ └── hmset.go ├── redlock │ └── redlock.go ├── redlock_lua │ └── redlock_lua.go └── update_redis_from_mysql │ └── main.go ├── set_proxy.sh ├── simple_project ├── cmd │ └── main.go ├── go.mod ├── go.sum └── util │ ├── math │ ├── add.go │ └── sub.go │ └── sum.go └── testing ├── README.md ├── benchmark └── main_test.go └── unit └── main_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | sql_test/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # golang-tips 2 | 3 | Provide some examples of learning Golang. Every project can build by `go run` command, such as 4 | ```bash 5 | go run redis/update_redis_from_mysql/main.go 6 | ``` 7 | -------------------------------------------------------------------------------- /gin/README.md: -------------------------------------------------------------------------------- 1 | # gin 2 | 3 | GET Request 4 | ```bash 5 | go test -v ./ -run=GetName -count=1 # -count=1 can clear cache 6 | ``` 7 | 8 | POST Request 9 | ```bash 10 | go test -v ./ -run=GetAge -count=1 11 | ``` 12 | 13 | POST Request (data type is JSON format) 14 | ```bash 15 | go test -v ./ -run=GetHeight -count=1 16 | ``` -------------------------------------------------------------------------------- /gin/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net/http" 7 | "strconv" 8 | 9 | "github.com/gin-gonic/gin" 10 | "github.com/go-redis/redis/v8" 11 | ) 12 | 13 | type Student struct { 14 | Name string 15 | Age int 16 | Height float32 17 | } 18 | 19 | type Request struct { 20 | StudentId string `json:"student_id"` 21 | } 22 | 23 | func GetStudentInfo(id string) Student { 24 | client := redis.NewClient(&redis.Options{ 25 | Addr: "localhost:6379", 26 | Password: "1", 27 | DB: 0, 28 | }) 29 | ctx := context.Background() 30 | 31 | stu := Student{} 32 | for field, value := range client.HGetAll(ctx, id).Val() { 33 | if field == "Name" { 34 | stu.Name = value 35 | } else if field == "Age" { 36 | if age, err := strconv.Atoi(value); err != nil { 37 | log.Println("convert failed:", err) 38 | } else { 39 | stu.Age = age 40 | } 41 | } else if field == "Height" { 42 | if height, err := strconv.ParseFloat(value, 10); err != nil { 43 | log.Println("convert failed:", err) 44 | } else { 45 | stu.Height = float32(height) 46 | } 47 | } 48 | } 49 | 50 | return stu 51 | } 52 | 53 | // GET interface: get parameters from the front end 54 | func GetName(ctx *gin.Context) { 55 | param := ctx.Query("student_id") // GET 56 | if len(param) == 0 { 57 | ctx.String(http.StatusBadRequest, "no student_id recv") 58 | return 59 | } 60 | 61 | stu := GetStudentInfo(param) 62 | ctx.String(http.StatusOK, stu.Name) 63 | } 64 | 65 | // POST interface: get parameters from the front end 66 | func GetAge(ctx *gin.Context) { 67 | param := ctx.PostForm("student_id") 68 | if len(param) == 0 { 69 | ctx.String(http.StatusBadRequest, "no student_id recv") 70 | return 71 | } 72 | 73 | stu := GetStudentInfo(param) 74 | ctx.String(http.StatusOK, strconv.Itoa(stu.Age)) 75 | } 76 | 77 | func GetHeight(ctx *gin.Context) { 78 | var param Request 79 | if err := ctx.BindJSON(¶m); err != nil { 80 | ctx.String(http.StatusBadRequest, "student_id need be JSON format") 81 | return 82 | } 83 | 84 | stu := GetStudentInfo(param.StudentId) 85 | ctx.JSON(http.StatusOK, stu) 86 | } 87 | 88 | func main() { 89 | engine := gin.Default() 90 | engine.GET("/get_name", GetName) // set up routing 91 | engine.POST("/get_age", GetAge) 92 | engine.POST("/get_height", GetHeight) 93 | if err := engine.Run("localhost:6677"); err != nil { 94 | panic(err) 95 | } // bind 96 | } 97 | -------------------------------------------------------------------------------- /gin/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | "net/url" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | func TestGetStudentInfo(t *testing.T) { 14 | id := "id:258" 15 | stu := GetStudentInfo(id) 16 | 17 | if len(stu.Name) == 0 { 18 | t.Fail() 19 | } else { 20 | log.Printf("%+v\n", stu) 21 | } 22 | } 23 | 24 | // http request 25 | func TestGetName(t *testing.T) { 26 | if rsp, err := http.Get("http://localhost:6677/get_name?student_id=id:258"); err != nil { 27 | log.Println("Get Failed:", err) 28 | t.Fail() 29 | } else { 30 | defer rsp.Body.Close() // close body 31 | if bt, err := ioutil.ReadAll(rsp.Body); err != nil { 32 | log.Println("ReadAll Failed:", err) 33 | t.Fail() 34 | } else { 35 | log.Println(string(bt)) 36 | } 37 | } 38 | } 39 | 40 | func TestGetAge(t *testing.T) { 41 | if rsp, err := http.PostForm("http://localhost:6677/get_age", url.Values{"student_id": []string{"id:369"}}); err != nil { 42 | log.Println("POST Failed:", err) 43 | t.Fail() 44 | } else { 45 | defer rsp.Body.Close() // close body 46 | if bt, err := ioutil.ReadAll(rsp.Body); err != nil { 47 | log.Println("ReadAll Failed:", err) 48 | t.Fail() 49 | } else { 50 | log.Println(string(bt)) 51 | } 52 | } 53 | } 54 | 55 | func TestGetHeight(t *testing.T) { 56 | client := http.Client{} 57 | reader := strings.NewReader(`{"student_id":"id:258"}`) 58 | request, err := http.NewRequest("POST", "http://localhost:6677/get_height", reader) 59 | if err != nil { 60 | log.Println("Create NewRequest Failed:", err) 61 | return 62 | } 63 | request.Header.Add("Content-Type", "application/json") // type 64 | 65 | if rsp, err := client.Do(request); err != nil { 66 | log.Println("POST Failed:", err) 67 | t.Fail() 68 | } else { 69 | defer rsp.Body.Close() // close body 70 | if bt, err := ioutil.ReadAll(rsp.Body); err != nil { 71 | log.Println("ReadAll Failed:", err) 72 | t.Fail() 73 | } else { 74 | var stu Student 75 | if err := json.Unmarshal(bt, &stu); err != nil { // deserialization 76 | log.Println(err) 77 | t.Fail() 78 | } 79 | log.Println("full info:", stu) 80 | log.Printf("%+v\n", stu.Height) 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module grammar 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/bytedance/sonic v1.9.1 // indirect 7 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 8 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 9 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 10 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 11 | github.com/gin-contrib/sse v0.1.0 // indirect 12 | github.com/gin-gonic/gin v1.9.1 // indirect 13 | github.com/go-playground/locales v0.14.1 // indirect 14 | github.com/go-playground/universal-translator v0.18.1 // indirect 15 | github.com/go-playground/validator/v10 v10.14.0 // indirect 16 | github.com/go-redis/redis/v8 v8.11.5 // indirect 17 | github.com/go-sql-driver/mysql v1.7.1 // indirect 18 | github.com/goccy/go-json v0.10.2 // indirect 19 | github.com/golang/protobuf v1.5.3 // indirect 20 | github.com/huandu/go-sqlbuilder v1.21.0 // indirect 21 | github.com/huandu/xstrings v1.3.2 // indirect 22 | github.com/jinzhu/inflection v1.0.0 // indirect 23 | github.com/jinzhu/now v1.1.5 // indirect 24 | github.com/json-iterator/go v1.1.12 // indirect 25 | github.com/klauspost/compress v1.15.9 // indirect 26 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 27 | github.com/leodido/go-urn v1.2.4 // indirect 28 | github.com/mattn/go-isatty v0.0.19 // indirect 29 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 30 | github.com/modern-go/reflect2 v1.0.2 // indirect 31 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 32 | github.com/pierrec/lz4/v4 v4.1.15 // indirect 33 | github.com/segmentio/kafka-go v0.4.40 // indirect 34 | github.com/spaolacci/murmur3 v1.1.0 // indirect 35 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 36 | github.com/ugorji/go/codec v1.2.11 // indirect 37 | github.com/willf/bitset v1.1.11 // indirect 38 | github.com/willf/bloom v2.0.3+incompatible // indirect 39 | golang.org/x/arch v0.3.0 // indirect 40 | golang.org/x/crypto v0.9.0 // indirect 41 | golang.org/x/net v0.10.0 // indirect 42 | golang.org/x/sys v0.8.0 // indirect 43 | golang.org/x/text v0.9.0 // indirect 44 | google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect 45 | google.golang.org/grpc v1.55.0 // indirect 46 | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 // indirect 47 | google.golang.org/protobuf v1.30.0 // indirect 48 | gopkg.in/yaml.v3 v3.0.1 // indirect 49 | gorm.io/driver/mysql v1.5.1 // indirect 50 | gorm.io/gorm v1.25.1 // indirect 51 | ) 52 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 2 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 3 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 4 | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= 5 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 6 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 7 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 8 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 9 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 10 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 11 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 13 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 14 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 15 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 16 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 17 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 18 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 19 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 20 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 21 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 22 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 23 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 24 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 25 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 26 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 27 | github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= 28 | github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= 29 | github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 30 | github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= 31 | github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 32 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 33 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 34 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 35 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 36 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 37 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 38 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 39 | github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= 40 | github.com/huandu/go-sqlbuilder v1.21.0 h1:+NLH8PQg5/WGMXJLIpAXTdoH1pv9Q3BU6w4P7OabBmc= 41 | github.com/huandu/go-sqlbuilder v1.21.0/go.mod h1:nUVmMitjOmn/zacMLXT0d3Yd3RHoO2K+vy906JzqxMI= 42 | github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= 43 | github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 44 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 45 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 46 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 47 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 48 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 49 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 50 | github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= 51 | github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= 52 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 53 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 54 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 55 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 56 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 57 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 58 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 59 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 60 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 61 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 62 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 63 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 64 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 65 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 66 | github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= 67 | github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 68 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 69 | github.com/segmentio/kafka-go v0.4.40 h1:sszW7c0/uyv7+VcTW5trx2ZC7kMWDTxuR/6Zn8U1bm8= 70 | github.com/segmentio/kafka-go v0.4.40/go.mod h1:naFEZc5MQKdeL3W6NkZIAn48Y6AazqjRFDhnXeg3h94= 71 | github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= 72 | github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 73 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 74 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 75 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 76 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 77 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 78 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 79 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 80 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 81 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 82 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 83 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 84 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 85 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 86 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 87 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 88 | github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= 89 | github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= 90 | github.com/willf/bitset v1.2.2/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= 91 | github.com/willf/bitset v1.3.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= 92 | github.com/willf/bitset v1.3.2/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= 93 | github.com/willf/bitset v1.6.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= 94 | github.com/willf/bloom v2.0.3+incompatible h1:QDacWdqcAUI1MPOwIQZRy9kOR7yxfyEmxX8Wdm2/JPA= 95 | github.com/willf/bloom v2.0.3+incompatible/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8= 96 | github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= 97 | github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= 98 | github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= 99 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 100 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 101 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 102 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 103 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 104 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 105 | golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= 106 | golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 107 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 108 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 109 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 110 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 111 | golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= 112 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 113 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 114 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 115 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 116 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 117 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 118 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 119 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 120 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 121 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 122 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 123 | golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= 124 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 125 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 126 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 127 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 128 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 129 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 130 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 131 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 132 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 133 | golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= 134 | golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 135 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 136 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 137 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 138 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 139 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 140 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 141 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 142 | google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= 143 | google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= 144 | google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= 145 | google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= 146 | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA= 147 | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= 148 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 149 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 150 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 151 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 152 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 153 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 154 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 155 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 156 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 157 | gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw= 158 | gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o= 159 | gorm.io/gorm v1.25.1 h1:nsSALe5Pr+cM3V1qwwQ7rOkw+6UeLrX5O4v3llhHa64= 160 | gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= 161 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 162 | -------------------------------------------------------------------------------- /gorm/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "gorm.io/driver/mysql" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | type User struct { 11 | Id int 12 | Uid int 13 | Keyword string `gorm:"column:keywords"` // key name mapping 14 | Degree string 15 | Gender string 16 | City string 17 | } 18 | 19 | func (User) TableName() string { 20 | return "user" // table name mapping 21 | } 22 | 23 | func query(client *gorm.DB, city string) *User { 24 | var user User 25 | user.Uid = 10086 26 | err := client.Select("id, city").Where("city=?", city).Limit(1).First(&user).Error 27 | if err != nil { 28 | log.Println("Error querying data:", err) 29 | } 30 | return &user 31 | } 32 | 33 | func main() { 34 | conn := "debian-sys-maint:P7g9fAYfTM4YhtoC@tcp(localhost:3306)/gorm?charset=utf8&parseTime=True" 35 | client, err := gorm.Open(mysql.Open(conn), nil) 36 | if err != nil { 37 | log.Println("Error connecting MySQL:", err) 38 | } 39 | 40 | // query 41 | user := query(client, "SH") 42 | if user != nil { 43 | log.Printf("%+v\n", *user) // "%+v" print struct data 44 | } else { 45 | log.Println("user is nil") 46 | } 47 | 48 | // insert 49 | newUser := User{ 50 | Uid: 8848, 51 | Keyword: "clam down", 52 | Degree: "B", 53 | Gender: "M", 54 | City: "BJ", 55 | } 56 | if err = client.Create(&newUser).Error; err != nil { 57 | log.Println("Error creating record:", err) 58 | } 59 | 60 | // update 61 | res := client.Model(&User{}).Where("uid=?", 8848).Update("city", "TW") 62 | if res.Error != nil { 63 | log.Println("Error updating record:", res.Error) 64 | } 65 | 66 | // update multiple column 67 | res = client.Model(&User{}).Where("uid=?", 8848).Updates( 68 | map[string]interface{}{"city": "TW", "gender": "F"}, 69 | ) 70 | if res.Error != nil { 71 | log.Println("Error updating multiple record:", res.Error) 72 | } 73 | 74 | // delete 75 | res = client.Where("uid=?", 95535).Delete(&User{}) 76 | if res.Error != nil { 77 | log.Println("Error deleting record:", res.Error) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /gorm/sql/create.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `user` ( 2 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键', 3 | `uid` int(11) NOT NULL COMMENT '用户id', 4 | `keywords` text NOT NULL COMMENT '索引词', 5 | `degree` char(2) NOT NULL COMMENT '学历', 6 | `gender` char(2) NOT NULL COMMENT '性别', 7 | `city` char(2) NOT NULL COMMENT '城市', 8 | PRIMARY KEY (`id`), 9 | UNIQUE KEY `idx_uid` (`uid`) 10 | ) ENGINE=InnoDB AUTO_INCREMENT=1707919 DEFAULT CHARSET=utf8 COMMENT='用户信息表'; -------------------------------------------------------------------------------- /gorm/sql/insert.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO `user` (uid, keywords, degree, gender, city) VALUES(10086, "bring it on", "B", "M", "SH"); 2 | INSERT INTO `user` (uid, keywords, degree, gender, city) VALUES(95535, "KFC", "M", "M", "BJ"); -------------------------------------------------------------------------------- /grammar/atomic/atomic.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | var counter int64 10 | var wg sync.WaitGroup 11 | 12 | func main() { 13 | ch := make(chan struct{}) 14 | 15 | wg.Add(10) 16 | 17 | for i := 0; i < 10; i++ { 18 | go increment(ch) 19 | } 20 | 21 | wg.Wait() 22 | close(ch) 23 | 24 | fmt.Println(atomic.LoadInt64(&counter)) 25 | } 26 | 27 | func increment(ch chan struct{}) { 28 | atomic.AddInt64(&counter, 1) 29 | wg.Done() 30 | <-ch 31 | } 32 | -------------------------------------------------------------------------------- /grammar/channel/channel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | var wg sync.WaitGroup 10 | 11 | func main() { 12 | ch1 := make(chan struct{}, 1) 13 | ch2 := make(chan struct{}, 1) 14 | ch3 := make(chan struct{}, 1) 15 | ch1 <- struct{}{} 16 | 17 | wg.Add(3) 18 | start := time.Now().Unix() 19 | go print("goroutine-1", ch1, ch2) 20 | go print("goroutine-2", ch2, ch3) 21 | go print("goroutine-3", ch3, ch1) 22 | wg.Wait() 23 | 24 | end := time.Now().Unix() 25 | fmt.Printf("duration: %d\n", end-start) 26 | } 27 | 28 | func print(g string, inchan chan struct{}, outchan chan struct{}) { 29 | time.Sleep(1 * time.Second) 30 | select { 31 | case <-inchan: 32 | fmt.Println(g) 33 | outchan <- struct{}{} 34 | } 35 | wg.Done() 36 | } 37 | -------------------------------------------------------------------------------- /grammar/context/cancel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "time" 7 | ) 8 | 9 | func ctxWithCancel() { 10 | ctx, cancel := context.WithCancel(context.TODO()) 11 | timeCancel := time.Now() 12 | 13 | go func() { 14 | time.Sleep(100 * time.Millisecond) 15 | cancel() 16 | }() 17 | 18 | select { 19 | case <-ctx.Done(): 20 | timeEnd := time.Now() 21 | log.Println(timeEnd.Sub(timeCancel).Milliseconds()) // 100 22 | err := ctx.Err() 23 | log.Println("Error:", err) 24 | } 25 | } 26 | 27 | func main() { 28 | ctxWithCancel() 29 | } 30 | -------------------------------------------------------------------------------- /grammar/context/done/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "time" 7 | ) 8 | 9 | func funcWithTimeoutParent() { 10 | parent, cancel1 := context.WithTimeout(context.TODO(), 1000*time.Millisecond) 11 | defer cancel1() 12 | timeParent := time.Now() 13 | 14 | time.Sleep(500 * time.Millisecond) // wait 500ms after start child process 15 | 16 | child, cancel2 := context.WithTimeout(parent, 1000*time.Millisecond) // 499 = 1000 - 500 17 | defer cancel2() 18 | timeChilld := time.Now() 19 | 20 | select { 21 | case <-child.Done(): 22 | err := child.Err() 23 | timeEnd := time.Now() 24 | log.Println(timeEnd.Sub(timeParent).Milliseconds(), timeEnd.Sub(timeChilld).Milliseconds()) // 1000, 499 25 | log.Println("Error:", err) 26 | } 27 | } 28 | 29 | func main() { 30 | funcWithTimeoutParent() 31 | } 32 | -------------------------------------------------------------------------------- /grammar/context/inheritance/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | ) 7 | 8 | func step1(ctx context.Context) context.Context { 9 | child := context.WithValue(ctx, "name", "enbin") // create context with key and value 10 | return child 11 | } 12 | 13 | func step2(ctx context.Context) context.Context { 14 | child := context.WithValue(ctx, "age", 23) 15 | return child 16 | } 17 | 18 | func step3(ctx context.Context) { 19 | log.Printf("name: %s\n", ctx.Value("name")) 20 | log.Printf("age: %d\n", ctx.Value("age")) 21 | } 22 | 23 | func main() { 24 | grandpa := context.TODO() 25 | father := step1(grandpa) 26 | grandson := step2(father) 27 | step3(grandson) 28 | } 29 | -------------------------------------------------------------------------------- /grammar/goroutine/goroutine.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | go count("goroutine 1", 5) 10 | go count("goroutine 2", 5) 11 | 12 | time.Sleep(6 * time.Second) 13 | 14 | fmt.Println("Exit") 15 | } 16 | 17 | func count(name string, n int) { 18 | for i := 1; i <= n; i++ { 19 | fmt.Println(name, ":", i) 20 | time.Sleep(1 * time.Second) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /grammar/goroutine_recover/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "time" 6 | ) 7 | 8 | func foo() { 9 | defer func() { 10 | err := recover() // catch panic and print log 11 | if err != nil { 12 | log.Println("panic:", err) // logging 13 | } 14 | }() 15 | a, b := 3, 0 16 | log.Println(a, b) 17 | _ = a / b // panic 18 | log.Println("foo finish") 19 | } 20 | 21 | func main() { 22 | go foo() 23 | time.Sleep(1 * time.Second) 24 | log.Println("main finish") 25 | } 26 | -------------------------------------------------------------------------------- /grammar/interface/basic/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Operation interface { 6 | getSum(int, int) int 7 | } 8 | 9 | type Robot struct{} 10 | 11 | func (r Robot) getSum(a int, b int) int { 12 | return a + b 13 | } 14 | 15 | func foo(r Operation) { 16 | res := r.getSum(1, 1) 17 | fmt.Println("sum =", res) 18 | } 19 | 20 | func main() { 21 | var r Operation 22 | r = Robot{} 23 | foo(r) 24 | } 25 | -------------------------------------------------------------------------------- /grammar/interface/flow_restriction/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "sync/atomic" 6 | "time" 7 | ) 8 | 9 | var ( 10 | concurrent int32 11 | concurrentLimit = make(chan struct{}, 10) // concurrency is 10 12 | ) 13 | 14 | func readDB() bool { 15 | atomic.AddInt32(&concurrent, 1) 16 | log.Println("readDB() concurrency:", atomic.LoadInt32(&concurrent)) 17 | time.Sleep(200 * time.Millisecond) // read data from DataBase 18 | atomic.AddInt32(&concurrent, -1) 19 | return true 20 | } 21 | 22 | func handler() { 23 | concurrentLimit <- struct{}{} // flow restriction 24 | readDB() 25 | <-concurrentLimit 26 | } 27 | 28 | func main() { 29 | for i := 1; i <= 100; i++ { 30 | go handler() 31 | } 32 | time.Sleep(10 * time.Second) 33 | } 34 | -------------------------------------------------------------------------------- /grammar/interface/latency/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "time" 6 | ) 7 | 8 | func handler(a int, b int) string { 9 | timeStart := time.Now() 10 | // calculate interface latency using defer anonymous functions 11 | defer func() { 12 | log.Println("latency:", time.Since(timeStart).Milliseconds()) 13 | }() 14 | 15 | if a > b { 16 | time.Sleep(100 * time.Millisecond) 17 | return "a" 18 | } else { 19 | time.Sleep(200 * time.Millisecond) 20 | return "b" 21 | } 22 | } 23 | 24 | func main() { 25 | handler(9, 7) 26 | } 27 | -------------------------------------------------------------------------------- /grammar/interface/timeout/README.md: -------------------------------------------------------------------------------- 1 | # Timeout 2 | 3 | Server 4 | ```bash 5 | go run grammar/interface/timeout/main.go 6 | ``` 7 | 8 | Client (start by `curl` command) 9 | ```bash 10 | curl http://localhost:7788/ 11 | ``` -------------------------------------------------------------------------------- /grammar/interface/timeout/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | ) 7 | 8 | func readDB() string { 9 | time.Sleep(200 * time.Millisecond) // service 10 | return "success\n" 11 | } 12 | 13 | func readDBInterface(w http.ResponseWriter, req *http.Request) { 14 | var rsp string 15 | 16 | done := make(chan struct{}, 1) 17 | go func() { 18 | rsp = readDB() 19 | done <- struct{}{} // write data after job done 20 | }() 21 | 22 | select { 23 | case <-done: 24 | case <-time.After(100 * time.Millisecond): 25 | rsp = "timeout\n" // timeout: 100ms 26 | } 27 | 28 | w.Write([]byte(rsp)) 29 | } 30 | 31 | func main() { 32 | http.HandleFunc("/", readDBInterface) // root path 33 | http.ListenAndServe("localhost:7788", nil) // bind IP and Port 34 | } 35 | -------------------------------------------------------------------------------- /grammar/map/map.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func processMap(data map[string]int) { 6 | fmt.Println("processMap") 7 | } 8 | 9 | func main() { 10 | ages := map[string]int{ 11 | "Alice": 25, 12 | "Bob": 30, 13 | "Carol": 35, 14 | } 15 | ages["Dave"] = 40 16 | 17 | scores := make(map[string]int) 18 | scores["Alice"] = 99 19 | 20 | fmt.Println(ages["Alice"], scores["Alice"]) 21 | 22 | delete(ages, "Dave") 23 | 24 | age, ok := ages["Alice"] 25 | if ok { 26 | fmt.Println("Alice's age is ", age) 27 | } else { 28 | fmt.Println("Alice not found") 29 | } 30 | 31 | for name, age := range ages { 32 | fmt.Println(name, age) 33 | } 34 | 35 | fmt.Println(len((ages))) 36 | 37 | processMap(scores) 38 | } 39 | -------------------------------------------------------------------------------- /grammar/mutex/mutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | var ( 9 | sum = 0 10 | mutex sync.Mutex 11 | wg sync.WaitGroup 12 | ) 13 | 14 | func main() { 15 | wg.Add(5) 16 | 17 | go addToSum(1) 18 | go addToSum(2) 19 | go addToSum(3) 20 | go addToSum(4) 21 | go addToSum(5) 22 | 23 | wg.Wait() 24 | fmt.Println("sum:", sum) 25 | } 26 | 27 | func addToSum(num int) { 28 | mutex.Lock() 29 | sum += num 30 | mutex.Unlock() 31 | wg.Done() 32 | } 33 | -------------------------------------------------------------------------------- /grammar/slice/slice.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func processSlice(number []int) { 6 | fmt.Println(number[0]) 7 | } 8 | 9 | func main() { 10 | numbers := []int{1, 2, 3, 4, 5} 11 | numbers = append(numbers, 6, 7) 12 | 13 | dst := make([]int, len(numbers)) 14 | copy(dst, numbers) 15 | 16 | fmt.Println(len(dst)) 17 | fmt.Println(cap(dst)) 18 | 19 | for index, value := range dst { 20 | fmt.Println(index, value) 21 | } 22 | 23 | processSlice(dst) 24 | 25 | matrix := [][]int{ 26 | {1, 2, 3}, 27 | {4, 5, 6}, 28 | {7, 8, 9}, 29 | } 30 | for _, row := range matrix { 31 | for _, value := range row { 32 | fmt.Println(value) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /grammar/sync_once_singleton/sync_once_singleton.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | type Singleton struct { 9 | Name string 10 | } 11 | 12 | var instance *Singleton 13 | var once sync.Once 14 | 15 | func getInstance() *Singleton { 16 | once.Do(func() { 17 | instance = &Singleton{Name: "Singleton Instance"} 18 | }) 19 | return instance 20 | } 21 | 22 | func main() { 23 | s1 := getInstance() 24 | s2 := getInstance() 25 | fmt.Println(s1 == s2) 26 | } 27 | -------------------------------------------------------------------------------- /grpc/api.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.30.0 4 | // protoc v3.18.0 5 | // source: api.proto 6 | 7 | package __ 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 | reflect "reflect" 13 | sync "sync" 14 | ) 15 | 16 | const ( 17 | // Verify that this generated code is sufficiently up-to-date. 18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 19 | // Verify that runtime/protoimpl is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 21 | ) 22 | 23 | type CallbackReq struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | TaskId string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` 29 | Data int32 `protobuf:"varint,2,opt,name=data,proto3" json:"data,omitempty"` 30 | } 31 | 32 | func (x *CallbackReq) Reset() { 33 | *x = CallbackReq{} 34 | if protoimpl.UnsafeEnabled { 35 | mi := &file_api_proto_msgTypes[0] 36 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 37 | ms.StoreMessageInfo(mi) 38 | } 39 | } 40 | 41 | func (x *CallbackReq) String() string { 42 | return protoimpl.X.MessageStringOf(x) 43 | } 44 | 45 | func (*CallbackReq) ProtoMessage() {} 46 | 47 | func (x *CallbackReq) ProtoReflect() protoreflect.Message { 48 | mi := &file_api_proto_msgTypes[0] 49 | if protoimpl.UnsafeEnabled && x != nil { 50 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 51 | if ms.LoadMessageInfo() == nil { 52 | ms.StoreMessageInfo(mi) 53 | } 54 | return ms 55 | } 56 | return mi.MessageOf(x) 57 | } 58 | 59 | // Deprecated: Use CallbackReq.ProtoReflect.Descriptor instead. 60 | func (*CallbackReq) Descriptor() ([]byte, []int) { 61 | return file_api_proto_rawDescGZIP(), []int{0} 62 | } 63 | 64 | func (x *CallbackReq) GetTaskId() string { 65 | if x != nil { 66 | return x.TaskId 67 | } 68 | return "" 69 | } 70 | 71 | func (x *CallbackReq) GetData() int32 { 72 | if x != nil { 73 | return x.Data 74 | } 75 | return 0 76 | } 77 | 78 | type CallbackRsp struct { 79 | state protoimpl.MessageState 80 | sizeCache protoimpl.SizeCache 81 | unknownFields protoimpl.UnknownFields 82 | 83 | Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` 84 | } 85 | 86 | func (x *CallbackRsp) Reset() { 87 | *x = CallbackRsp{} 88 | if protoimpl.UnsafeEnabled { 89 | mi := &file_api_proto_msgTypes[1] 90 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 91 | ms.StoreMessageInfo(mi) 92 | } 93 | } 94 | 95 | func (x *CallbackRsp) String() string { 96 | return protoimpl.X.MessageStringOf(x) 97 | } 98 | 99 | func (*CallbackRsp) ProtoMessage() {} 100 | 101 | func (x *CallbackRsp) ProtoReflect() protoreflect.Message { 102 | mi := &file_api_proto_msgTypes[1] 103 | if protoimpl.UnsafeEnabled && x != nil { 104 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 105 | if ms.LoadMessageInfo() == nil { 106 | ms.StoreMessageInfo(mi) 107 | } 108 | return ms 109 | } 110 | return mi.MessageOf(x) 111 | } 112 | 113 | // Deprecated: Use CallbackRsp.ProtoReflect.Descriptor instead. 114 | func (*CallbackRsp) Descriptor() ([]byte, []int) { 115 | return file_api_proto_rawDescGZIP(), []int{1} 116 | } 117 | 118 | func (x *CallbackRsp) GetCode() int32 { 119 | if x != nil { 120 | return x.Code 121 | } 122 | return 0 123 | } 124 | 125 | var File_api_proto protoreflect.FileDescriptor 126 | 127 | var file_api_proto_rawDesc = []byte{ 128 | 0x0a, 0x09, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3a, 0x0a, 0x0b, 0x43, 129 | 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x61, 130 | 0x73, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x73, 131 | 0x6b, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 132 | 0x05, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x21, 0x0a, 0x0b, 0x43, 0x61, 0x6c, 0x6c, 0x62, 133 | 0x61, 0x63, 0x6b, 0x52, 0x73, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 134 | 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x32, 0x3b, 0x0a, 0x0f, 0x43, 0x61, 135 | 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x28, 0x0a, 136 | 0x08, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x0c, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 137 | 0x62, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x62, 0x61, 138 | 0x63, 0x6b, 0x52, 0x73, 0x70, 0x22, 0x00, 0x42, 0x03, 0x5a, 0x01, 0x2e, 0x62, 0x06, 0x70, 0x72, 139 | 0x6f, 0x74, 0x6f, 0x33, 140 | } 141 | 142 | var ( 143 | file_api_proto_rawDescOnce sync.Once 144 | file_api_proto_rawDescData = file_api_proto_rawDesc 145 | ) 146 | 147 | func file_api_proto_rawDescGZIP() []byte { 148 | file_api_proto_rawDescOnce.Do(func() { 149 | file_api_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_proto_rawDescData) 150 | }) 151 | return file_api_proto_rawDescData 152 | } 153 | 154 | var file_api_proto_msgTypes = make([]protoimpl.MessageInfo, 2) 155 | var file_api_proto_goTypes = []interface{}{ 156 | (*CallbackReq)(nil), // 0: CallbackReq 157 | (*CallbackRsp)(nil), // 1: CallbackRsp 158 | } 159 | var file_api_proto_depIdxs = []int32{ 160 | 0, // 0: CallbackService.Callback:input_type -> CallbackReq 161 | 1, // 1: CallbackService.Callback:output_type -> CallbackRsp 162 | 1, // [1:2] is the sub-list for method output_type 163 | 0, // [0:1] is the sub-list for method input_type 164 | 0, // [0:0] is the sub-list for extension type_name 165 | 0, // [0:0] is the sub-list for extension extendee 166 | 0, // [0:0] is the sub-list for field type_name 167 | } 168 | 169 | func init() { file_api_proto_init() } 170 | func file_api_proto_init() { 171 | if File_api_proto != nil { 172 | return 173 | } 174 | if !protoimpl.UnsafeEnabled { 175 | file_api_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 176 | switch v := v.(*CallbackReq); i { 177 | case 0: 178 | return &v.state 179 | case 1: 180 | return &v.sizeCache 181 | case 2: 182 | return &v.unknownFields 183 | default: 184 | return nil 185 | } 186 | } 187 | file_api_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 188 | switch v := v.(*CallbackRsp); i { 189 | case 0: 190 | return &v.state 191 | case 1: 192 | return &v.sizeCache 193 | case 2: 194 | return &v.unknownFields 195 | default: 196 | return nil 197 | } 198 | } 199 | } 200 | type x struct{} 201 | out := protoimpl.TypeBuilder{ 202 | File: protoimpl.DescBuilder{ 203 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 204 | RawDescriptor: file_api_proto_rawDesc, 205 | NumEnums: 0, 206 | NumMessages: 2, 207 | NumExtensions: 0, 208 | NumServices: 1, 209 | }, 210 | GoTypes: file_api_proto_goTypes, 211 | DependencyIndexes: file_api_proto_depIdxs, 212 | MessageInfos: file_api_proto_msgTypes, 213 | }.Build() 214 | File_api_proto = out.File 215 | file_api_proto_rawDesc = nil 216 | file_api_proto_goTypes = nil 217 | file_api_proto_depIdxs = nil 218 | } 219 | -------------------------------------------------------------------------------- /grpc/api.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "."; 4 | 5 | service CallbackService { 6 | rpc Callback(CallbackReq) returns (CallbackRsp) {} 7 | } 8 | 9 | message CallbackReq { 10 | string task_id = 1; 11 | int32 data = 2; 12 | } 13 | 14 | message CallbackRsp { 15 | int32 code = 1; 16 | } -------------------------------------------------------------------------------- /grpc/api_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.3.0 4 | // - protoc v3.18.0 5 | // source: api.proto 6 | 7 | package __ 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | const ( 22 | CallbackService_Callback_FullMethodName = "/CallbackService/Callback" 23 | ) 24 | 25 | // CallbackServiceClient is the client API for CallbackService service. 26 | // 27 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 28 | type CallbackServiceClient interface { 29 | Callback(ctx context.Context, in *CallbackReq, opts ...grpc.CallOption) (*CallbackRsp, error) 30 | } 31 | 32 | type callbackServiceClient struct { 33 | cc grpc.ClientConnInterface 34 | } 35 | 36 | func NewCallbackServiceClient(cc grpc.ClientConnInterface) CallbackServiceClient { 37 | return &callbackServiceClient{cc} 38 | } 39 | 40 | func (c *callbackServiceClient) Callback(ctx context.Context, in *CallbackReq, opts ...grpc.CallOption) (*CallbackRsp, error) { 41 | out := new(CallbackRsp) 42 | err := c.cc.Invoke(ctx, CallbackService_Callback_FullMethodName, in, out, opts...) 43 | if err != nil { 44 | return nil, err 45 | } 46 | return out, nil 47 | } 48 | 49 | // CallbackServiceServer is the server API for CallbackService service. 50 | // All implementations must embed UnimplementedCallbackServiceServer 51 | // for forward compatibility 52 | type CallbackServiceServer interface { 53 | Callback(context.Context, *CallbackReq) (*CallbackRsp, error) 54 | mustEmbedUnimplementedCallbackServiceServer() 55 | } 56 | 57 | // UnimplementedCallbackServiceServer must be embedded to have forward compatible implementations. 58 | type UnimplementedCallbackServiceServer struct { 59 | } 60 | 61 | func (UnimplementedCallbackServiceServer) Callback(context.Context, *CallbackReq) (*CallbackRsp, error) { 62 | return nil, status.Errorf(codes.Unimplemented, "method Callback not implemented") 63 | } 64 | func (UnimplementedCallbackServiceServer) mustEmbedUnimplementedCallbackServiceServer() {} 65 | 66 | // UnsafeCallbackServiceServer may be embedded to opt out of forward compatibility for this service. 67 | // Use of this interface is not recommended, as added methods to CallbackServiceServer will 68 | // result in compilation errors. 69 | type UnsafeCallbackServiceServer interface { 70 | mustEmbedUnimplementedCallbackServiceServer() 71 | } 72 | 73 | func RegisterCallbackServiceServer(s grpc.ServiceRegistrar, srv CallbackServiceServer) { 74 | s.RegisterService(&CallbackService_ServiceDesc, srv) 75 | } 76 | 77 | func _CallbackService_Callback_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 78 | in := new(CallbackReq) 79 | if err := dec(in); err != nil { 80 | return nil, err 81 | } 82 | if interceptor == nil { 83 | return srv.(CallbackServiceServer).Callback(ctx, in) 84 | } 85 | info := &grpc.UnaryServerInfo{ 86 | Server: srv, 87 | FullMethod: CallbackService_Callback_FullMethodName, 88 | } 89 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 90 | return srv.(CallbackServiceServer).Callback(ctx, req.(*CallbackReq)) 91 | } 92 | return interceptor(ctx, in, info, handler) 93 | } 94 | 95 | // CallbackService_ServiceDesc is the grpc.ServiceDesc for CallbackService service. 96 | // It's only intended for direct use with grpc.RegisterService, 97 | // and not to be introspected or modified (even as a copy) 98 | var CallbackService_ServiceDesc = grpc.ServiceDesc{ 99 | ServiceName: "CallbackService", 100 | HandlerType: (*CallbackServiceServer)(nil), 101 | Methods: []grpc.MethodDesc{ 102 | { 103 | MethodName: "Callback", 104 | Handler: _CallbackService_Callback_Handler, 105 | }, 106 | }, 107 | Streams: []grpc.StreamDesc{}, 108 | Metadata: "api.proto", 109 | } 110 | -------------------------------------------------------------------------------- /grpc/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | pb "grammar/grpc" 6 | "log" 7 | "time" 8 | 9 | "google.golang.org/grpc" 10 | ) 11 | 12 | func main() { 13 | // 建立与 gRPC 服务器的连接 14 | conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure()) 15 | if err != nil { 16 | log.Fatalf("Failed to connect to server: %v", err) 17 | } 18 | defer conn.Close() 19 | 20 | // 创建客户端 21 | client := pb.NewCallbackServiceClient(conn) 22 | 23 | // 创建回调请求 24 | req := &pb.CallbackReq{ 25 | TaskId: "123456", 26 | Data: 0, 27 | } 28 | 29 | // 设置上下文超时 30 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 31 | defer cancel() 32 | 33 | // 发起回调 34 | rsp, err := client.Callback(ctx, req) 35 | if err != nil { 36 | log.Fatalf("Failed to send callback request: %v", err) 37 | } 38 | 39 | log.Printf("Callback response: code=%d", rsp.Code) 40 | } 41 | -------------------------------------------------------------------------------- /grpc/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | pb "grammar/grpc" 7 | "log" 8 | "net" 9 | 10 | "google.golang.org/grpc" 11 | ) 12 | 13 | type CallbackServer struct { 14 | *pb.UnimplementedCallbackServiceServer 15 | } 16 | 17 | func (s *CallbackServer) Callback(ctx context.Context, req *pb.CallbackReq) (*pb.CallbackRsp, error) { 18 | fmt.Printf("Received callback request for task id: %s\n", req.TaskId) 19 | return &pb.CallbackRsp{Code: 1}, nil 20 | } 21 | 22 | func main() { 23 | // 创建 gRPC 服务器 24 | server := grpc.NewServer() 25 | 26 | // 注册回调服务 27 | pb.RegisterCallbackServiceServer(server, &CallbackServer{}) 28 | 29 | // 监听端口并启动服务器 30 | listener, err := net.Listen("tcp", ":50051") 31 | if err != nil { 32 | log.Fatalf("Failed to listen: %v", err) 33 | } 34 | if err := server.Serve(listener); err != nil { 35 | log.Fatalf("Failed to serve: %v", err) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /interview/deeproute.ai/concurrent_printout/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func main() { 9 | ch := make(chan struct{}, 5) 10 | 11 | var wg sync.WaitGroup 12 | 13 | for i := 1; i <= 10; i++ { 14 | wg.Add(1) 15 | 16 | go func(num int) { 17 | <-ch 18 | fmt.Println(num) 19 | wg.Done() 20 | }(i) 21 | } 22 | 23 | close(ch) 24 | 25 | wg.Wait() 26 | } 27 | -------------------------------------------------------------------------------- /interview/deeproute.ai/context-timeout-retry.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 11 | defer cancel() 12 | 13 | go func() { 14 | retryCount := 0 15 | maxRetry := 3 16 | 17 | for { 18 | select { 19 | case <-ctx.Done(): 20 | fmt.Println("timeout: stop retry") 21 | return 22 | default: 23 | if retryCount < maxRetry { 24 | fmt.Println("retry time:", retryCount+1) 25 | retryCount++ 26 | time.Sleep(1 * time.Second) 27 | } else { 28 | fmt.Println("timeout: stop retry") 29 | return 30 | } 31 | } 32 | } 33 | }() 34 | 35 | time.Sleep(10 * time.Second) 36 | 37 | cancel() 38 | 39 | time.Sleep(1 * time.Second) 40 | fmt.Println("exit") 41 | } 42 | -------------------------------------------------------------------------------- /interview/deeproute.ai/context_timeout_retry/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 11 | defer cancel() 12 | 13 | go func() { 14 | retryCount := 0 15 | maxRetry := 3 16 | 17 | for { 18 | select { 19 | case <-ctx.Done(): 20 | fmt.Println("timeout: stop retry") 21 | return 22 | default: 23 | if retryCount < maxRetry { 24 | fmt.Println("retry time:", retryCount+1) 25 | retryCount++ 26 | time.Sleep(1 * time.Second) 27 | } else { 28 | fmt.Println("timeout: stop retry") 29 | return 30 | } 31 | } 32 | } 33 | }() 34 | 35 | time.Sleep(10 * time.Second) 36 | 37 | cancel() 38 | 39 | time.Sleep(1 * time.Second) 40 | fmt.Println("exit") 41 | } 42 | -------------------------------------------------------------------------------- /kafka/README.md: -------------------------------------------------------------------------------- 1 | # Kafka 2 | 3 | Required to install Java, Zookeeper, and Kafka. You need to start Zookeeper first to use Kafka. 4 | 5 | Start Zookeeper 6 | ```bash 7 | /opt/zookeeper/bin/zkServer.sh start 8 | ``` 9 | 10 | Start Kafka 11 | ```bash 12 | nohup bin/kafka-server-start.sh config/server.properties & 13 | ``` 14 | 15 | Create a Kafka Topic 16 | ```bash 17 | bin/kafka-topics.sh --create --topic test --bootstrap-server localhost:9092 --partitions 1 --replication-factor 1 18 | ``` 19 | 20 | List all Topic in Kafka 21 | ```bash 22 | bin/kafka-topics.sh --list --bootstrap-server localhost:9092 23 | ``` -------------------------------------------------------------------------------- /kafka/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | "os/signal" 8 | "syscall" 9 | "time" 10 | 11 | "github.com/segmentio/kafka-go" 12 | ) 13 | 14 | var ( 15 | reader *kafka.Reader 16 | topic = "user_click" 17 | ) 18 | 19 | func writeKafka(ctx context.Context) { 20 | writer := &kafka.Writer{ 21 | Addr: kafka.TCP("localhost:9092"), // broker addr 22 | Topic: topic, 23 | Balancer: &kafka.Hash{}, 24 | WriteTimeout: 1 * time.Second, 25 | RequiredAcks: kafka.RequireNone, 26 | AllowAutoTopicCreation: true, 27 | } 28 | defer writer.Close() 29 | 30 | // max retry three times 31 | for i := 0; i < 3; i++ { 32 | if err := writer.WriteMessages( 33 | ctx, 34 | kafka.Message{Key: []byte("1"), Value: []byte("enbin")}, 35 | kafka.Message{Key: []byte("2"), Value: []byte("yang")}, 36 | kafka.Message{Key: []byte("3"), Value: []byte("shaun")}, 37 | kafka.Message{Key: []byte("1"), Value: []byte("hugo")}, 38 | kafka.Message{Key: []byte("1"), Value: []byte("robot")}, 39 | ); err != nil { 40 | if err == kafka.LeaderNotAvailable { 41 | log.Println("Leader not available!") 42 | time.Sleep(500 * time.Millisecond) 43 | continue 44 | } else { 45 | log.Println("Error writing Kafka:", err) 46 | } 47 | } else { 48 | log.Println("Writing Kafka successful! Topic:", topic) 49 | break 50 | } 51 | } 52 | } 53 | 54 | func readKafka(ctx context.Context) { 55 | reader := kafka.NewReader(kafka.ReaderConfig{ 56 | Brokers: []string{"localhost:9092"}, 57 | Topic: topic, 58 | CommitInterval: 1 * time.Second, 59 | GroupID: "ai_team", 60 | StartOffset: kafka.FirstOffset, 61 | }) 62 | 63 | for { 64 | if msg, err := reader.ReadMessage(ctx); err != nil { 65 | log.Println("Error reading Kafka:", err) 66 | break 67 | } else { 68 | log.Printf("topic=%s, partition=%d, offset=%d, key=%s, value=%s\n", msg.Topic, msg.Partition, msg.Offset, msg.Key, msg.Value) 69 | time.Sleep(1 * time.Second) 70 | } 71 | } 72 | } 73 | 74 | func listenSignal() { 75 | c := make(chan os.Signal, 1) 76 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) // ctrl+c && kill signal 77 | sig := <-c 78 | log.Printf("Catch signal %s", sig.String()) 79 | if reader != nil { 80 | reader.Close() 81 | } 82 | os.Exit(0) 83 | } 84 | 85 | func main() { 86 | ctx := context.Background() 87 | writeKafka(ctx) 88 | readKafka(ctx) 89 | } 90 | -------------------------------------------------------------------------------- /multi_thread/async_get_age/async_get_age.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type CallbackFunc func(userId int, result string) 9 | 10 | type Response struct { 11 | userId int 12 | result string 13 | } 14 | 15 | func getUserAgeAsync(userId int, callback CallbackFunc) { 16 | fmt.Printf("proceeding task {id = %d}\n", userId) 17 | time.Sleep(3 * time.Second) 18 | 19 | age := fmt.Sprintf("%d", userId) 20 | fmt.Printf("get task {id = %d} result suc\n", userId) 21 | 22 | callback(userId, age) 23 | } 24 | 25 | func handleCallbackResult(userId int, result string) { 26 | fmt.Printf("async done, user id = %d, age = %s\n", userId, result) 27 | } 28 | 29 | func main() { 30 | resultChan := make(chan Response) 31 | 32 | userIds := []int{20, 25, 30, 35} 33 | for _, userId := range userIds { 34 | go getUserAgeAsync(userId, func(userId int, result string) { 35 | rsp := Response{ 36 | userId: userId, 37 | result: result, 38 | } 39 | resultChan <- rsp 40 | }) 41 | } 42 | 43 | fmt.Println("proceeding other operation...") 44 | time.Sleep(5 * time.Second) 45 | 46 | for range userIds { 47 | rsp := <-resultChan 48 | handleCallbackResult(rsp.userId, rsp.result) 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /multi_thread/async_get_age_with_timeout/async_get_age_with_timeout.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | type CallbackFunc func(userId int, result string) 10 | 11 | func getUserAgeAsync(ctx context.Context, userId int, callback CallbackFunc) { 12 | ctx, cancel := context.WithTimeout(ctx, 2*time.Second) // timeout is two seconds 13 | defer cancel() 14 | 15 | fmt.Printf("proceeding task {id = %d}\n", userId) 16 | time.Sleep(3 * time.Second) // operation execution time is three seconds 17 | 18 | select { 19 | case <-time.After(3 * time.Second): 20 | age := fmt.Sprintf("%d", userId) 21 | fmt.Printf("get task {id = %d} result suc\n", userId) 22 | callback(userId, age) 23 | 24 | case <-ctx.Done(): 25 | if ctx.Err() != nil { 26 | fmt.Printf("error: get task {id = %d} timeout\n", userId) 27 | } 28 | } 29 | } 30 | 31 | func handleCallbackResult(userId int, result string) { 32 | fmt.Printf("async done, user id = %d, age = %s\n", userId, result) 33 | } 34 | 35 | func main() { 36 | ctx := context.Background() 37 | 38 | userIds := []int{20, 25, 30, 35} 39 | for _, userId := range userIds { 40 | go getUserAgeAsync(ctx, userId, func(userId int, result string) { 41 | handleCallbackResult(userId, result) 42 | }) 43 | } 44 | 45 | fmt.Println("proceeding other operation...") 46 | time.Sleep(5 * time.Second) 47 | } 48 | 49 | -------------------------------------------------------------------------------- /redis/basic_operation/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "time" 7 | 8 | "github.com/go-redis/redis/v8" 9 | ) 10 | 11 | func stringOperation(ctx context.Context, client *redis.Client) { 12 | key := "basic:enbin" 13 | value := "lol" 14 | 15 | // set 16 | if err := client.Set(ctx, key, value, 1*time.Second).Err(); err != nil { 17 | log.Println("Error setting Redis:", err) 18 | } 19 | 20 | // expire 21 | client.Expire(ctx, key, 3*time.Second) 22 | 23 | // get 24 | if value, err := client.Get(ctx, key).Result(); err != nil { 25 | log.Println("Error getting from Redis:", err) 26 | } else { 27 | log.Println("String value:", value) 28 | } 29 | 30 | // delete 31 | client.Del(ctx, key) 32 | } 33 | 34 | func listOperation(ctx context.Context, client *redis.Client) { 35 | key := "task_id" 36 | values := []interface{}{1, 2, 3} 37 | 38 | // RPush 39 | if err := client.RPush(ctx, key, values...).Err(); err != nil { 40 | log.Println("Error RPush to Redis:", err) 41 | } 42 | 43 | // LRange 44 | if value, err := client.LRange(ctx, key, 0, -1).Result(); err != nil { 45 | log.Println("Error LRange Redis:", err) 46 | } else { 47 | log.Println("LRange value:", value) 48 | } 49 | 50 | // delete 51 | client.Del(ctx, key) 52 | } 53 | 54 | func hashtableOperation(ctx context.Context, client *redis.Client) { 55 | // HSet 56 | if err := client.HSet(ctx, "id:258", "Name", "enbin", "Age", 23, "Height", 178).Err(); err != nil { 57 | log.Println("Error HSet Redis:", err) 58 | } 59 | if err := client.HSet(ctx, "id:369", "Name", "shaun", "Age", 99, "Height", 160).Err(); err != nil { 60 | log.Println("Error HSet Redis:", err) 61 | } 62 | 63 | // HGet 64 | if value, err := client.HGet(ctx, "id:258", "Age").Result(); err != nil { 65 | log.Println("Error HGet Redis:", err) 66 | } else { 67 | log.Println("HGet value:", value) 68 | } 69 | 70 | // HGetAll 71 | for field, value := range client.HGetAll(ctx, "id:369").Val() { 72 | log.Println(field, value) 73 | } 74 | 75 | // delete 76 | client.Del(ctx, "id:258") // key: id:258 77 | client.Del(ctx, "id:369") 78 | } 79 | 80 | func main() { 81 | client := redis.NewClient(&redis.Options{ 82 | Addr: "localhost:6379", 83 | Password: "1", 84 | DB: 0, 85 | }) 86 | ctx := context.Background() // null context 87 | 88 | stringOperation(ctx, client) 89 | listOperation(ctx, client) 90 | hashtableOperation(ctx, client) 91 | } 92 | -------------------------------------------------------------------------------- /redis/bloom_filter/bloom_filter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/go-redis/redis/v8" 9 | "github.com/willf/bloom" 10 | "gorm.io/driver/mysql" 11 | "gorm.io/gorm" 12 | ) 13 | 14 | func main() { 15 | ctx := context.Background() 16 | 17 | redisClient := redis.NewClient(&redis.Options{ 18 | Addr: "localhost:6379", 19 | Password: "1", 20 | DB: 0, 21 | }) 22 | defer redisClient.Close() 23 | 24 | conn := "debian-sys-maint:P7g9fAYfTM4YhtoC@tcp(localhost:3306)/webserver" 25 | db, err := gorm.Open(mysql.Open(conn), &gorm.Config{}) 26 | if err != nil { 27 | log.Fatal("Error connecting to MySQL:", err) 28 | } 29 | 30 | // create bloom filter 31 | bf := bloom.NewWithEstimates(1000000, 0.01) 32 | 33 | // select data from MySQL 34 | rows, err := db.Raw("SELECT username, password FROM user").Rows() 35 | if err != nil { 36 | log.Fatal("Error executing MySQL query:", err) 37 | } 38 | defer rows.Close() 39 | 40 | for rows.Next() { 41 | var uname string 42 | var password string 43 | err := rows.Scan(&uname, &password) 44 | if err != nil { 45 | log.Fatal("Error scanning MySQL row:", err) 46 | continue 47 | } 48 | 49 | // insert data into Redis 50 | err = redisClient.Set(ctx, fmt.Sprintf("username:%s", uname), password, 0).Err() 51 | if err != nil { 52 | log.Fatal("Error setting Redis:", err) 53 | continue 54 | } 55 | 56 | // insert data into bloom filter 57 | bf.Add([]byte(fmt.Sprintf("%s", uname))) 58 | } 59 | 60 | queryName := "enbin" 61 | var password string 62 | if bf.Test([]byte(fmt.Sprintf("%s", queryName))) { 63 | // bloom filter hit 64 | password, err = redisClient.Get(ctx, fmt.Sprintf("username:%s", queryName)).Result() 65 | if err == redis.Nil { 66 | log.Println("Data not found in Redis, key:", queryName) 67 | // query data from MySQL 68 | err = db.Raw("SELECT password FROM user WHERE username = ?", queryName).Scan(&password).Error 69 | if err != nil { 70 | log.Fatal("Error querying MySQL:", err) 71 | } else { 72 | log.Println("Data found in MySQL:", password) 73 | } 74 | } else if err != nil { 75 | log.Fatal("Error querying Redis:", err) 76 | } else { 77 | log.Println("Data found in Redis:", password) 78 | } 79 | } else { 80 | // NOT FOUND 81 | log.Println("Data not found") 82 | } 83 | 84 | // close MySQL connection 85 | dbSQL, err := db.DB() 86 | if err != nil { 87 | log.Fatal("Error closing MySQL connection:", err) 88 | } 89 | dbSQL.Close() 90 | } 91 | -------------------------------------------------------------------------------- /redis/hmset_get/hmset.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/go-redis/redis/v8" 8 | ) 9 | 10 | func main() { 11 | // 创建 Redis 客户端 12 | cli := redis.NewClient(&redis.Options{ 13 | Addr: "localhost:6379", 14 | Password: "1", 15 | DB: 0, 16 | }) 17 | 18 | data := map[string]interface{}{ 19 | "task_id": "1", 20 | "status": 0, 21 | "ctime": "2023-05-24 15:22:00", 22 | } 23 | 24 | // 插入数据 25 | err := cli.HMSet(context.Background(), "myhash", data).Err() 26 | if err != nil { 27 | fmt.Println("Failed to write data to Redis: ", err) 28 | return 29 | } 30 | 31 | fields := []string{"task_id", "status", "ctime"} 32 | 33 | values, err := cli.HMGet(context.Background(), "myhash", fields...).Result() 34 | if err != nil { 35 | fmt.Println("Failed to read data from Redis: ", err) 36 | return 37 | } 38 | 39 | for idx, field := range fields { 40 | value := values[idx] 41 | fmt.Printf("%s: %v\n", field, value) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /redis/redlock/redlock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | "github.com/go-redis/redis/v8" 10 | ) 11 | 12 | type RedLock struct { 13 | cli *redis.Client 14 | key string 15 | expiration time.Duration 16 | } 17 | 18 | func NewRedLock(cli *redis.Client, key string, expiration time.Duration) *RedLock { 19 | return &RedLock{ 20 | cli: cli, 21 | key: key, 22 | expiration: expiration, 23 | } 24 | } 25 | 26 | func (lock *RedLock) Lock() (bool, error) { 27 | ctx := context.TODO() 28 | 29 | suc, err := lock.cli.SetNX(ctx, lock.key, "locked", time.Duration(lock.expiration)).Result() 30 | if err != nil { 31 | return false, err 32 | } 33 | 34 | return suc, nil 35 | } 36 | 37 | func (lock *RedLock) UnLock() error { 38 | ctx := context.TODO() 39 | 40 | _, err := lock.cli.Del(ctx, lock.key).Result() 41 | if err != nil { 42 | return err 43 | } 44 | 45 | return nil 46 | } 47 | 48 | func main() { 49 | cli := redis.NewClient(&redis.Options{ 50 | Addr: "localhost:6379", 51 | Password: "1", 52 | DB: 0, 53 | }) 54 | 55 | ticker := time.NewTicker(2 * time.Second) 56 | lock := NewRedLock(cli, "mylock", 5*time.Second) 57 | count := 0 58 | 59 | for range ticker.C { 60 | suc, err := lock.Lock() 61 | if err != nil { 62 | log.Fatalf("Failed to acquire lock: %v", err) 63 | } 64 | if suc { 65 | fmt.Println("Lock acquired") 66 | 67 | fmt.Println("Doing") // operation 68 | 69 | err := lock.UnLock() 70 | if err != nil { 71 | log.Fatalf("Failed to release lock: %v", err) 72 | } 73 | fmt.Println("Lock released") 74 | } else { 75 | log.Fatalf("Failed to acquire lock") 76 | } 77 | 78 | count++ 79 | if count == 10 { 80 | break 81 | } 82 | } 83 | 84 | err := cli.Close() 85 | if err != nil { 86 | log.Fatalf("Failed to close Redis client: %v", err) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /redis/redlock_lua/redlock_lua.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync" 7 | "time" 8 | 9 | "github.com/go-redis/redis/v8" 10 | ) 11 | 12 | type RedisMutex struct { 13 | client *redis.Client 14 | lockKey string 15 | expiration time.Duration 16 | } 17 | 18 | func NewRedisMutex(client *redis.Client, lockKey string, expiration time.Duration) *RedisMutex { 19 | return &RedisMutex{ 20 | client: client, 21 | lockKey: lockKey, 22 | expiration: expiration, 23 | } 24 | } 25 | 26 | // 加锁 27 | func (m *RedisMutex) acquireLock(lockValue string) bool { 28 | // 循环获取锁, 获取失败则睡眠 100ms 再重试 29 | for { 30 | // SETNX 尝试获取锁, 设置过期时间为 expiration 31 | result, err := m.client.SetNX(context.Background(), m.lockKey, lockValue, m.expiration).Result() 32 | if err != nil { 33 | return false 34 | } 35 | if result { 36 | return true 37 | } 38 | time.Sleep(100 * time.Millisecond) 39 | } 40 | } 41 | 42 | // 解锁 43 | func (m *RedisMutex) releaseLock(lockValue string) bool { 44 | // 使用 Lua 脚本删除锁 (保证原子性) 45 | luaScript := ` 46 | if redis.call("GET", KEYS[1]) == ARGV[1] then 47 | return redis.call("DEL", KEYS[1]) 48 | else 49 | return 0 50 | end 51 | ` 52 | 53 | result, err := m.client.Eval(context.Background(), luaScript, []string{m.lockKey}, lockValue).Result() 54 | if err != nil || result == nil { 55 | return false 56 | } 57 | 58 | return result.(int64) == 1 59 | } 60 | 61 | // 工作线程 62 | func worker(mutex *RedisMutex, lockValue string, wg *sync.WaitGroup) { 63 | defer wg.Done() 64 | 65 | // 获取锁 66 | if mutex.acquireLock(lockValue) { 67 | fmt.Println("Lock acquired by worker", lockValue) 68 | time.Sleep(5 * time.Second) 69 | fmt.Println("Worker", lockValue, "completed") 70 | 71 | // 释放锁 72 | if mutex.releaseLock(lockValue) { 73 | fmt.Println("Lock released by worker", lockValue) 74 | } else { 75 | fmt.Println("Failed to release lock by worker", lockValue) 76 | } 77 | } else { 78 | fmt.Println("Failed to acquire lock by worker", lockValue) 79 | } 80 | } 81 | 82 | func main() { 83 | client := redis.NewClient(&redis.Options{ 84 | Addr: "localhost:6379", 85 | Password: "1", 86 | DB: 0, 87 | }) 88 | 89 | // 设置锁的键名和过期时间 90 | lockKey := "mylock" 91 | expiration := 10 * time.Second 92 | 93 | // 定义一个 Redis 分布式锁 94 | mutex := NewRedisMutex(client, lockKey, expiration) 95 | 96 | // 创建多个 Worker 并启动 97 | var wg sync.WaitGroup 98 | numWorkers := 5 99 | 100 | for i := 0; i < numWorkers; i++ { 101 | // 生成锁的唯一标识 102 | lockValue := fmt.Sprintf("worker-%d", i+1) 103 | wg.Add(1) 104 | go worker(mutex, lockValue, &wg) // 启动一个线程 105 | } 106 | 107 | // 等待所有 worker 执行完成 108 | wg.Wait() 109 | 110 | // 关闭客户端 111 | client.Close() 112 | } 113 | -------------------------------------------------------------------------------- /redis/update_redis_from_mysql/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | "sync" 8 | "time" 9 | 10 | "github.com/go-redis/redis/v8" 11 | _ "github.com/go-sql-driver/mysql" 12 | ) 13 | 14 | type Cache struct { 15 | redisClient *redis.Client 16 | lock sync.RWMutex 17 | } 18 | 19 | func NewCache() *Cache { 20 | redisClient := redis.NewClient(&redis.Options{ 21 | Addr: "localhost:6379", 22 | Password: "1", 23 | DB: 0, 24 | }) 25 | 26 | return &Cache{ 27 | redisClient: redisClient, 28 | lock: sync.RWMutex{}, 29 | } 30 | } 31 | 32 | func fetchDataFromMySQL(key string) (string, error) { 33 | conn := "debian-sys-maint:P7g9fAYfTM4YhtoC@tcp(localhost:3306)/webserver" 34 | db, err := sql.Open("mysql", conn) 35 | if err != nil { 36 | fmt.Println("MySQL error:", err) 37 | return "", err 38 | } 39 | defer db.Close() 40 | 41 | query := fmt.Sprintf("SELECT password from user WHERE username = '%s'", key) 42 | fmt.Println("Query:", query) 43 | row := db.QueryRow(query) 44 | 45 | var data string 46 | err = row.Scan(&data) 47 | if err != nil { 48 | fmt.Println("Fetch error:", err) 49 | return "", err 50 | } 51 | 52 | fmt.Printf("Fetch data from MySQL, key: %s, value: %s", key, data) 53 | return data, nil 54 | } 55 | 56 | func (c *Cache) getKeyFromRedis(ctx context.Context, key string) (string, error) { 57 | c.lock.RLock() 58 | defer c.lock.RUnlock() 59 | 60 | value, err := c.redisClient.Get(ctx, key).Result() 61 | if err == redis.Nil { 62 | value, err := fetchDataFromMySQL(key) 63 | if err != nil { 64 | return "", err 65 | } 66 | 67 | c.lock.Lock() 68 | err = c.redisClient.Set(ctx, key, value, 1).Err() 69 | c.lock.Unlock() 70 | if err != nil { 71 | return "", err 72 | } 73 | 74 | return value, nil 75 | } else if err != nil { 76 | return "", err 77 | } 78 | 79 | fmt.Printf("Fetch data from MySQL, key: %s, value: %s", key, value) 80 | return value, nil 81 | } 82 | 83 | func main() { 84 | ctx := context.Background() 85 | cache := NewCache() 86 | 87 | // 第一次获取值, 若缓存中 Key 不存在, 直接从 MySQL 获取并更新缓存 88 | value, err := cache.redisClient.Get(ctx, "enbin").Result() 89 | if err != nil { 90 | fmt.Println("Warning:", err) 91 | } else { 92 | fmt.Println("Value:", value) 93 | } 94 | 95 | // 第二次获取值, 此时缓存中 Key 不存在, 直接从 Redis 获取 96 | value, err = cache.redisClient.Get(ctx, "enbin").Result() 97 | if err != nil { 98 | fmt.Println("Warning:", err) 99 | } else { 100 | fmt.Println("Value:", value) 101 | } 102 | 103 | // 后台异步更新 104 | go func() { 105 | for { 106 | // 定时更新缓存 107 | time.Sleep(1 * time.Second) 108 | value, err := fetchDataFromMySQL("enbin") 109 | if err != nil { 110 | fmt.Println("Error:", err) 111 | continue 112 | } 113 | 114 | cache.lock.Lock() 115 | err = cache.redisClient.Set(ctx, "enbin", value, 60).Err() 116 | cache.lock.Unlock() 117 | if err != nil { 118 | fmt.Println("Error:", err) 119 | } else { 120 | fmt.Println("Success set data into MySQL") 121 | } 122 | } 123 | }() 124 | 125 | time.Sleep(10 * time.Second) 126 | 127 | value, err = cache.redisClient.Get(ctx, "enbin").Result() 128 | if err != nil { 129 | fmt.Println("Error:", err) 130 | } else { 131 | fmt.Println("Value:", value) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /set_proxy.sh: -------------------------------------------------------------------------------- 1 | export GO111MODULE=on 2 | export GOPROXY=https://goproxy.io -------------------------------------------------------------------------------- /simple_project/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "simple_project/util" 7 | maths "simple_project/util/math" 8 | 9 | "github.com/bytedance/sonic" 10 | ) 11 | 12 | func main() { 13 | fmt.Println(util.Name) 14 | fmt.Println(util.Sum(1, 1)) 15 | fmt.Println(maths.Add(1, 2, 3)) 16 | 17 | bytes, _ := sonic.Marshal("Halo") 18 | fmt.Println(string(bytes)) 19 | } 20 | -------------------------------------------------------------------------------- /simple_project/go.mod: -------------------------------------------------------------------------------- 1 | module simple_project 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/bytedance/sonic v1.9.2 // indirect 7 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 8 | github.com/klauspost/cpuid/v2 v2.0.9 // indirect 9 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 10 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /simple_project/go.sum: -------------------------------------------------------------------------------- 1 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 2 | github.com/bytedance/sonic v1.9.2 h1:GDaNjuWSGu09guE9Oql0MSTNhNCLlWwO8y/xM5BzcbM= 3 | github.com/bytedance/sonic v1.9.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 4 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 5 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 6 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= 10 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 11 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 12 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 13 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 14 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 15 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 16 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 17 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 18 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 19 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 20 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 21 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= 22 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 23 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 24 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 25 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 26 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 27 | -------------------------------------------------------------------------------- /simple_project/util/math/add.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | // Add is available out of this package 4 | func Add(a int, b int, c int) int { 5 | return a + sub(b, c) 6 | } 7 | -------------------------------------------------------------------------------- /simple_project/util/math/sub.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import "fmt" 4 | 5 | // Add is NOT available out of this package 6 | func sub(a int, b int) int { 7 | return a - b 8 | } 9 | 10 | // automatic call 11 | func init() { 12 | fmt.Println("init math package") 13 | } 14 | -------------------------------------------------------------------------------- /simple_project/util/sum.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "fmt" 4 | 5 | var ( 6 | Name = "enbin" 7 | ) 8 | 9 | func Sum(a int, b int) int { 10 | return a + b 11 | } 12 | 13 | func init() { 14 | fmt.Println("init util package") 15 | } 16 | -------------------------------------------------------------------------------- /testing/README.md: -------------------------------------------------------------------------------- 1 | # Benchmark and Unit Testing 2 | 3 | Benchmark testing 4 | ```bash 5 | go test -bench=JSON ./testing/benchmark/main_test.go -benchmem 6 | go test -bench=Sonic ./testing/benchmark/main_test.go -benchmem 7 | ``` 8 | 9 | Unit testing 10 | ```bash 11 | go test -v ./testing/unit/main_test.go -run=JSON 12 | ``` -------------------------------------------------------------------------------- /testing/benchmark/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | "github.com/bytedance/sonic" 8 | ) 9 | 10 | type Student struct { 11 | Name string 12 | Age int 13 | Gender bool 14 | } 15 | 16 | type Class struct { 17 | Id string 18 | Student []Student 19 | } 20 | 21 | var ( 22 | s = Student{"enbin", 23, true} 23 | c = Class{ 24 | Id: "CS", 25 | Student: []Student{s, s, s}, 26 | } 27 | ) 28 | 29 | func BenchmarkJSON(b *testing.B) { 30 | // b.N is a hunge number 31 | for i := 0; i < b.N; i++ { 32 | bytes, _ := json.Marshal(c) 33 | var data Class 34 | json.Unmarshal(bytes, &data) 35 | } 36 | } 37 | 38 | func BenchmarkSonic(b *testing.B) { 39 | for i := 0; i < b.N; i++ { 40 | bytes, _ := sonic.Marshal(c) 41 | var data Class 42 | sonic.Unmarshal(bytes, &data) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /testing/unit/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "testing" 7 | ) 8 | 9 | type Student struct { 10 | Name string 11 | Age int 12 | Gender bool 13 | } 14 | 15 | type Class struct { 16 | Id string 17 | Student []Student 18 | } 19 | 20 | var ( 21 | s = Student{"enbin", 23, true} 22 | c = Class{ 23 | Id: "CS", 24 | Student: []Student{s, s, s}, 25 | } 26 | ) 27 | 28 | func TestJSON(t *testing.T) { 29 | bytes, err := json.Marshal(c) 30 | if err != nil { 31 | t.Fail() 32 | } 33 | 34 | var data Class 35 | if err := json.Unmarshal(bytes, &data); err != nil { 36 | t.Fail() 37 | } 38 | if !(c.Id == data.Id && len(c.Student) == len(data.Student)) { 39 | t.Fail() 40 | } 41 | 42 | log.Println("Success") 43 | } 44 | --------------------------------------------------------------------------------