├── .gitignore ├── LICENSE ├── README.md ├── barrier └── barrier.go ├── doublebarrier └── doublebarrier.go ├── go.mod ├── go.sum ├── leader_elect └── leader.go ├── locker └── locker.go ├── mutex └── mutex.go ├── priorityqueue └── priorityqueue.go ├── queue └── queue.go ├── rwmutex └── rwmutex.go └── stm └── stm.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 smallnest 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## examples of etcd distributed concurrency primitives 2 | 3 | 4 | - Leader election 5 | - Locker 6 | - Mutex 7 | - RWMutex 8 | - Barrier 9 | - DoubleBarrier 10 | - Queue 11 | - PriorityQueue 12 | - STM -------------------------------------------------------------------------------- /barrier/barrier.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | "strings" 10 | 11 | clientv3 "go.etcd.io/etcd/client/v3" 12 | recipe "go.etcd.io/etcd/client/v3/experimental/recipes" 13 | ) 14 | 15 | var ( 16 | addr = flag.String("addr", "http://127.0.0.1:2379", "etcd addresses") 17 | barrierName = flag.String("name", "my-test-barrier", "barrier name") 18 | ) 19 | 20 | func main() { 21 | flag.Parse() 22 | 23 | // 解析etcd地址 24 | endpoints := strings.Split(*addr, ",") 25 | 26 | // 创建etcd的client 27 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | defer cli.Close() 32 | 33 | // 创建/获取栅栏 34 | b := recipe.NewBarrier(cli, *barrierName) 35 | 36 | // 从命令行读取命令 37 | consolescanner := bufio.NewScanner(os.Stdin) 38 | for consolescanner.Scan() { 39 | action := consolescanner.Text() 40 | items := strings.Split(action, " ") 41 | switch items[0] { 42 | case "hold": 43 | holdBarrier(b) 44 | case "release": 45 | releaseBarrier(b) 46 | case "wait": 47 | waitBarrier(b) 48 | case "quit", "exit": 49 | return 50 | default: 51 | fmt.Println("unknown action") 52 | } 53 | } 54 | } 55 | 56 | func holdBarrier(b *recipe.Barrier) { 57 | b.Hold() 58 | fmt.Println("hold") 59 | } 60 | 61 | func releaseBarrier(b *recipe.Barrier) { 62 | b.Release() 63 | fmt.Println("released") 64 | } 65 | 66 | func waitBarrier(b *recipe.Barrier) { 67 | b.Wait() 68 | fmt.Println("after wait") 69 | } 70 | -------------------------------------------------------------------------------- /doublebarrier/doublebarrier.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | "strings" 10 | 11 | clientv3 "go.etcd.io/etcd/client/v3" 12 | "go.etcd.io/etcd/client/v3/concurrency" 13 | recipe "go.etcd.io/etcd/client/v3/experimental/recipes" 14 | ) 15 | 16 | var ( 17 | addr = flag.String("addr", "http://127.0.0.1:2379", "etcd addresses") 18 | barrierName = flag.String("name", "my-test-doublebarrier", "barrier name") 19 | count = flag.Int("c", 2, "") 20 | ) 21 | 22 | func main() { 23 | flag.Parse() 24 | 25 | // 解析etcd地址 26 | endpoints := strings.Split(*addr, ",") 27 | 28 | // 创建etcd的client 29 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | defer cli.Close() 34 | // 创建session 35 | s1, err := concurrency.NewSession(cli) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | defer s1.Close() 40 | 41 | // 创建/获取队列 42 | b := recipe.NewDoubleBarrier(s1, *barrierName, *count) 43 | 44 | // 从命令行读取命令 45 | consolescanner := bufio.NewScanner(os.Stdin) 46 | for consolescanner.Scan() { 47 | action := consolescanner.Text() 48 | items := strings.Split(action, " ") 49 | switch items[0] { 50 | case "enter": // 持有这个barrier 51 | b.Enter() 52 | fmt.Println("enter") 53 | case "leave": // 释放这个barrier 54 | b.Leave() 55 | fmt.Println("leave") 56 | case "quit", "exit": //退出 57 | return 58 | default: 59 | fmt.Println("unknown action") 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/smallnest/distributed 2 | 3 | go 1.20 4 | 5 | replace ( 6 | github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.5 7 | google.golang.org/grpc => google.golang.org/grpc v1.26.0 8 | ) 9 | 10 | require go.etcd.io/etcd/client/v3 v3.5.7 11 | 12 | require ( 13 | github.com/coreos/go-semver v0.3.1 // indirect 14 | github.com/coreos/go-systemd/v22 v22.3.2 // indirect 15 | github.com/gogo/protobuf v1.3.2 // indirect 16 | github.com/golang/protobuf v1.5.2 // indirect 17 | github.com/google/go-cmp v0.5.6 // indirect 18 | github.com/pkg/errors v0.9.1 // indirect 19 | go.etcd.io/etcd/api/v3 v3.5.7 // indirect 20 | go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect 21 | go.uber.org/atomic v1.10.0 // indirect 22 | go.uber.org/multierr v1.9.0 // indirect 23 | go.uber.org/zap v1.24.0 // indirect 24 | golang.org/x/net v0.7.0 // indirect 25 | golang.org/x/sys v0.5.0 // indirect 26 | golang.org/x/text v0.7.0 // indirect 27 | google.golang.org/genproto v0.0.0-20230223222841-637eb2293923 // indirect 28 | google.golang.org/grpc v1.53.0 // indirect 29 | google.golang.org/protobuf v1.28.1 // indirect 30 | ) 31 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= 2 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 3 | github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= 4 | github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= 5 | github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= 6 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 7 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 8 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 9 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 10 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 11 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 12 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 13 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 14 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 15 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 16 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 17 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 18 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 19 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 20 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 21 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 22 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 23 | github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= 24 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 25 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 26 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 27 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 28 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 29 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 30 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 31 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 32 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 33 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 34 | go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY= 35 | go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA= 36 | go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg= 37 | go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY= 38 | go.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4= 39 | go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw= 40 | go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= 41 | go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 42 | go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= 43 | go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= 44 | go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= 45 | go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= 46 | go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= 47 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 48 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 49 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 50 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 51 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 52 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 53 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 54 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 55 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 56 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 57 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 58 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 59 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 60 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 61 | golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= 62 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 63 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 64 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 65 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 66 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 67 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 68 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 69 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 70 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 71 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= 72 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 73 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 74 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 75 | golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= 76 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 77 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 78 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 79 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 80 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 81 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 82 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 83 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 84 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 85 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 86 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 87 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 88 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 89 | google.golang.org/genproto v0.0.0-20230223222841-637eb2293923 h1:znp6mq/drrY+6khTAlJUDNFFcDGV2ENLYKpMq8SyCds= 90 | google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= 91 | google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= 92 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 93 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 94 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 95 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 96 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 97 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 98 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 99 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 100 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 101 | -------------------------------------------------------------------------------- /leader_elect/leader.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "os" 10 | "strings" 11 | 12 | clientv3 "go.etcd.io/etcd/client/v3" 13 | "go.etcd.io/etcd/client/v3/concurrency" 14 | ) 15 | 16 | var ( 17 | nodeID = flag.Int("id", 0, "node ID") 18 | addr = flag.String("addr", "http://127.0.0.1:2379", "etcd addresses") 19 | electName = flag.String("name", "my-test-elect", "election name") 20 | ) 21 | 22 | func main() { 23 | flag.Parse() 24 | 25 | endpoints := strings.Split(*addr, ",") 26 | 27 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | defer cli.Close() 32 | 33 | session, err := concurrency.NewSession(cli) 34 | defer session.Close() 35 | 36 | e1 := concurrency.NewElection(session, *electName) 37 | 38 | consolescanner := bufio.NewScanner(os.Stdin) 39 | for consolescanner.Scan() { 40 | action := consolescanner.Text() 41 | switch action { 42 | case "elect": 43 | go elect(e1, *electName) 44 | case "proclaim": 45 | proclaim(e1, *electName) 46 | case "resign": 47 | resign(e1, *electName) 48 | case "watch": 49 | go watch(e1, *electName) 50 | case "query": 51 | query(e1, *electName) 52 | case "rev": 53 | rev(e1, *electName) 54 | default: 55 | fmt.Println("unknown action") 56 | } 57 | } 58 | } 59 | 60 | var count int 61 | 62 | func elect(e1 *concurrency.Election, electName string) { 63 | log.Println("acampaigning for ID:", *nodeID) 64 | if err := e1.Campaign(context.Background(), fmt.Sprintf("value-%d-%d", *nodeID, count)); err != nil { 65 | log.Println(err) 66 | } 67 | log.Println("campaigned for ID:", *nodeID) 68 | count++ 69 | } 70 | 71 | func proclaim(e1 *concurrency.Election, electName string) { 72 | log.Println("proclaiming for ID:", *nodeID) 73 | if err := e1.Proclaim(context.Background(), fmt.Sprintf("value-%d-%d", *nodeID, count)); err != nil { 74 | log.Println(err) 75 | } 76 | log.Println("proclaimed for ID:", *nodeID) 77 | count++ 78 | } 79 | 80 | func resign(e1 *concurrency.Election, electName string) { 81 | log.Println("resigning for ID:", *nodeID) 82 | if err := e1.Resign(context.TODO()); err != nil { 83 | log.Println(err) 84 | } 85 | log.Println("resigned for ID:", *nodeID) 86 | } 87 | 88 | func watch(e1 *concurrency.Election, electName string) { 89 | ch := e1.Observe(context.TODO()) 90 | 91 | log.Println("start to watch for ID:", *nodeID) 92 | for i := 0; i < 10; i++ { 93 | resp := <-ch 94 | log.Println("leader changed to", string(resp.Kvs[0].Key), string(resp.Kvs[0].Value)) 95 | } 96 | } 97 | 98 | func query(e1 *concurrency.Election, electName string) { 99 | resp, err := e1.Leader(context.Background()) 100 | if err != nil { 101 | log.Printf("failed to get the current leader: %v", err) 102 | } 103 | log.Println("current leader:", string(resp.Kvs[0].Key), string(resp.Kvs[0].Value)) 104 | } 105 | 106 | func rev(e1 *concurrency.Election, electName string) { 107 | rev := e1.Rev() 108 | log.Println("current rev:", rev) 109 | } 110 | -------------------------------------------------------------------------------- /locker/locker.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "math/rand" 7 | "strings" 8 | "time" 9 | 10 | clientv3 "go.etcd.io/etcd/client/v3" 11 | "go.etcd.io/etcd/client/v3/concurrency" 12 | ) 13 | 14 | var ( 15 | addr = flag.String("addr", "http://127.0.0.1:2379", "etcd addresses") 16 | lockName = flag.String("name", "my-test-lock", "lock name") 17 | ) 18 | 19 | func main() { 20 | flag.Parse() 21 | 22 | rand.Seed(time.Now().UnixNano()) 23 | 24 | // etcd地址 25 | endpoints := strings.Split(*addr, ",") 26 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | defer cli.Close() 31 | 32 | useLock(cli) 33 | } 34 | 35 | func useLock(cli *clientv3.Client) { 36 | // 为锁生成session 37 | s1, err := concurrency.NewSession(cli) 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | defer s1.Close() 42 | locker := concurrency.NewLocker(s1, *lockName) 43 | 44 | // 请求锁 45 | log.Println("acquiring lock") 46 | locker.Lock() 47 | log.Println("acquired lock") 48 | 49 | // 等待一段时间 50 | time.Sleep(time.Duration(rand.Intn(30)) * time.Second) 51 | locker.Unlock() 52 | 53 | log.Println("released lock") 54 | } 55 | -------------------------------------------------------------------------------- /mutex/mutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "log" 7 | "math/rand" 8 | "strings" 9 | "time" 10 | 11 | clientv3 "go.etcd.io/etcd/client/v3" 12 | "go.etcd.io/etcd/client/v3/concurrency" 13 | ) 14 | 15 | var ( 16 | addr = flag.String("addr", "http://127.0.0.1:2379", "etcd addresses") 17 | lockName = flag.String("name", "my-test-lock", "lock name") 18 | ) 19 | 20 | func main() { 21 | flag.Parse() 22 | 23 | rand.Seed(time.Now().UnixNano()) 24 | 25 | // etcd地址 26 | endpoints := strings.Split(*addr, ",") 27 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | defer cli.Close() 32 | 33 | useMutex(cli) 34 | } 35 | 36 | func useMutex(cli *clientv3.Client) { 37 | // 为锁生成session 38 | s1, err := concurrency.NewSession(cli) 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | defer s1.Close() 43 | m1 := concurrency.NewMutex(s1, *lockName) 44 | 45 | log.Printf("before acquiring. key: %s", m1.Key()) 46 | // 请求锁 47 | log.Println("acquiring lock") 48 | if err := m1.Lock(context.TODO()); err != nil { 49 | log.Fatal(err) 50 | } 51 | log.Printf("acquired lock. key: %s", m1.Key()) 52 | 53 | time.Sleep(time.Duration(rand.Intn(30)) * time.Second) 54 | 55 | if err := m1.Unlock(context.TODO()); err != nil { 56 | log.Fatal(err) 57 | } 58 | log.Println("released lock") 59 | } 60 | -------------------------------------------------------------------------------- /priorityqueue/priorityqueue.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | "strconv" 10 | "strings" 11 | 12 | clientv3 "go.etcd.io/etcd/client/v3" 13 | recipe "go.etcd.io/etcd/client/v3/experimental/recipes" 14 | ) 15 | 16 | var ( 17 | addr = flag.String("addr", "http://127.0.0.1:2379", "etcd addresses") 18 | queueName = flag.String("name", "my-test-queue", "queue name") 19 | ) 20 | 21 | func main() { 22 | flag.Parse() 23 | 24 | // 解析etcd地址 25 | endpoints := strings.Split(*addr, ",") 26 | 27 | // 创建etcd的client 28 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | defer cli.Close() 33 | 34 | // 创建/获取队列 35 | q := recipe.NewPriorityQueue(cli, *queueName) 36 | 37 | // 从命令行读取命令 38 | consolescanner := bufio.NewScanner(os.Stdin) 39 | for consolescanner.Scan() { 40 | action := consolescanner.Text() 41 | items := strings.Split(action, " ") 42 | switch items[0] { 43 | case "push": // 加入队列 44 | if len(items) != 3 { 45 | fmt.Println("must set value and priority to push") 46 | continue 47 | } 48 | pr, err := strconv.Atoi(items[2]) // 读取优先级 49 | if err != nil { 50 | fmt.Println("must set uint16 as priority") 51 | continue 52 | } 53 | q.Enqueue(items[1], uint16(pr)) // 入队 54 | case "pop": // 从队列弹出 55 | v, err := q.Dequeue() // 出队 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | fmt.Println(v) // 输出出队的元素 60 | case "quit", "exit": //退出 61 | return 62 | default: 63 | fmt.Println("unknown action") 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /queue/queue.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | "strings" 10 | 11 | clientv3 "go.etcd.io/etcd/client/v3" 12 | recipe "go.etcd.io/etcd/client/v3/experimental/recipes" 13 | ) 14 | 15 | var ( 16 | addr = flag.String("addr", "http://127.0.0.1:2379", "etcd addresses") 17 | queueName = flag.String("name", "my-test-queue", "queue name") 18 | ) 19 | 20 | func main() { 21 | flag.Parse() 22 | 23 | // 解析etcd地址 24 | endpoints := strings.Split(*addr, ",") 25 | 26 | // 创建etcd的client 27 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | defer cli.Close() 32 | 33 | // 创建/获取队列 34 | q := recipe.NewQueue(cli, *queueName) 35 | 36 | // 从命令行读取命令 37 | consolescanner := bufio.NewScanner(os.Stdin) 38 | for consolescanner.Scan() { 39 | action := consolescanner.Text() 40 | items := strings.Split(action, " ") 41 | switch items[0] { 42 | case "push": // 加入队列 43 | if len(items) != 2 { 44 | fmt.Println("must set value to push") 45 | continue 46 | } 47 | q.Enqueue(items[1]) // 入队 48 | case "pop": // 从队列弹出 49 | v, err := q.Dequeue() // 出队 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | fmt.Println(v) // 输出出队的元素 54 | case "quit", "exit": //退出 55 | return 56 | default: 57 | fmt.Println("unknown action") 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /rwmutex/rwmutex.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | "strings" 10 | "time" 11 | 12 | clientv3 "go.etcd.io/etcd/client/v3" 13 | "go.etcd.io/etcd/client/v3/concurrency" 14 | recipe "go.etcd.io/etcd/client/v3/experimental/recipes" 15 | ) 16 | 17 | var ( 18 | addr = flag.String("addr", "http://127.0.0.1:2379", "etcd addresses") 19 | lockName = flag.String("name", "my-test-lock", "lock name") 20 | action = flag.String("rw", "w", "r means acquiring read lock, w means acquiring write lock") 21 | ) 22 | 23 | func main() { 24 | flag.Parse() 25 | 26 | // 解析etcd地址 27 | endpoints := strings.Split(*addr, ",") 28 | 29 | // 创建etcd的client 30 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | defer cli.Close() 35 | // 创建session 36 | s1, err := concurrency.NewSession(cli) 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | defer s1.Close() 41 | m1 := recipe.NewRWMutex(s1, *lockName) 42 | 43 | // 从命令行读取命令 44 | consolescanner := bufio.NewScanner(os.Stdin) 45 | for consolescanner.Scan() { 46 | action := consolescanner.Text() 47 | switch action { 48 | case "w": // 请求写锁 49 | testWriteLocker(m1) 50 | case "r": // 请求读锁 51 | testReadLocker(m1) 52 | default: 53 | fmt.Println("unknown action") 54 | } 55 | } 56 | } 57 | 58 | func testWriteLocker(m1 *recipe.RWMutex) { 59 | // 请求写锁 60 | log.Println("acquiring write lock") 61 | if err := m1.Lock(); err != nil { 62 | log.Fatal(err) 63 | } 64 | log.Println("acquired write lock") 65 | 66 | // 等待一段时间 67 | time.Sleep(30 * time.Second) 68 | 69 | // 释放写锁 70 | if err := m1.Unlock(); err != nil { 71 | log.Fatal(err) 72 | } 73 | log.Println("released write lock") 74 | } 75 | 76 | func testReadLocker(m1 *recipe.RWMutex) { 77 | // 请求读锁 78 | log.Println("acquiring read lock") 79 | if err := m1.RLock(); err != nil { 80 | log.Fatal(err) 81 | } 82 | log.Println("acquired read lock") 83 | 84 | // 等待一段时间 85 | time.Sleep(30 * time.Second) 86 | 87 | // 释放写锁 88 | if err := m1.RUnlock(); err != nil { 89 | log.Fatal(err) 90 | } 91 | log.Println("released read lock") 92 | } 93 | -------------------------------------------------------------------------------- /stm/stm.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "math/rand" 9 | "strings" 10 | "sync" 11 | 12 | clientv3 "go.etcd.io/etcd/client/v3" 13 | "go.etcd.io/etcd/client/v3/concurrency" 14 | ) 15 | 16 | var ( 17 | addr = flag.String("addr", "http://127.0.0.1:2379", "etcd addresses") 18 | ) 19 | 20 | func main() { 21 | flag.Parse() 22 | 23 | // 解析etcd地址 24 | endpoints := strings.Split(*addr, ",") 25 | 26 | cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | defer cli.Close() 31 | 32 | // 设置5个账户,每个账号都有100元,总共500元 33 | totalAccounts := 5 34 | for i := 0; i < totalAccounts; i++ { 35 | k := fmt.Sprintf("accts/%d", i) 36 | if _, err = cli.Put(context.TODO(), k, "100"); err != nil { 37 | log.Fatal(err) 38 | } 39 | } 40 | 41 | // STM的应用函数,主要的事务逻辑 42 | exchange := func(stm concurrency.STM) error { 43 | // 随机得到两个转账账号 44 | from, to := rand.Intn(totalAccounts), rand.Intn(totalAccounts) 45 | if from == to { 46 | // 自己不和自己转账 47 | return nil 48 | } 49 | // 读取账号的值 50 | fromK, toK := fmt.Sprintf("accts/%d", from), fmt.Sprintf("accts/%d", to) 51 | fromV, toV := stm.Get(fromK), stm.Get(toK) 52 | fromInt, toInt := 0, 0 53 | fmt.Sscanf(fromV, "%d", &fromInt) 54 | fmt.Sscanf(toV, "%d", &toInt) 55 | 56 | // 把源账号的一半转账给目标账号 57 | xfer := fromInt / 2 58 | fromInt, toInt = fromInt-xfer, toInt+xfer 59 | 60 | // 把转账后的值写回 61 | stm.Put(fromK, fmt.Sprintf("%d", fromInt)) 62 | stm.Put(toK, fmt.Sprintf("%d", toInt)) 63 | return nil 64 | } 65 | 66 | // 启动10个goroutine进行转账操作 67 | var wg sync.WaitGroup 68 | wg.Add(10) 69 | for i := 0; i < 10; i++ { 70 | go func() { 71 | defer wg.Done() 72 | for j := 0; j < 100; j++ { 73 | if _, serr := concurrency.NewSTM(cli, exchange); serr != nil { 74 | log.Fatal(serr) 75 | } 76 | } 77 | }() 78 | } 79 | wg.Wait() 80 | 81 | // 检查账号最后的数目 82 | sum := 0 83 | accts, err := cli.Get(context.TODO(), "accts/", clientv3.WithPrefix()) // 得到所有账号 84 | if err != nil { 85 | log.Fatal(err) 86 | } 87 | for _, kv := range accts.Kvs { // 遍历账号的值 88 | v := 0 89 | fmt.Sscanf(string(kv.Value), "%d", &v) 90 | sum += v 91 | log.Printf("account %s: %d", kv.Key, v) 92 | } 93 | 94 | log.Println("account sum is", sum) // 总数 95 | } 96 | --------------------------------------------------------------------------------