├── bin └── .gitkeep ├── log └── .gitkeep ├── etc ├── env.txt.sample └── local │ ├── config.yaml │ ├── etcd.yaml │ ├── queue.yaml │ ├── cache.yaml │ └── database.yaml ├── .gitignore ├── lib ├── logger │ ├── logger_test.go │ └── logger.go ├── config │ ├── config_test.go │ └── config.go ├── gmq │ ├── base │ │ └── gmq_base.go │ ├── gmq_test.go │ ├── gmq.go │ ├── driver │ │ └── gmq_driver.go │ └── rabbitmq │ │ └── gmq_rabbitmq.go ├── example │ ├── httpserver │ │ └── example_httpserver.go │ ├── gmqclient │ │ └── example_gmqclient.go │ ├── socketclient │ │ └── example_socketclient.go │ ├── getcdclient │ │ └── example_getcdclient.go │ ├── gcacheclient │ │ └── example_gcacheclient.go │ ├── socketserver │ │ └── example_socketserver.go │ └── gdoclient │ │ └── example_gdoclient.go ├── gutil │ ├── gutil_test.go │ └── gutil.go ├── base │ ├── base_queue.go │ ├── base_hmap.go │ ├── base_stack.go │ └── base_list.go ├── gdo │ ├── parser │ │ ├── gdo_parser_test.go │ │ └── gdo_parser.go │ ├── cluster │ │ ├── gdo_cluster_shard.go │ │ └── gdo_cluster.go │ ├── gdo.go │ ├── base │ │ └── gdo_base.go │ ├── driver │ │ └── gdo_driver.go │ ├── pool │ │ ├── gdo_pool.go │ │ └── gdo_pool.go.old │ ├── gdo_test.go │ └── mysql │ │ └── gdo_mysql.go ├── gcache │ ├── base │ │ └── gcache_base.go │ ├── gcache_test.go │ ├── gcache.go │ ├── region │ │ └── gcache_region.go │ ├── driver │ │ └── gcache_driver.go │ ├── pool │ │ ├── gcache_pool.go │ │ └── gcache_pool.go.old │ └── redis │ │ └── gcache_redis.go ├── proto │ └── proto.go └── getcd │ ├── getcd_test.go │ └── getcd.go ├── doc ├── tpl │ ├── main.t │ └── Makefile └── sql │ └── test.sql ├── go.mod ├── Makefile ├── main.go ├── README.md └── go.sum /bin/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /log/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /etc/env.txt.sample: -------------------------------------------------------------------------------- 1 | dev -------------------------------------------------------------------------------- /etc/local/config.yaml: -------------------------------------------------------------------------------- 1 | # app configs 2 | server: 3 | http: 4 | addr: "0.0.0.0:80" 5 | socket: 6 | addr: "0.0.0.0:8080" 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pkg/ 2 | vendor/ 3 | 4 | bin/* 5 | log/* 6 | etc/env.txt 7 | !.gitkeep 8 | 9 | .env 10 | *.log 11 | *.swp 12 | .project 13 | .settings/ 14 | .vscode/ 15 | .idea/ -------------------------------------------------------------------------------- /etc/local/etcd.yaml: -------------------------------------------------------------------------------- 1 | # etcd configs 2 | # notice: cluster needs at least three endpoints 3 | config: 4 | timeout: 5 5 | endpoints: 6 | - 127.0.0.1:2379 7 | - 127.0.0.1:2379 8 | - 127.0.0.1:2379 9 | -------------------------------------------------------------------------------- /lib/logger/logger_test.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestLogger(t *testing.T) { 8 | Info("test info") 9 | Warn("test warn") 10 | Error("test error") 11 | } 12 | -------------------------------------------------------------------------------- /etc/local/queue.yaml: -------------------------------------------------------------------------------- 1 | # drivers 2 | drivers: 3 | default: 4 | type: rabbitmq 5 | algo: rand # rand|hash 6 | nodes: 7 | - amqp://guest:guest@localhost:5672/ 8 | - amqp://guest:guest@localhost:5672/ 9 | 10 | -------------------------------------------------------------------------------- /doc/tpl/main.t: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "base/util" 5 | ) 6 | 7 | func main() { 8 | // dump project configs 9 | gutil.Dump("gutil.GetEnv()", gutil.GetEnv()) 10 | gutil.Dump("gutil.GetRootPath()", gutil.GetRootPath()) 11 | } 12 | -------------------------------------------------------------------------------- /lib/config/config_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/jameschz/go-base/lib/gutil" 7 | ) 8 | 9 | func TestConfigDatabase(t *testing.T) { 10 | Init() 11 | tmp := Load("database").GetStringMap("drivers") 12 | gutil.Dump(tmp) 13 | Init() 14 | all := Load("etcd").AllSettings() 15 | gutil.Dump(all) 16 | } 17 | -------------------------------------------------------------------------------- /lib/gmq/base/gmq_base.go: -------------------------------------------------------------------------------- 1 | package gmqbase 2 | 3 | import ( 4 | gmqdriver "github.com/jameschz/go-base/lib/gmq/driver" 5 | ) 6 | 7 | // MQ : 8 | type MQ struct { 9 | Driver *gmqdriver.Driver // driver ptr 10 | NodeName string // node name 11 | } 12 | 13 | // IMQ : 14 | type IMQ interface { 15 | Connect(node string) error 16 | Close() error 17 | Shard(sk string) error 18 | Publish(qName string, qBody string) error 19 | Consume(qName string, callback func(done chan bool, body []byte)) error 20 | } 21 | -------------------------------------------------------------------------------- /lib/example/httpserver/example_httpserver.go: -------------------------------------------------------------------------------- 1 | package examplehttpserver 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jameschz/go-base/lib/config" 6 | "io" 7 | "net/http" 8 | ) 9 | 10 | // HTTPIndex : 11 | func HTTPIndex(w http.ResponseWriter, req *http.Request) { 12 | io.WriteString(w, "hello") 13 | } 14 | 15 | // HTTPHello : 16 | func HTTPHello(w http.ResponseWriter, req *http.Request) { 17 | io.WriteString(w, "Hello, We can load configs:\n") 18 | io.WriteString(w, fmt.Sprintf("%#v", config.Load("config").GetStringMap("server"))) 19 | 20 | } 21 | -------------------------------------------------------------------------------- /lib/gmq/gmq_test.go: -------------------------------------------------------------------------------- 1 | package gmq 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/jameschz/go-base/lib/gutil" 7 | ) 8 | 9 | func TestPub(t *testing.T) { 10 | rmq := D("default") 11 | rmq.Publish("q1", `{name:"james",text:"hello"}`) 12 | } 13 | 14 | func TestSub(t *testing.T) { 15 | rmq := D("default") 16 | rmq.Consume("q1", func(done chan bool, body []byte) { 17 | // print messages from queue 18 | gutil.Dump("done status:", done) 19 | gutil.Dump("msg body bytes:", body) 20 | gutil.Dump("msg body string:", string(body)) 21 | // quiting the loop 22 | done <- true 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jameschz/go-base 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/bwmarrin/snowflake v0.3.1-0.20190412223032-c09e69ae5993 7 | github.com/coreos/etcd v3.3.22+incompatible 8 | github.com/dustin/go-humanize v1.0.0 // indirect 9 | github.com/go-redis/redis v6.15.8+incompatible 10 | github.com/go-sql-driver/mysql v1.5.1-0.20200720071143-73dc904a9ece 11 | github.com/golang/protobuf v1.4.2 12 | github.com/google/uuid v1.1.1 // indirect 13 | github.com/onsi/ginkgo v1.14.1 // indirect 14 | github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b 15 | github.com/spf13/viper v1.7.1 16 | github.com/streadway/amqp v1.0.0 17 | sigs.k8s.io/yaml v1.2.0 // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /lib/gutil/gutil_test.go: -------------------------------------------------------------------------------- 1 | package gutil 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestUtil(t *testing.T) { 8 | Dump("GetRootPath", GetRootPath()) 9 | Dump("GetMACs", GetMACs()) 10 | Dump("GetIPs", GetIPs()) 11 | Dump("UUID", UUID()) 12 | Dump("SFID", SFID()) 13 | } 14 | 15 | func TestXOR(t *testing.T) { 16 | key := []byte("KEY") 17 | encrypt := EncryptXOR("testbyjames1中国", key) 18 | decrypt := DecryptXOR(encrypt, key) 19 | Dump("XOR encrypt", encrypt) 20 | Dump("XOR decrypt", decrypt) 21 | decrypt1 := DecryptXOR("KjY4Ly04Ii1oeazB9KPp/w==", key) 22 | Dump("XOR decrypt1", decrypt1) 23 | } 24 | 25 | func TestThrow(t *testing.T) { 26 | defer Catch() 27 | Throw("exception:1") 28 | Throw("exception:2") 29 | } 30 | -------------------------------------------------------------------------------- /lib/gmq/gmq.go: -------------------------------------------------------------------------------- 1 | package gmq 2 | 3 | import ( 4 | gmqbase "github.com/jameschz/go-base/lib/gmq/base" 5 | gmqdriver "github.com/jameschz/go-base/lib/gmq/driver" 6 | gmqrabbitmq "github.com/jameschz/go-base/lib/gmq/rabbitmq" 7 | ) 8 | 9 | // D : connect by driver 10 | func D(cs string) (imq gmqbase.IMQ) { 11 | // init driver 12 | gmqdriver.Init() 13 | // get mq driver 14 | mqDriver := gmqdriver.GetDriver(cs) 15 | if len(mqDriver.Type) == 0 { 16 | panic("gmq> mq driver error") 17 | } 18 | // mq initialize 19 | switch mqDriver.Type { 20 | case "rabbitmq": 21 | rmq := &gmqrabbitmq.RabbitMQ{} 22 | rmq.Driver = mqDriver 23 | imq = rmq 24 | default: 25 | panic("gmq> unknown driver type") 26 | } 27 | return imq 28 | } 29 | -------------------------------------------------------------------------------- /etc/local/cache.yaml: -------------------------------------------------------------------------------- 1 | # pool configs 2 | _pool: &_pool 3 | pool_init_size: 10 4 | pool_idle_min_size: 5 5 | pool_idle_timeout_min: 5 6 | 7 | # drivers 8 | drivers: 9 | default: 10 | type: redis 11 | name: default 12 | algo: hash # rand|hash 13 | nodes: 14 | - 127.0.0.1:6379:0 15 | - 127.0.0.1:6379:1 16 | <<: *_pool 17 | rank: 18 | type: redis 19 | name: rank 20 | algo: hash 21 | nodes: 22 | - 127.0.0.1:6379 23 | <<: *_pool 24 | 25 | # regions 26 | regions: 27 | user: 28 | name: user 29 | driver: default 30 | desc: "user cache" 31 | ttl: 60 * 60 * 24 32 | rank: 33 | name: rank 34 | driver: rank 35 | desc: "rank cache" 36 | ttl: 60 * 60 * 24 37 | -------------------------------------------------------------------------------- /lib/base/base_queue.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "container/list" 5 | ) 6 | 7 | // Queue : 8 | type Queue struct { 9 | list *list.List 10 | } 11 | 12 | // NewQueue : 13 | func NewQueue() *Queue { 14 | qs := new(Queue) 15 | qs.list = list.New() 16 | return qs 17 | } 18 | 19 | // PushFront : 20 | func (qs *Queue) PushFront(x interface{}) { 21 | qs.list.PushFront(x) 22 | } 23 | 24 | // PushBack : 25 | func (qs *Queue) PushBack(x interface{}) { 26 | qs.list.PushBack(x) 27 | } 28 | 29 | // PopFront : 30 | func (qs *Queue) PopFront() interface{} { 31 | x := qs.list.Front() 32 | return qs.list.Remove(x) 33 | } 34 | 35 | // PopBack : 36 | func (qs *Queue) PopBack() interface{} { 37 | x := qs.list.Back() 38 | return qs.list.Remove(x) 39 | } 40 | -------------------------------------------------------------------------------- /lib/base/base_hmap.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | // Hmap : 4 | type Hmap struct { 5 | size int 6 | data map[string]interface{} 7 | } 8 | 9 | // NewHmap : 10 | func NewHmap() *Hmap { 11 | hash := make(map[string]interface{}, 0) 12 | return &Hmap{ 13 | size: 0, 14 | data: hash, 15 | } 16 | } 17 | 18 | // Data : 19 | func (h *Hmap) Data() map[string]interface{} { 20 | return h.data 21 | } 22 | 23 | // Len : 24 | func (h *Hmap) Len() int { 25 | return h.size 26 | } 27 | 28 | // Set : 29 | func (h *Hmap) Set(s string, v interface{}) { 30 | h.data[s] = v 31 | h.size++ 32 | } 33 | 34 | // Get : 35 | func (h *Hmap) Get(s string) interface{} { 36 | return h.data[s] 37 | } 38 | 39 | // Delete : 40 | func (h *Hmap) Delete(s string) { 41 | delete(h.data, s) 42 | h.size-- 43 | } 44 | -------------------------------------------------------------------------------- /lib/gdo/parser/gdo_parser_test.go: -------------------------------------------------------------------------------- 1 | package gdoparser 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestSqlType(t *testing.T) { 9 | sqlType := GetSQLType(`select * from user where 1=1`) 10 | fmt.Println("sqlType", sqlType) 11 | } 12 | 13 | func TestGetSeqIdPos(t *testing.T) { 14 | cs := [][]string{ 15 | {`select * from user where id=? and name=?`, "id"}, 16 | {`insert into user set id=?,name=?`, "name"}, 17 | {`update user set name=? where id=?`, "id"}, 18 | } 19 | for _, c := range cs { 20 | sqlPos := GetSeqIDPos(c[0], c[1]) 21 | fmt.Println("sqlPos", sqlPos) 22 | } 23 | 24 | } 25 | 26 | func TestGetSeqIdVal(t *testing.T) { 27 | sql := `select * from user where id=? and name=?` 28 | val := GetSeqIDVal(sql, "id", 1, "james") 29 | fmt.Println("sql_val", val) 30 | } 31 | -------------------------------------------------------------------------------- /lib/example/gmqclient/example_gmqclient.go: -------------------------------------------------------------------------------- 1 | package examplegmqclient 2 | 3 | import ( 4 | "github.com/jameschz/go-base/lib/gmq" 5 | "github.com/jameschz/go-base/lib/gutil" 6 | ) 7 | 8 | // RabbitPub : 9 | func RabbitPub() { 10 | // get driver default 11 | rmq := gmq.D("default") 12 | // publish to queue default 13 | rmq.Publish("default", `{name:"james",text:"hello"}`) 14 | } 15 | 16 | // RabbitSub : 17 | func RabbitSub() { 18 | // get driver default 19 | rmq := gmq.D("default") 20 | // comsume from queue default 21 | rmq.Consume("default", func(done chan bool, body []byte) { 22 | // print messages from queue 23 | gutil.Dump("done status:", done) 24 | gutil.Dump("msg body bytes:", body) 25 | gutil.Dump("msg body string:", string(body)) 26 | // quiting the loop 27 | done <- true 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /lib/base/base_stack.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import "container/list" 4 | 5 | // Stack : 6 | type Stack struct { 7 | list *list.List 8 | } 9 | 10 | // NewStack : 11 | func NewStack() *Stack { 12 | list := list.New() 13 | return &Stack{list} 14 | } 15 | 16 | // Push : 17 | func (stack *Stack) Push(value interface{}) { 18 | stack.list.PushBack(value) 19 | } 20 | 21 | // Pop : 22 | func (stack *Stack) Pop() interface{} { 23 | e := stack.list.Back() 24 | if e != nil { 25 | return stack.list.Remove(e) 26 | } 27 | return nil 28 | } 29 | 30 | // Peak : 31 | func (stack *Stack) Peak() interface{} { 32 | e := stack.list.Back() 33 | if e != nil { 34 | return e.Value 35 | } 36 | return nil 37 | } 38 | 39 | // Len : 40 | func (stack *Stack) Len() int { 41 | return stack.list.Len() 42 | } 43 | 44 | // Empty : 45 | func (stack *Stack) Empty() bool { 46 | return stack.list.Len() == 0 47 | } 48 | -------------------------------------------------------------------------------- /lib/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/jameschz/go-base/lib/gutil" 5 | "github.com/spf13/viper" 6 | ) 7 | 8 | var ( 9 | _configMap = make(map[string]*viper.Viper) 10 | _configDir = "" 11 | ) 12 | 13 | // Init : 14 | func Init() { 15 | // init config dir 16 | if len(_configDir) == 0 { 17 | _configDir = gutil.GetRootPath() + "/etc/" + gutil.GetEnv() 18 | } 19 | } 20 | 21 | // Load : 22 | func Load(configName string) *viper.Viper { 23 | // init 24 | Init() 25 | // cache config 26 | if _, exist := _configMap[configName]; !exist { 27 | _configMap[configName] = viper.New() 28 | _configMap[configName].SetConfigType("yaml") 29 | _configMap[configName].AddConfigPath(_configDir) 30 | _configMap[configName].SetConfigName(configName) 31 | if err := _configMap[configName].ReadInConfig(); err != nil { 32 | panic(err) 33 | } 34 | } 35 | // return config 36 | return _configMap[configName] 37 | } 38 | -------------------------------------------------------------------------------- /lib/example/socketclient/example_socketclient.go: -------------------------------------------------------------------------------- 1 | package examplesocketclient 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | // Client : 12 | func Client(addr string) { 13 | // dial and handshake to get connections 14 | conn, err := net.Dial("tcp", addr) 15 | if err != nil { 16 | fmt.Println("> connect error:", err) 17 | os.Exit(1) 18 | } 19 | fmt.Println("> connect success!") 20 | defer conn.Close() 21 | // keep connection 22 | i := 0 23 | for { 24 | i++ 25 | // send to server 26 | words := strconv.Itoa(i) + " Hello I'm heartbeat client." 27 | msg, err := conn.Write([]byte(words)) 28 | if err != nil { 29 | fmt.Println("> write error:", err) 30 | break 31 | } 32 | fmt.Println("> send:", msg, "text:", words) 33 | // lost connection 34 | if i >= 5 { 35 | time.Sleep(5 * time.Second) 36 | continue 37 | } 38 | // keep connection 39 | time.Sleep(1 * time.Second) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/gdo/cluster/gdo_cluster_shard.go: -------------------------------------------------------------------------------- 1 | package gdocluster 2 | 3 | // ShardRange : shard by range 4 | type ShardRange struct { 5 | DrName string 6 | FrNum int64 7 | ToNum int64 8 | } 9 | 10 | // GetDrName : 11 | func (s *ShardRange) GetDrName() string { 12 | return s.DrName 13 | } 14 | 15 | // IsMatch : 16 | func (s *ShardRange) IsMatch(seqID int64, shardNum int64) bool { 17 | if seqID >= s.FrNum && seqID <= s.ToNum { 18 | return true 19 | } 20 | return false 21 | } 22 | 23 | // ShardHash : shard by hush 24 | type ShardHash struct { 25 | DrName string 26 | ModRes int64 27 | } 28 | 29 | // GetDrName : 30 | func (s *ShardHash) GetDrName() string { 31 | return s.DrName 32 | } 33 | 34 | // IsMatch : 35 | func (s *ShardHash) IsMatch(seqID int64, shardNum int64) bool { 36 | modRes := seqID % shardNum 37 | if s.ModRes == modRes { 38 | return true 39 | } 40 | return false 41 | } 42 | 43 | // Shard : shard interface 44 | type Shard interface { 45 | GetDrName() string 46 | IsMatch(seqID int64, shardNum int64) bool 47 | } 48 | -------------------------------------------------------------------------------- /lib/gcache/base/gcache_base.go: -------------------------------------------------------------------------------- 1 | package gcachebase 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/go-redis/redis" 7 | gcachedriver "github.com/jameschz/go-base/lib/gcache/driver" 8 | gcacheregion "github.com/jameschz/go-base/lib/gcache/region" 9 | ) 10 | 11 | // DataSource : 12 | type DataSource struct { 13 | ID string 14 | Name string 15 | Node string // connection node 16 | RedisConn *redis.Client // redis connection 17 | } 18 | 19 | // Cache : 20 | type Cache struct { 21 | Node string // connection node 22 | Driver *gcachedriver.Driver // driver ptr 23 | Region *gcacheregion.Region // region ptr 24 | DataSource *DataSource // datasource 25 | RedisConn *redis.Client // redis connection 26 | } 27 | 28 | // ICache : 29 | type ICache interface { 30 | Connect(k string) error 31 | Close() error 32 | Set(k string, v string) error 33 | SetTTL(k string, v string, exp time.Duration) error 34 | Get(k string) (string, error) 35 | Del(k string) error 36 | Incr(k string) (int64, error) 37 | IncrBy(k string, val int64) (int64, error) 38 | SetNX(k string, v string, exp time.Duration) (bool, error) 39 | TTL(k string) (int64, error) 40 | Expire(k string, expiration time.Duration) (bool, error) 41 | } 42 | -------------------------------------------------------------------------------- /lib/gcache/gcache_test.go: -------------------------------------------------------------------------------- 1 | package gcache 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | gcacheredis "github.com/jameschz/go-base/lib/gcache/redis" 8 | "github.com/jameschz/go-base/lib/gutil" 9 | ) 10 | 11 | func TestSet(t *testing.T) { 12 | cache := D("default") 13 | gutil.Dump(cache.Set("test1", "test1")) 14 | gutil.Dump(cache.SetTTL("test2", "test1", 5*time.Second)) 15 | } 16 | 17 | func TestGet(t *testing.T) { 18 | cache := D("default") 19 | gutil.Dump(cache.Get("test1")) 20 | gutil.Dump(cache.Get("test2")) 21 | } 22 | 23 | func TestDel(t *testing.T) { 24 | cache := D("default") 25 | gutil.Dump(cache.Del("test1")) 26 | } 27 | 28 | func TestRSet(t *testing.T) { 29 | cache := R("user") 30 | gutil.Dump(cache.Set("user1", "user1")) 31 | gutil.Dump(cache.SetTTL("user2", "user2", 5*time.Second)) 32 | } 33 | 34 | func TestRGet(t *testing.T) { 35 | cache := R("user") 36 | gutil.Dump(cache.Get("user1")) 37 | } 38 | 39 | func TestRDel(t *testing.T) { 40 | cache := R("user") 41 | gutil.Dump(cache.Del("user1")) 42 | } 43 | 44 | func TestQueue(t *testing.T) { 45 | r := &gcacheredis.Redis{} 46 | e := r.Connect("127.0.0.1:6379") 47 | if e != nil { 48 | gutil.Dump(e) 49 | } 50 | r.RedisConn.LPush("q1", 1) 51 | rs := r.RedisConn.RPop("q1").Val() 52 | gutil.Dump(rs) 53 | } 54 | -------------------------------------------------------------------------------- /lib/gcache/gcache.go: -------------------------------------------------------------------------------- 1 | package gcache 2 | 3 | import ( 4 | gcachebase "github.com/jameschz/go-base/lib/gcache/base" 5 | gcachedriver "github.com/jameschz/go-base/lib/gcache/driver" 6 | gcacheredis "github.com/jameschz/go-base/lib/gcache/redis" 7 | gcacheregion "github.com/jameschz/go-base/lib/gcache/region" 8 | ) 9 | 10 | // D : connect by driver 11 | func D(cs string) (ic gcachebase.ICache) { 12 | // init driver 13 | gcachedriver.Init() 14 | // get mq driver 15 | _cDriver := gcachedriver.GetDriver(cs) 16 | if len(_cDriver.Type) == 0 { 17 | panic("gcache> cache driver error") 18 | } 19 | // mq initialize 20 | switch _cDriver.Type { 21 | case "redis": 22 | cache := &gcacheredis.Redis{} 23 | cache.Driver = _cDriver 24 | ic = cache 25 | default: 26 | panic("gcache> unknown driver type") 27 | } 28 | return ic 29 | } 30 | 31 | // R : connect by region 32 | func R(rs string) (ic gcachebase.ICache) { 33 | // init all 34 | gcacheregion.Init() 35 | // get db cluster 36 | _cRegion := gcacheregion.GetRegion(rs) 37 | if len(_cRegion.Name) == 0 { 38 | panic("gcache> cache region error") 39 | } 40 | // db initialize 41 | switch _cRegion.Driver.Type { 42 | case "redis": 43 | cache := &gcacheredis.Redis{} 44 | cache.Region = _cRegion 45 | cache.Driver = _cRegion.Driver 46 | ic = cache 47 | default: 48 | panic("gcache> unknown region driver type") 49 | } 50 | return ic 51 | } 52 | -------------------------------------------------------------------------------- /lib/gdo/gdo.go: -------------------------------------------------------------------------------- 1 | package gdo 2 | 3 | import ( 4 | gdobase "github.com/jameschz/go-base/lib/gdo/base" 5 | gdocluster "github.com/jameschz/go-base/lib/gdo/cluster" 6 | gdodriver "github.com/jameschz/go-base/lib/gdo/driver" 7 | gdomysql "github.com/jameschz/go-base/lib/gdo/mysql" 8 | ) 9 | 10 | // D : connect by driver 11 | func D(dbs string) (idb gdobase.IDb) { 12 | // init driver 13 | gdodriver.Init() 14 | // get db driver 15 | dbDriver := gdodriver.GetDriver(dbs) 16 | if len(dbDriver.Type) == 0 { 17 | panic("gdo> db driver error") 18 | } 19 | // db initialize 20 | var err error 21 | switch dbDriver.Type { 22 | case "mysql": 23 | mysql := &gdomysql.Mysql{} 24 | err = mysql.Connect(dbDriver) 25 | idb = mysql 26 | default: 27 | panic("gdo> unknown driver type") 28 | } 29 | // throw error 30 | if err != nil { 31 | panic("gdo> db connect error") 32 | } 33 | return idb 34 | } 35 | 36 | // C : connect by cluster 37 | func C(cs string) (idb gdobase.IDb) { 38 | // init all 39 | gdocluster.Init() 40 | // get db cluster 41 | dbCluster := gdocluster.GetCluster(cs) 42 | if len(dbCluster.Type) == 0 { 43 | panic("gdo> db cluster error") 44 | } 45 | // db initialize 46 | switch dbCluster.Type { 47 | case "mysql": 48 | mysql := &gdomysql.Mysql{} 49 | mysql.Cluster = dbCluster 50 | idb = mysql 51 | default: 52 | panic("gdo> unknown driver type") 53 | } 54 | return idb 55 | } 56 | -------------------------------------------------------------------------------- /etc/local/database.yaml: -------------------------------------------------------------------------------- 1 | # pool configs 2 | _pool: &_pool 3 | pool_max_active: 10 4 | pool_max_active_sec: 300 5 | pool_max_idle: 5 6 | pool_max_idle_sec: 600 7 | 8 | # database configs 9 | _base: &_base 10 | type: "mysql" 11 | host: "127.0.0.1" 12 | port: "3306" 13 | user: "root" 14 | pass: "passwd" 15 | charset: "utf8" 16 | <<: *_pool 17 | 18 | # db drivers list 19 | drivers: 20 | base: 21 | db: gb_base 22 | <<: *_base 23 | user_shard_1: 24 | db: gb_user_shard_1 25 | <<: *_base 26 | user_shard_2: 27 | db: gb_user_shard_2 28 | <<: *_base 29 | user_shard_3: 30 | db: gb_user_shard_3 31 | <<: *_base 32 | user_shard_4: 33 | db: gb_user_shard_4 34 | <<: *_base 35 | log_shard_1: 36 | db: gb_log_shard_1 37 | <<: *_base 38 | log_shard_2: 39 | db: gb_log_shard_2 40 | <<: *_base 41 | 42 | # db cluster configs 43 | clusters: 44 | user: 45 | type: mysql 46 | algo: hash 47 | seq_id: id 48 | shards: 49 | - driver: user_shard_1 50 | mod_res: 1 51 | - driver: user_shard_2 52 | mod_res: 2 53 | - driver: user_shard_3 54 | mod_res: 3 55 | - driver: user_shard_4 56 | mod_res: 0 57 | log: 58 | type: mysql 59 | algo: range 60 | seq_id: id 61 | shards: 62 | - driver: log_shard_1 63 | fr_num: 1 64 | to_num: 500 65 | - driver: log_shard_2 66 | fr_num: 501 67 | to_num: 1000 68 | 69 | 70 | -------------------------------------------------------------------------------- /lib/gdo/parser/gdo_parser.go: -------------------------------------------------------------------------------- 1 | package gdoparser 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | ) 7 | 8 | const ( 9 | // Select : 10 | Select = 1 << iota 11 | // Insert : 12 | Insert 13 | // Update : 14 | Update 15 | // Delete : 16 | Delete 17 | ) 18 | 19 | // GetSQLType : 20 | func GetSQLType(sql string) int { 21 | sqlType := -1 22 | sqlArr := strings.Split(sql, " ") 23 | if len(sqlArr) > 0 { 24 | switch strings.ToUpper(sqlArr[0]) { 25 | case "SELECT": 26 | sqlType = Select 27 | case "INSERT": 28 | sqlType = Insert 29 | case "UPDATE": 30 | sqlType = Update 31 | case "DELETE": 32 | sqlType = Delete 33 | } 34 | } 35 | return sqlType 36 | } 37 | 38 | // GetSeqIDPos : 39 | func GetSeqIDPos(sql string, seqID string) int { 40 | sqlIdx := -1 41 | var rs [][]string 42 | switch GetSQLType(sql) { 43 | //case Select: 44 | //case Insert: 45 | //case Update: 46 | //case Delete: 47 | default: 48 | rs = regexp.MustCompile(`([^\s,]+)=([^\s,=]+)`).FindAllStringSubmatch(sql, -1) 49 | //gutil.Dump(rs) 50 | } 51 | if len(rs) > 0 { 52 | for k, v := range rs { 53 | find := false 54 | for _, x := range v { 55 | if x == seqID { 56 | find = true 57 | } 58 | } 59 | if find { 60 | sqlIdx = k 61 | break 62 | } 63 | } 64 | } 65 | return sqlIdx 66 | } 67 | 68 | // GetSeqIDVal : 69 | func GetSeqIDVal(sql string, seqID string, args ...interface{}) interface{} { 70 | pos := GetSeqIDPos(sql, seqID) 71 | if pos >= 0 { 72 | return args[pos] 73 | } 74 | return nil 75 | } 76 | -------------------------------------------------------------------------------- /lib/gdo/base/gdo_base.go: -------------------------------------------------------------------------------- 1 | package gdobase 2 | 3 | import ( 4 | "database/sql" 5 | 6 | gdocluster "github.com/jameschz/go-base/lib/gdo/cluster" 7 | gdodriver "github.com/jameschz/go-base/lib/gdo/driver" 8 | ) 9 | 10 | // DataSource : 11 | type DataSource struct { 12 | ID string 13 | Name string 14 | Conn *sql.DB 15 | } 16 | 17 | // Db : 18 | type Db struct { 19 | Driver *gdodriver.Driver // driver 20 | Cluster *gdocluster.Cluster // cluster 21 | DataSource *DataSource // datasource 22 | Conn *sql.DB // db connection 23 | Tx *sql.Tx // transaction 24 | TxBegin bool // begin tx 25 | TableName string // table name 26 | } 27 | 28 | // IDb : 29 | type IDb interface { 30 | Connect(driver *gdodriver.Driver) error 31 | T(tableName string) (db IDb) 32 | Close() error 33 | Begin() error 34 | Commit() error 35 | Rollback() error 36 | Max(field string) (val int64, err error) 37 | Min(field string) (val int64, err error) 38 | Shard(sql string, params ...interface{}) error 39 | Query(sql string, params ...interface{}) (rows *sql.Rows, err error) 40 | Exec(sql string, params ...interface{}) (res sql.Result, err error) 41 | Select(field string, where string, params ...interface{}) (rows *sql.Rows, err error) 42 | FetchStructs(rows *sql.Rows, data interface{}) (res *[]interface{}, err error) 43 | FetchStruct(rows *sql.Rows, data interface{}) (res interface{}, err error) 44 | FetchMaps(rows *sql.Rows) (res *[]map[string]interface{}, err error) 45 | FetchMap(rows *sql.Rows) (res map[string]interface{}, err error) 46 | Insert(sql string, params ...interface{}) (id int64, err error) 47 | Update(sql string, params ...interface{}) (affect int64, err error) 48 | Delete(where string, params ...interface{}) (affect int64, err error) 49 | } 50 | -------------------------------------------------------------------------------- /lib/gmq/driver/gmq_driver.go: -------------------------------------------------------------------------------- 1 | package gmqdriver 2 | 3 | import ( 4 | "hash/crc32" 5 | "math/rand" 6 | "time" 7 | 8 | "github.com/jameschz/go-base/lib/config" 9 | ) 10 | 11 | // Driver : 12 | type Driver struct { 13 | Type string 14 | Algo string 15 | Nodes []string 16 | } 17 | 18 | var ( 19 | _mqDrivers map[string]*Driver 20 | ) 21 | 22 | // Init : 23 | func Init() bool { 24 | if len(_mqDrivers) > 0 { 25 | return true 26 | } 27 | drivers := config.Load("queue").GetStringMap("drivers") 28 | _mqDrivers = make(map[string]*Driver, len(drivers)) 29 | for _mqName, _mqConf := range drivers { 30 | // convert interface map 31 | _mqDriver := _mqConf.(map[string]interface{}) 32 | // get nodes config 33 | _mqNodesList := _mqDriver["nodes"].([]interface{}) 34 | _mqNodes := make([]string, len(_mqNodesList)) 35 | for k, v := range _mqNodesList { 36 | _mqNodes[k] = v.(string) 37 | } 38 | // check driver 39 | driver := &Driver{ 40 | Type: _mqDriver["type"].(string), 41 | Algo: _mqDriver["algo"].(string), 42 | Nodes: _mqNodes, 43 | } 44 | // check driver 45 | if len(driver.Algo) == 0 || 46 | len(driver.Nodes) == 0 { 47 | panic("gmq> init driver error") 48 | } 49 | // save driver 50 | _mqDrivers[_mqName] = driver 51 | } 52 | return true 53 | } 54 | 55 | // GetDriver : 56 | func GetDriver(ds string) (driver *Driver) { 57 | if _, r := _mqDrivers[ds]; !r { 58 | panic("gmq> can not find driver") 59 | } 60 | driver = _mqDrivers[ds] 61 | return driver 62 | } 63 | 64 | // GetShardNode : 65 | func (d *Driver) GetShardNode(s string) string { 66 | var sk int 67 | switch d.Algo { 68 | case "rand": 69 | rand.Seed(time.Now().UnixNano()) 70 | sk = rand.Intn(len(d.Nodes)) 71 | case "hash": 72 | code := crc32.ChecksumIEEE([]byte(s)) 73 | sk = int(code) % len(d.Nodes) 74 | } 75 | return d.Nodes[sk] 76 | } 77 | -------------------------------------------------------------------------------- /lib/proto/proto.go: -------------------------------------------------------------------------------- 1 | package proto 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "fmt" 7 | "net" 8 | "strconv" 9 | 10 | proto_base "github.com/golang/protobuf/proto" 11 | ) 12 | 13 | // Message : message interface 14 | type Message interface { 15 | proto_base.Message 16 | } 17 | 18 | // SendMessage : send message to conn 19 | func SendMessage(conn net.Conn, msg Message) (err error) { 20 | var ( 21 | sendBytes []byte 22 | sendBuf [4]byte 23 | readLen int 24 | ) 25 | // protobuf pack msg 26 | if sendBytes, err = proto_base.Marshal(msg); err != nil { 27 | return err 28 | } 29 | // get msg length 30 | binary.BigEndian.PutUint32(sendBuf[:4], uint32(len(sendBytes))) 31 | // send msg length 32 | if readLen, err = conn.Write(sendBuf[:4]); readLen != 4 && err != nil { 33 | if readLen == 0 { 34 | return errors.New("proto.SendMessage length is zero") 35 | } 36 | return err 37 | } 38 | // send msg data 39 | if readLen, err = conn.Write(sendBytes); err != nil { 40 | if readLen == 0 { 41 | return errors.New("proto.SendMessage write error") 42 | } 43 | return err 44 | } 45 | return nil 46 | } 47 | 48 | // ReadMessage : read message from conn to buf 49 | func ReadMessage(conn net.Conn, buf []byte, msg Message) (err error) { 50 | var ( 51 | pkgLen uint32 52 | readLen int 53 | ) 54 | // read msg length 55 | if _, err = conn.Read(buf[:4]); err != nil { 56 | return errors.New("proto.ReadMessage read error") 57 | } 58 | // get msg length 59 | pkgLen = binary.BigEndian.Uint32(buf[:4]) 60 | fmt.Println("> pkgLen : ", strconv.FormatInt(int64(pkgLen), 10)) 61 | // get msg data 62 | if readLen, err = conn.Read(buf[:pkgLen]); readLen != int(pkgLen) || err != nil { 63 | if err == nil { 64 | return errors.New("proto.ReadMessage length error") 65 | } 66 | return err 67 | } 68 | // protobuf unpack msg 69 | if err = proto_base.Unmarshal(buf[:pkgLen], msg); err != nil { 70 | return err 71 | } 72 | return nil 73 | } 74 | -------------------------------------------------------------------------------- /lib/example/getcdclient/example_getcdclient.go: -------------------------------------------------------------------------------- 1 | package examplegetcdclient 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/jameschz/go-base/lib/getcd" 8 | "github.com/jameschz/go-base/lib/gutil" 9 | ) 10 | 11 | // TestPut : 12 | func TestPut() { 13 | // init client 14 | c := getcd.Client() 15 | defer c.Close() 16 | // set resource 17 | c.Put("/host/192.168.1.1", "123") 18 | c.Put("/host/102.168.1.2", "333") 19 | c.Put("/room/100001", "192.168.1.1") 20 | c.Put("/room/100002", "192.168.1.2") 21 | c.PutWithLease("/host/tmp", "ttt", 5) 22 | } 23 | 24 | // TestGet : 25 | func TestGet() { 26 | // init client 27 | c := getcd.Client() 28 | defer c.Close() 29 | // get resource 30 | r, err := c.Get("/room") 31 | if err != nil { 32 | gutil.Dump(err) 33 | } 34 | gutil.Dump("test get >>>") 35 | for k, v := range r { 36 | gutil.Dump(k, v) 37 | } 38 | // get with sora 39 | r, err = c.GetWithSort("/host", "value|desc") 40 | if err != nil { 41 | gutil.Dump(err) 42 | } 43 | gutil.Dump("test get sorted >>>") 44 | for k, v := range r { 45 | gutil.Dump(k, v) 46 | } 47 | } 48 | 49 | // TestDel : 50 | func TestDel() { 51 | // init client 52 | c := getcd.Client() 53 | defer c.Close() 54 | // set resource 55 | c.Del("/host") 56 | } 57 | 58 | // TestKA : 59 | func TestKA() { 60 | // init client 61 | c := getcd.Client() 62 | defer c.Close() 63 | // keep alive 64 | c.KeepAlive("/host/ka", "1", 3) 65 | } 66 | 67 | // TestSync : 68 | func TestSync() { 69 | // init client 70 | c := getcd.Client() 71 | defer c.Close() 72 | // do trans 73 | s1 := "abc" 74 | c.Sync("mutex2", func() error { 75 | gutil.Dump(s1) 76 | time.Sleep(5 * time.Second) 77 | return nil 78 | }) 79 | } 80 | 81 | // TestWatch : 82 | func TestWatch() { 83 | // init client 84 | c := getcd.Client() 85 | defer c.Close() 86 | // watch resource 87 | rch := c.WatchWithPrefix("foo") 88 | for wresp := range rch { 89 | for _, ev := range wresp.Events { 90 | fmt.Printf("%s %q : %q\n", ev.Type, ev.Kv.Key, ev.Kv.Value) 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/gcache/region/gcache_region.go: -------------------------------------------------------------------------------- 1 | package gcacheregion 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | "time" 7 | 8 | "github.com/jameschz/go-base/lib/config" 9 | gcachedriver "github.com/jameschz/go-base/lib/gcache/driver" 10 | ) 11 | 12 | // Region : 13 | type Region struct { 14 | Driver *gcachedriver.Driver 15 | Name string 16 | Desc string 17 | TTL int 18 | } 19 | 20 | var ( 21 | _cRegions map[string]*Region 22 | ) 23 | 24 | // private static func 25 | func stringToInt(secondStr string) int { 26 | digits := strings.Split(secondStr, "*") 27 | sum := 1 28 | for _, v := range digits { 29 | i, err := strconv.Atoi(strings.Trim(v, " ")) 30 | if err != nil { 31 | panic("gcache> ttl format error") 32 | } 33 | sum *= i 34 | } 35 | return sum 36 | } 37 | 38 | // Init : 39 | func Init() bool { 40 | // init once 41 | if len(_cRegions) > 0 { 42 | return true 43 | } 44 | // init drivers 45 | gcachedriver.Init() 46 | // init regions 47 | regions := config.Load("cache").GetStringMap("regions") 48 | _cRegions = make(map[string]*Region, len(regions)) 49 | for _rName, _rConf := range regions { 50 | // convert interface map 51 | _cRegion := _rConf.(map[string]interface{}) 52 | // check driver 53 | region := &Region{ 54 | Driver: gcachedriver.GetDriver(_cRegion["driver"].(string)), 55 | Name: _cRegion["name"].(string), 56 | Desc: _cRegion["desc"].(string), 57 | TTL: stringToInt(_cRegion["ttl"].(string)), 58 | } 59 | // check region 60 | if len(region.Name) == 0 || 61 | region.TTL <= 0 { 62 | panic("gcache> region format error") 63 | } 64 | // save region 65 | _cRegions[_rName] = region 66 | } 67 | return true 68 | } 69 | 70 | // GetRegion : 71 | func GetRegion(rs string) (region *Region) { 72 | if _, r := _cRegions[rs]; !r { 73 | panic("gmq> can not find region") 74 | } 75 | region = _cRegions[rs] 76 | return region 77 | } 78 | 79 | // GetKey : 80 | func (r *Region) GetKey(k string) string { 81 | return r.Name + "_" + k 82 | } 83 | 84 | // GetExp : 85 | func (r *Region) GetExp() time.Duration { 86 | return time.Duration(r.TTL) * time.Second 87 | } 88 | -------------------------------------------------------------------------------- /lib/base/base_list.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import "container/list" 4 | 5 | // List : 6 | type List struct { 7 | list *list.List 8 | } 9 | 10 | // NewList : 11 | func NewList() *List { 12 | list := list.New() 13 | return &List{list} 14 | } 15 | 16 | // Back : 17 | func (list *List) Back() *list.Element { 18 | return list.list.Back() 19 | } 20 | 21 | // BackValue : 22 | func (list *List) BackValue() interface{} { 23 | return list.list.Back().Value 24 | } 25 | 26 | // Front : 27 | func (list *List) Front() *list.Element { 28 | return list.list.Front() 29 | } 30 | 31 | // FrontValue : 32 | func (list *List) FrontValue() interface{} { 33 | return list.list.Front().Value 34 | } 35 | 36 | // Len : 37 | func (list *List) Len() int { 38 | return list.list.Len() 39 | } 40 | 41 | // InsertAfter : 42 | func (list *List) InsertAfter(v interface{}, m *list.Element) *list.Element { 43 | return list.list.InsertAfter(v, m) 44 | } 45 | 46 | // InsertBefore : 47 | func (list *List) InsertBefore(v interface{}, m *list.Element) *list.Element { 48 | return list.list.InsertBefore(v, m) 49 | } 50 | 51 | // MoveAfter : 52 | func (list *List) MoveAfter(e *list.Element, m *list.Element) { 53 | list.list.MoveAfter(e, m) 54 | } 55 | 56 | // MoveBefore : 57 | func (list *List) MoveBefore(e *list.Element, m *list.Element) { 58 | list.list.MoveBefore(e, m) 59 | } 60 | 61 | // MoveToBack : 62 | func (list *List) MoveToBack(e *list.Element) { 63 | list.list.MoveToBack(e) 64 | } 65 | 66 | // MoveToFront : 67 | func (list *List) MoveToFront(e *list.Element) { 68 | list.list.MoveToFront(e) 69 | } 70 | 71 | // PushBack : 72 | func (list *List) PushBack(v interface{}) *list.Element { 73 | return list.list.PushBack(v) 74 | } 75 | 76 | // PushFront : 77 | func (list *List) PushFront(v interface{}) *list.Element { 78 | return list.list.PushFront(v) 79 | } 80 | 81 | // PushBackList : 82 | func (list *List) PushBackList(l *list.List) { 83 | list.list.PushBackList(l) 84 | } 85 | 86 | // PushFrontList : 87 | func (list *List) PushFrontList(l *list.List) { 88 | list.list.PushFrontList(l) 89 | } 90 | 91 | // Remove : 92 | func (list *List) Remove(e *list.Element) interface{} { 93 | return list.list.Remove(e) 94 | } 95 | -------------------------------------------------------------------------------- /lib/gdo/driver/gdo_driver.go: -------------------------------------------------------------------------------- 1 | package gdodriver 2 | 3 | import ( 4 | "github.com/jameschz/go-base/lib/config" 5 | ) 6 | 7 | // Driver : 8 | type Driver struct { 9 | Type string 10 | Host string 11 | Port string 12 | User string 13 | Pass string 14 | Charset string 15 | DbName string 16 | // pool attrs start 17 | PoolMaxActive int 18 | PoolMaxActiveSec int 19 | PoolMaxIdle int 20 | PoolMaxIdleSec int 21 | // pool attrs end 22 | } 23 | 24 | var ( 25 | _dbDrivers map[string]*Driver 26 | ) 27 | 28 | // Init : 29 | func Init() bool { 30 | if len(_dbDrivers) > 0 { 31 | return true 32 | } 33 | drivers := config.Load("database").GetStringMap("drivers") 34 | _dbDrivers = make(map[string]*Driver, len(drivers)) 35 | for _dbName, _dbConf := range drivers { 36 | // convert interface map 37 | _dbDriver := _dbConf.(map[string]interface{}) 38 | // check driver 39 | driver := &Driver{ 40 | Type: _dbDriver["type"].(string), 41 | Host: _dbDriver["host"].(string), 42 | Port: _dbDriver["port"].(string), 43 | User: _dbDriver["user"].(string), 44 | Pass: _dbDriver["pass"].(string), 45 | Charset: _dbDriver["charset"].(string), 46 | DbName: _dbDriver["db"].(string), 47 | // pool attrs start 48 | PoolMaxActive: _dbDriver["pool_max_active"].(int), 49 | PoolMaxActiveSec: _dbDriver["pool_max_active_sec"].(int), 50 | PoolMaxIdle: _dbDriver["pool_max_idle"].(int), 51 | PoolMaxIdleSec: _dbDriver["pool_max_idle_sec"].(int), 52 | // pool attrs end 53 | } 54 | // check driver 55 | if len(driver.Type) == 0 || 56 | len(driver.Host) == 0 || 57 | len(driver.Port) == 0 || 58 | len(driver.User) == 0 || 59 | len(driver.Charset) == 0 || 60 | len(driver.DbName) == 0 { 61 | panic("gdo> init driver error") 62 | } 63 | // save driver 64 | _dbDrivers[_dbName] = driver 65 | } 66 | return true 67 | } 68 | 69 | // GetDriver : 70 | func GetDriver(drName string) (driver *Driver) { 71 | if _, r := _dbDrivers[drName]; !r { 72 | panic("gdo> can not find driver") 73 | } 74 | driver = _dbDrivers[drName] 75 | return driver 76 | } 77 | 78 | // GetDrivers : 79 | func GetDrivers() map[string]*Driver { 80 | return _dbDrivers 81 | } 82 | -------------------------------------------------------------------------------- /lib/getcd/getcd_test.go: -------------------------------------------------------------------------------- 1 | package getcd 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/jameschz/go-base/lib/gutil" 9 | ) 10 | 11 | func TestPut(t *testing.T) { 12 | // init client 13 | c := Client() 14 | defer c.Close() 15 | // set resource 16 | c.Put("/host/192.168.1.1", "123") 17 | c.Put("/host/102.168.1.2", "333") 18 | c.Put("/room/100001", "192.168.1.1") 19 | c.Put("/room/100002", "192.168.1.2") 20 | c.PutWithLease("/host/tmp", "ttt", 5) 21 | } 22 | 23 | func TestIncr(a *testing.T) { 24 | // init client 25 | c := Client() 26 | defer c.Close() 27 | // incr 28 | c.Incr("/host/192.168.1.1") 29 | } 30 | 31 | func TestDecr(a *testing.T) { 32 | // init client 33 | c := Client() 34 | defer c.Close() 35 | // incr 36 | c.Decr("/host/192.168.1.1") 37 | } 38 | 39 | func TestGet(t *testing.T) { 40 | // init client 41 | c := Client() 42 | defer c.Close() 43 | // get resource 44 | r, err := c.Get("/room") 45 | if err != nil { 46 | gutil.Dump(err) 47 | } 48 | gutil.Dump("test get >>>") 49 | for k, v := range r { 50 | gutil.Dump(k, v) 51 | } 52 | // get with sora 53 | r, err = c.GetWithSort("/host", "value|asc") 54 | if err != nil { 55 | gutil.Dump(err) 56 | } 57 | gutil.Dump("test get sorted >>>") 58 | for k, v := range r { 59 | gutil.Dump(k, v) 60 | } 61 | } 62 | 63 | func TestDel(t *testing.T) { 64 | // init client 65 | c := Client() 66 | defer c.Close() 67 | // del resource 68 | c.Del("/host") 69 | } 70 | 71 | func TestSync(t *testing.T) { 72 | // init client 73 | c := Client() 74 | defer c.Close() 75 | // do trans 76 | s1 := "abc" 77 | c.Sync("mutex2", func() error { 78 | gutil.Dump(s1) 79 | time.Sleep(5 * time.Second) 80 | return nil 81 | }) 82 | } 83 | 84 | func TestWatch(t *testing.T) { 85 | // init client 86 | c := Client() 87 | defer c.Close() 88 | // watch resource 89 | rch := c.WatchWithPrefix("foo") 90 | for wresp := range rch { 91 | for _, ev := range wresp.Events { 92 | fmt.Printf("%s %q : %q\n", ev.Type, ev.Kv.Key, ev.Kv.Value) 93 | } 94 | } 95 | } 96 | 97 | func TestKA(t *testing.T) { 98 | // init client 99 | c := Client() 100 | defer c.Close() 101 | // keep alive 102 | c.KeepAlive("/host/ka", "1", 3) 103 | } 104 | -------------------------------------------------------------------------------- /lib/gcache/driver/gcache_driver.go: -------------------------------------------------------------------------------- 1 | package gcachedriver 2 | 3 | import ( 4 | "hash/crc32" 5 | "math/rand" 6 | "time" 7 | 8 | "github.com/jameschz/go-base/lib/config" 9 | ) 10 | 11 | // Driver : 12 | type Driver struct { 13 | Type string 14 | Name string 15 | Algo string 16 | Nodes []string 17 | // pool attrs start 18 | PoolInitSize int 19 | PoolIdleMinSize int 20 | PoolIdleTimeoutMin int 21 | // pool attrs end 22 | } 23 | 24 | var ( 25 | _cDrivers map[string]*Driver 26 | ) 27 | 28 | // Init : 29 | func Init() bool { 30 | if len(_cDrivers) > 0 { 31 | return true 32 | } 33 | drivers := config.Load("cache").GetStringMap("drivers") 34 | _cDrivers = make(map[string]*Driver, len(drivers)) 35 | for _cName, _cConf := range drivers { 36 | // convert interface map 37 | _cDriver := _cConf.(map[string]interface{}) 38 | // get nodes config 39 | _cNodesList := _cDriver["nodes"].([]interface{}) 40 | _cNodes := make([]string, len(_cNodesList)) 41 | for k, v := range _cNodesList { 42 | _cNodes[k] = v.(string) 43 | } 44 | // check driver 45 | driver := &Driver{ 46 | Type: _cDriver["type"].(string), 47 | Name: _cDriver["name"].(string), 48 | Algo: _cDriver["algo"].(string), 49 | Nodes: _cNodes, 50 | // pool attrs start 51 | PoolInitSize: _cDriver["pool_init_size"].(int), 52 | PoolIdleMinSize: _cDriver["pool_idle_min_size"].(int), 53 | PoolIdleTimeoutMin: _cDriver["pool_idle_timeout_min"].(int), 54 | // pool attrs end 55 | } 56 | // check driver 57 | if len(driver.Algo) == 0 || 58 | len(driver.Nodes) == 0 { 59 | panic("gmq> init driver error") 60 | } 61 | // save driver 62 | _cDrivers[_cName] = driver 63 | } 64 | return true 65 | } 66 | 67 | // GetDriver : 68 | func GetDriver(ds string) (driver *Driver) { 69 | if _, r := _cDrivers[ds]; !r { 70 | panic("gmq> can not find driver") 71 | } 72 | driver = _cDrivers[ds] 73 | return driver 74 | } 75 | 76 | // GetDrivers : 77 | func GetDrivers() map[string]*Driver { 78 | return _cDrivers 79 | } 80 | 81 | // GetShardNode : 82 | func (d *Driver) GetShardNode(s string) string { 83 | var sk int 84 | switch d.Algo { 85 | case "rand": 86 | rand.Seed(time.Now().UnixNano()) 87 | sk = rand.Intn(len(d.Nodes)) 88 | case "hash": 89 | code := crc32.ChecksumIEEE([]byte(s)) 90 | sk = int(code) % len(d.Nodes) 91 | } 92 | return d.Nodes[sk] 93 | } 94 | -------------------------------------------------------------------------------- /lib/example/gcacheclient/example_gcacheclient.go: -------------------------------------------------------------------------------- 1 | package examplegcacheclient 2 | 3 | import ( 4 | "strconv" 5 | "time" 6 | 7 | "github.com/jameschz/go-base/lib/gcache" 8 | gcachepool "github.com/jameschz/go-base/lib/gcache/pool" 9 | "github.com/jameschz/go-base/lib/gutil" 10 | ) 11 | 12 | // TestDriver : 13 | func TestDriver() { 14 | // print debug info 15 | gcachepool.SetDebug(true) 16 | // get by driver 17 | cache := gcache.D("default") 18 | cache.Set("test0", "test1") 19 | cache.Close() 20 | 21 | cache = gcache.D("default") 22 | cache.Set("test1", "test1") 23 | 24 | cache = gcache.D("default") 25 | cache.Set("test2", "test1") 26 | cache.Close() 27 | 28 | cache = gcache.D("default") 29 | cache.Set("test3", "test1") 30 | cache.Close() 31 | 32 | cache = gcache.D("default") 33 | cache.Set("test4", "test1") 34 | cache.Close() 35 | 36 | cache = gcache.D("default") 37 | cache.Set("test5", "test1") 38 | cache.Close() 39 | cache = gcache.D("default") 40 | cache.Set("test6", "test1") 41 | cache.Close() 42 | cache = gcache.D("default") 43 | cache.Set("test7", "test1") 44 | cache.Close() 45 | cache = gcache.D("default") 46 | cache.Set("test8", "test1") 47 | cache.Close() 48 | cache = gcache.D("default") 49 | cache.Set("test9", "test1") 50 | cache.Close() 51 | cache = gcache.D("default") 52 | cache.Set("test10", "test1") 53 | cache.Close() 54 | cache = gcache.D("default") 55 | cache.Set("test11", "test1") 56 | cache.Close() 57 | 58 | // test connection timeout 59 | for i := 0; i < 5; i++ { 60 | time.Sleep(10 * time.Second) 61 | cache = gcache.D("default") 62 | cache.Set("test.timeout."+strconv.Itoa(i), "test1") 63 | cache.Close() 64 | } 65 | 66 | } 67 | 68 | // TestRegion : 69 | func TestRegion() { 70 | // print debug info 71 | gcachepool.SetDebug(true) 72 | // get user region 73 | cache := gcache.R("user") 74 | gutil.Dump(cache.Set("user1", "user1")) 75 | gutil.Dump(cache.SetTTL("user2", "user2", 5*time.Second)) 76 | gutil.Dump(cache.Get("user1")) 77 | gutil.Dump(cache.Del("user1")) 78 | 79 | incr, _ := cache.Incr("incr") 80 | gutil.Dump("IncrR:", incr) 81 | 82 | incr, _ = cache.Incr("incr") 83 | gutil.Dump("IncrR:", incr) 84 | 85 | incrBy, _ := cache.IncrBy("incr", 10) 86 | gutil.Dump("IncrByR:", incrBy) 87 | 88 | result, _ := cache.SetNX("SetNXR", "v", 10*time.Second) 89 | gutil.Dump("SetNXR:", result) 90 | 91 | cache.Close() 92 | } 93 | -------------------------------------------------------------------------------- /lib/example/socketserver/example_socketserver.go: -------------------------------------------------------------------------------- 1 | package examplesocketserver 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jameschz/go-base/lib/logger" 6 | "net" 7 | "time" 8 | ) 9 | 10 | func _handleConn(conn net.Conn) { 11 | buffer := make([]byte, 1024) 12 | for { 13 | // recv message 14 | n, err := conn.Read(buffer) 15 | if err != nil { 16 | logger.Warn(conn.RemoteAddr().String(), "buffer error", err) 17 | break 18 | } 19 | // init message 20 | data := buffer[:n] 21 | if len(data) > 0 { 22 | // init channles 23 | msgS := make(chan []byte) 24 | msgH := make(chan byte) 25 | // handle message 26 | go _getMessage(conn, msgS) 27 | // handle heartbeat 28 | go _heartBeating(conn, msgH, 4) 29 | // send messages to channel 30 | msgS <- data 31 | msgH <- data[0] 32 | } 33 | } 34 | // connection closed log 35 | fmt.Println(">", conn.RemoteAddr().String(), "disconnected.") 36 | logger.Info(conn.RemoteAddr().String(), "connection closed!") 37 | // close connection 38 | conn.Close() 39 | } 40 | 41 | func _getMessage(conn net.Conn, msgS chan []byte) { 42 | select { 43 | case data := <-msgS: 44 | // get data string 45 | dataS := string(data) 46 | // print message string 47 | fmt.Println("> get message :", dataS) 48 | // todo : handle message logic 49 | // ... 50 | } 51 | close(msgS) 52 | } 53 | 54 | func _heartBeating(conn net.Conn, msgH chan byte, timeout int) { 55 | select { 56 | case hb := <-msgH: 57 | // set next deadline time 58 | hbS := string(hb) 59 | fmt.Println("> get heartbeat :", hbS) 60 | logger.Info(conn.RemoteAddr().String(), "get heartbeat :", hbS) 61 | conn.SetDeadline(time.Now().Add(time.Duration(timeout) * time.Second)) 62 | break 63 | } 64 | close(msgH) 65 | } 66 | 67 | // Server : 68 | func Server(addr string) { 69 | listener, err := net.Listen("tcp", addr) 70 | // listen error 71 | if err != nil { 72 | fmt.Println("> error:", err) 73 | } 74 | defer listener.Close() 75 | // wait for client 76 | for { 77 | conn, err := listener.Accept() 78 | // accept error 79 | if err != nil { 80 | logger.Error(conn.RemoteAddr().String(), "accept err", err) 81 | continue 82 | } 83 | // set timeout 84 | conn.SetReadDeadline(time.Now().Add(time.Second * 10)) 85 | // connected log 86 | fmt.Println(">", conn.RemoteAddr().String(), "connected.") 87 | logger.Info(conn.RemoteAddr().String(), "connect success!") 88 | // conn handler 89 | go _handleConn(conn) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /doc/tpl/Makefile: -------------------------------------------------------------------------------- 1 | #include .env 2 | 3 | PROJECTNAME=$(shell basename "$(PWD)") 4 | 5 | # Go related variables. 6 | GOBASE=$(shell pwd) 7 | GOPATH=$(GOBASE)/vendor:$(GOBASE) 8 | GOBIN=$(GOBASE)/bin 9 | GOFILES=$(wildcard *.go) 10 | 11 | # Make is verbose in Linux. Make it silent. 12 | MAKEFLAGS += --silent 13 | 14 | # Sed commands 15 | ifeq ($(shell uname), Darwin) 16 | SEDICMD=sed -i "" 17 | else 18 | SEDICMD=sed -i 19 | endif 20 | 21 | 22 | ## compile: Compile binary files (default running logic). 23 | compile: go-compile 24 | 25 | ## install: Install missing dependencies. Runs `go get` internally. e.g; make install get=github.com/foo/bar 26 | install: go-get 27 | 28 | ## clean: Clean built & tmp files. Runs `go clean` internally. 29 | clean: go-clean clean-swp 30 | 31 | ## synclib: Sync libraries 32 | synclib: sync-lib 33 | 34 | # Go internal commands. 35 | go-compile: go-mkdir go-clean go-get go-build 36 | go-mkdir: 37 | @echo "make> Prepare dirs ..." 38 | @mkdir -p $(GOBASE)/bin 39 | @mkdir -p $(GOBASE)/log 40 | @mkdir -p $(GOBASE)/vendor 41 | go-build: 42 | @echo "make> Building binary files ..." 43 | @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go build -o $(GOBIN)/$(PROJECTNAME) $(GOFILES) 44 | go-generate: 45 | @echo "make> Generating dependency files ..." 46 | @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go generate $(generate) 47 | go-get: 48 | @echo "make> Checking missing dependencies ..." 49 | @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go get $(get) 50 | go-install: 51 | @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go install $(GOFILES) 52 | go-clean: 53 | @echo "make> Cleaning build cache ..." 54 | @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go clean 55 | 56 | # Development commands 57 | clean-swp: 58 | @echo "make> Cleaning .swp files ..." 59 | @find . -type f -name '*.swp' | xargs rm -f 60 | sync-lib: proj_name := $(notdir $(path)) 61 | sync-lib: proj_name_u := `echo $(proj_name) | tr a-z- A-Z_` 62 | sync-lib: 63 | ifdef path 64 | @echo "make> Sync all libraries to $(path) ..." 65 | @mkdir -p $(path)/src/base; cp -r ./src/base/* $(path)/src/base/ 66 | @echo "make> Change project base ENV to $(proj_name_u)_ENV ..." 67 | @$(SEDICMD) "s/GO_BASE_/$(proj_name_u)_/g" $(path)/src/base/util/base.go 68 | @$(SEDICMD) "s/GO_BASE_/$(proj_name_u)_/g" $(path)/Makefile 69 | else 70 | @echo "make> Usage : make synclib path=/path/to/project" 71 | endif 72 | 73 | #Help commands 74 | .PHONY: help 75 | all: help 76 | help: Makefile 77 | @echo 78 | @echo "make> Choose a command run in "$(PROJECTNAME)":" 79 | @echo 80 | @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' 81 | @echo 82 | -------------------------------------------------------------------------------- /lib/gdo/cluster/gdo_cluster.go: -------------------------------------------------------------------------------- 1 | package gdocluster 2 | 3 | import ( 4 | "github.com/jameschz/go-base/lib/config" 5 | gdodriver "github.com/jameschz/go-base/lib/gdo/driver" 6 | ) 7 | 8 | // Cluster : 9 | type Cluster struct { 10 | Type string 11 | Algo string 12 | SeqID string 13 | Shards []Shard 14 | } 15 | 16 | // Shard : 17 | func (c *Cluster) Shard(seqID int64) (drName string) { 18 | if len(c.Shards) == 0 { 19 | panic("gdo> cluster has no shard") 20 | } 21 | shardNum := int64(len(c.Shards)) 22 | for _, shard := range c.Shards { 23 | if shard.IsMatch(seqID, shardNum) { 24 | drName = shard.GetDrName() 25 | break 26 | } 27 | } 28 | return drName 29 | } 30 | 31 | var ( 32 | _dbClusters map[string]*Cluster 33 | ) 34 | 35 | // Init : 36 | func Init() bool { 37 | // init once 38 | if len(_dbClusters) > 0 { 39 | return true 40 | } 41 | // init drivers 42 | gdodriver.Init() 43 | // init clusters 44 | clusters := config.Load("database").GetStringMap("clusters") 45 | _dbClusters = make(map[string]*Cluster, len(clusters)) 46 | for _cName, _cData := range clusters { 47 | // new cluster ptr 48 | _cCluster := _cData.(map[string]interface{}) 49 | cluster := &Cluster{} 50 | cluster.Type = _cCluster["type"].(string) 51 | cluster.Algo = _cCluster["algo"].(string) 52 | cluster.SeqID = _cCluster["seq_id"].(string) 53 | // get cluster shards 54 | _cShards := _cCluster["shards"].([]interface{}) 55 | shards := make([]Shard, len(_cShards)) 56 | var i = 0 57 | for _, _shardData := range _cShards { 58 | _cShard := make(map[string]interface{}) 59 | for k, v := range _shardData.(map[interface{}]interface{}) { 60 | _cShard[k.(string)] = v 61 | } 62 | switch cluster.Algo { 63 | case "range": 64 | shards[i] = &ShardRange{ 65 | DrName: _cShard["driver"].(string), 66 | FrNum: int64(_cShard["fr_num"].(int)), 67 | ToNum: int64(_cShard["to_num"].(int)), 68 | } 69 | case "hash": 70 | shards[i] = &ShardHash{ 71 | DrName: _cShard["driver"].(string), 72 | ModRes: int64(_cShard["mod_res"].(int)), 73 | } 74 | } 75 | i++ 76 | } 77 | cluster.Shards = shards 78 | // check cluster 79 | if len(cluster.Algo) == 0 || 80 | len(cluster.SeqID) == 0 || 81 | len(cluster.Shards) == 0 { 82 | panic("gdo> init cluster error") 83 | } 84 | // save cluster 85 | _dbClusters[_cName] = cluster 86 | } 87 | return true 88 | } 89 | 90 | // GetCluster : 91 | func GetCluster(cs string) (cluster *Cluster) { 92 | if _, r := _dbClusters[cs]; !r { 93 | panic("gdo> can not find cluster") 94 | } 95 | cluster = _dbClusters[cs] 96 | return cluster 97 | } 98 | -------------------------------------------------------------------------------- /lib/gmq/rabbitmq/gmq_rabbitmq.go: -------------------------------------------------------------------------------- 1 | package gmqrabbitmq 2 | 3 | import ( 4 | gmqbase "github.com/jameschz/go-base/lib/gmq/base" 5 | 6 | "github.com/streadway/amqp" 7 | ) 8 | 9 | // RabbitMQ : 10 | type RabbitMQ struct { 11 | gmqbase.MQ // extends Driver, NodeName 12 | Conn *amqp.Connection // rabbitmq connection 13 | } 14 | 15 | // Connect : 16 | func (r *RabbitMQ) Connect(node string) (err error) { 17 | conn, err := amqp.Dial(node) 18 | if err != nil { 19 | return err 20 | } 21 | r.Conn = conn 22 | r.NodeName = node 23 | return nil 24 | } 25 | 26 | // Close : 27 | func (r *RabbitMQ) Close() (err error) { 28 | return r.Conn.Close() 29 | } 30 | 31 | // Shard : 32 | func (r *RabbitMQ) Shard(k string) (err error) { 33 | // if not connect, do sharding 34 | if r.Conn == nil { 35 | return r.Connect(r.Driver.GetShardNode(k)) 36 | } 37 | return nil 38 | } 39 | 40 | // Publish : 41 | func (r *RabbitMQ) Publish(qName string, qBody string) (err error) { 42 | // shard by queue name 43 | r.Shard(qName) 44 | // open channel 45 | ch, err := r.Conn.Channel() 46 | if err != nil { 47 | return err 48 | } 49 | defer ch.Close() 50 | // open queue 51 | q, err := ch.QueueDeclare( 52 | qName, // name 53 | false, // durable 54 | false, // delete when unused 55 | false, // exclusive 56 | false, // no-wait 57 | nil, // arguments 58 | ) 59 | if err != nil { 60 | return err 61 | } 62 | // publish 63 | return ch.Publish( 64 | "", // exchange 65 | q.Name, // routing key 66 | false, // mandatory 67 | false, // immediate 68 | amqp.Publishing{ 69 | ContentType: "text/plain", 70 | Body: []byte(qBody), 71 | }) 72 | } 73 | 74 | // Consume : 75 | func (r *RabbitMQ) Consume(qName string, callback func(done chan bool, body []byte)) (err error) { 76 | // shard by queue name 77 | r.Shard(qName) 78 | // open channel 79 | ch, err := r.Conn.Channel() 80 | if err != nil { 81 | return err 82 | } 83 | defer ch.Close() 84 | // open queue 85 | q, err := ch.QueueDeclare( 86 | qName, // name 87 | false, // durable 88 | false, // delete when unused 89 | false, // exclusive 90 | false, // no-wait 91 | nil, // arguments 92 | ) 93 | if err != nil { 94 | return err 95 | } 96 | msgs, err := ch.Consume( 97 | q.Name, // queue 98 | "", // consumer 99 | true, // auto-ack 100 | false, // exclusive 101 | false, // no-local 102 | false, // no-wait 103 | nil, // args 104 | ) 105 | if err != nil { 106 | return err 107 | } 108 | done := make(chan bool) 109 | go func() { 110 | for d := range msgs { 111 | callback(done, d.Body) 112 | } 113 | }() 114 | <-done // wait for done 115 | return nil 116 | } 117 | -------------------------------------------------------------------------------- /lib/gdo/pool/gdo_pool.go: -------------------------------------------------------------------------------- 1 | package gdopool 2 | 3 | import ( 4 | "database/sql" 5 | "time" 6 | 7 | base "github.com/jameschz/go-base/lib/base" 8 | gdobase "github.com/jameschz/go-base/lib/gdo/base" 9 | gdodriver "github.com/jameschz/go-base/lib/gdo/driver" 10 | gutil "github.com/jameschz/go-base/lib/gutil" 11 | 12 | // import mysql lib 13 | _ "github.com/go-sql-driver/mysql" 14 | ) 15 | 16 | var ( 17 | _debugStatus bool 18 | _dbPoolInit bool 19 | _dbPool *base.Hmap 20 | ) 21 | 22 | // private 23 | func debugPrint(vals ...interface{}) { 24 | if _debugStatus { 25 | gutil.Dump(vals...) 26 | } 27 | } 28 | 29 | // private 30 | func createDataSource(driver *gdodriver.Driver) *gdobase.DataSource { 31 | ds := &gdobase.DataSource{} 32 | ds.Name = driver.DbName 33 | ds.ID = gutil.UUID() 34 | // open db connection 35 | dsn := driver.User + ":" + 36 | driver.Pass + "@tcp(" + 37 | driver.Host + ":" + 38 | driver.Port + ")/" + 39 | driver.DbName + "?charset=" + 40 | driver.Charset 41 | switch driver.Type { 42 | case "mysql": 43 | dbc, err := sql.Open("mysql", dsn) 44 | if err != nil { 45 | panic("gdo> open db error") 46 | } 47 | err = dbc.Ping() 48 | if err != nil { 49 | panic("gdo> ping db error : " + err.Error()) 50 | } 51 | dbc.SetMaxOpenConns(driver.PoolMaxActive) 52 | dbc.SetMaxIdleConns(driver.PoolMaxIdle) 53 | dbc.SetConnMaxLifetime(time.Duration(driver.PoolMaxActiveSec * int(time.Second))) 54 | dbc.SetConnMaxIdleTime(time.Duration(driver.PoolMaxIdleSec * int(time.Second))) 55 | ds.Conn = dbc 56 | } 57 | // for debug 58 | debugPrint("gdopool.createDataSource", ds) 59 | return ds 60 | } 61 | 62 | // private 63 | func releaseDataSource(ds *gdobase.DataSource) { 64 | if ds != nil { 65 | ds = nil 66 | } 67 | // for debug 68 | debugPrint("gdopool.releaseDataSource", ds) 69 | } 70 | 71 | // SetDebug : public 72 | func SetDebug(status bool) { 73 | _debugStatus = status 74 | } 75 | 76 | // Init : public 77 | func Init() (err error) { 78 | // init once 79 | if _dbPoolInit { 80 | return nil 81 | } 82 | // init drivers 83 | gdodriver.Init() 84 | // init pool by drivers 85 | _dbPool = base.NewHmap() 86 | dbDrivers := gdodriver.GetDrivers() 87 | for _, dbDriver := range dbDrivers { 88 | _dbPool.Set(dbDriver.DbName, createDataSource(dbDriver)) 89 | } 90 | // for debug 91 | debugPrint("gdopool.Init", _dbPool) 92 | // init ok status 93 | if err == nil { 94 | _dbPoolInit = true 95 | } 96 | return err 97 | } 98 | 99 | // Fetch : public 100 | func Fetch(dbName string) (ds *gdobase.DataSource, err error) { 101 | // get datasource from pool 102 | ds = _dbPool.Get(dbName).(*gdobase.DataSource) 103 | // return ds 0 104 | return ds, err 105 | } 106 | 107 | // Return : public 108 | func Return(ds *gdobase.DataSource) (err error) { 109 | // release datasource 110 | releaseDataSource(ds) 111 | // return 0 112 | return err 113 | } 114 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #include .env 2 | 3 | PROJECTNAME=$(shell basename "$(PWD)") 4 | 5 | # Go related variables. 6 | GOBASE=$(shell pwd) 7 | GOPATH=$(GOBASE)/vendor:$(GOBASE) 8 | GOBIN=$(GOBASE)/bin 9 | GOFILES=$(wildcard *.go) 10 | 11 | # Make is verbose in Linux. Make it silent. 12 | MAKEFLAGS += --silent 13 | 14 | # Sed commands 15 | ifeq ($(shell uname), Darwin) 16 | SEDICMD=sed -i "" 17 | else 18 | SEDICMD=sed -i 19 | endif 20 | 21 | 22 | ## compile: Compile binary files (default running logic). 23 | compile: go-compile 24 | 25 | ## install: Install missing dependencies. Runs `go get` internally. e.g; make install get=github.com/foo/bar 26 | install: go-get 27 | 28 | ## clean: Clean built & tmp files. Runs `go clean` internally. 29 | clean: go-clean clean-swp 30 | 31 | ## create: Create new project 32 | create: create-project 33 | 34 | ## synclib: Sync libraries 35 | synclib: sync-lib 36 | 37 | # Go internal commands. 38 | go-compile: go-mkdir go-clean go-get go-build 39 | go-mkdir: 40 | @echo "make> Prepare dirs ..." 41 | @mkdir -p $(GOBASE)/bin 42 | @mkdir -p $(GOBASE)/log 43 | @mkdir -p $(GOBASE)/vendor 44 | go-build: 45 | @echo "make> Building binary files ..." 46 | @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go build -o $(GOBIN)/$(PROJECTNAME) $(GOFILES) 47 | go-generate: 48 | @echo "make> Generating dependency files ..." 49 | @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go generate $(generate) 50 | go-get: 51 | @echo "make> Checking missing dependencies ..." 52 | @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go get $(get) 53 | go-install: 54 | @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go install $(GOFILES) 55 | go-clean: 56 | @echo "make> Cleaning build cache ..." 57 | @GOPATH=$(GOPATH) GOBIN=$(GOBIN) go clean 58 | 59 | # Development commands 60 | clean-swp: 61 | @echo "make> Cleaning .swp files ..." 62 | @find . -type f -name '*.swp' | xargs rm -f 63 | create-project: proj_name := $(notdir $(path)) 64 | create-project: proj_name_u := `echo $(proj_name) | tr a-z- A-Z_` 65 | create-project: 66 | ifdef path 67 | @echo "make> Create project '$(proj_name)' at $(path) ..." 68 | @mkdir -p $(path); cp ./.gitignore $(path)/.gitignore; cp ./doc/tpl/main.t $(path)/main.go; cp ./doc/tpl/Makefile $(path)/Makefile 69 | @mkdir -p $(path)/etc/; cp -r ./etc/* $(path)/etc/ 70 | @mkdir -p $(path)/src/base; cp -r ./src/base/* $(path)/src/base/ 71 | @echo "make> Change project base ENV to $(proj_name_u)_ENV ..." 72 | @$(SEDICMD) "s/GO_BASE_/$(proj_name_u)_/g" $(path)/src/base/util/base.go 73 | @$(SEDICMD) "s/GO_BASE_/$(proj_name_u)_/g" $(path)/Makefile 74 | else 75 | @echo "make> Usage : make create path=/path/to/project" 76 | endif 77 | sync-lib: proj_name := $(notdir $(path)) 78 | sync-lib: proj_name_u := `echo $(proj_name) | tr a-z- A-Z_` 79 | sync-lib: 80 | ifdef path 81 | @echo "make> Sync all libraries to $(path) ..." 82 | @mkdir -p $(path)/src/base; cp -r ./src/base/* $(path)/src/base/ 83 | @echo "make> Change project base ENV to $(proj_name_u)_ENV ..." 84 | @$(SEDICMD) "s/GO_BASE_/$(proj_name_u)_/g" $(path)/src/base/util/base.go 85 | @$(SEDICMD) "s/GO_BASE_/$(proj_name_u)_/g" $(path)/Makefile 86 | else 87 | @echo "make> Usage : make synclib path=/path/to/project" 88 | endif 89 | 90 | #Help commands 91 | .PHONY: help 92 | all: help 93 | help: Makefile 94 | @echo 95 | @echo "make> Choose a command run in "$(PROJECTNAME)":" 96 | @echo 97 | @sed -n 's/^##//p' $< | column -t -s ':' | sed -e 's/^/ /' 98 | @echo 99 | -------------------------------------------------------------------------------- /lib/gdo/gdo_test.go: -------------------------------------------------------------------------------- 1 | package gdo 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/jameschz/go-base/lib/gutil" 8 | ) 9 | 10 | func TestD(t *testing.T) { 11 | gutil.Dump(D("demo")) 12 | } 13 | 14 | func TestC(t *testing.T) { 15 | gutil.Dump(C("user")) 16 | gutil.Dump(C("user")) 17 | gutil.Dump(C("log")) 18 | gutil.Dump(C("log")) 19 | } 20 | 21 | func TestSql(t *testing.T) { 22 | res, err := D("demo").T("story").Select("*", "1=1") 23 | gutil.Dump(res, err) 24 | } 25 | 26 | func TestTx(t *testing.T) { 27 | // db init 28 | db := D("demo") 29 | defer db.Close() 30 | // tx begin 31 | db.Begin() 32 | // tx select 33 | id, err := db.T("story").Max("id") 34 | if err != nil { 35 | gutil.Dump(err) 36 | } else { 37 | gutil.Dump("before insert", id) 38 | } 39 | // tx insert 40 | id, err = db.T("story").Insert("title=?,content=?,dtime=?", "title N", "content N", time.Now().Unix()) 41 | if err != nil { 42 | gutil.Dump("insert fail", id, err) 43 | db.Rollback() 44 | } 45 | // tx select 46 | id, err = db.T("story").Max("id") 47 | if err != nil { 48 | gutil.Dump(err) 49 | } else { 50 | gutil.Dump("before commit", id) 51 | } 52 | // tx commit 53 | gutil.Dump("insert ok", id) 54 | db.Commit() 55 | // tx select 56 | id, err = db.T("story").Max("id") 57 | if err != nil { 58 | gutil.Dump(err) 59 | } else { 60 | gutil.Dump("after commit", id) 61 | } 62 | } 63 | 64 | func TestTxNest(t *testing.T) { 65 | // db init 66 | db1 := D("demo") 67 | defer db1.Close() 68 | // tx1 begin 69 | db1.Begin() 70 | gutil.Dump("tx1 begin") 71 | // call tx2 logic 72 | res := func() bool { 73 | // tx2 begin 74 | db2 := D("demo") 75 | defer db2.Close() 76 | db2.Begin() 77 | gutil.Dump("tx2 begin") 78 | // tx2 commit & rollback 79 | id, err := db2.T("story").Insert("title=?,content=?,dtime=?", "title N", "content N", time.Now().Unix()) 80 | if err != nil { 81 | gutil.Dump("tx2 rollback") 82 | db2.Rollback() 83 | return false 84 | } 85 | gutil.Dump("tx2 commit", id) 86 | db2.Commit() 87 | return true 88 | }() 89 | // tx1 logic 90 | if res { 91 | // tx1 commit & rollback 92 | id, err := db1.T("story").Insert("title=?,content=?,dtime=?", "title N", "content N", time.Now().Unix()) 93 | if err != nil { 94 | gutil.Dump("tx1 rollback") 95 | db1.Rollback() 96 | } else { 97 | gutil.Dump("tx1 commit", id) 98 | db1.Commit() 99 | } 100 | } 101 | } 102 | 103 | func TestShard(t *testing.T) { 104 | // db init 105 | db := C("user") 106 | defer db.Close() 107 | // insert 108 | id, err := db.T("user_info").Insert("id=?,name=?,dtime=?", 101, "james", time.Now().Unix()) 109 | if err != nil { 110 | gutil.Dump("insert fail", id, err) 111 | } 112 | // select by cluster 113 | res, err := db.T("user_info").Select("*", "id=?", 101) 114 | gutil.Dump(res, err) 115 | // select by specify 116 | res, err = D("user_shard_1").T("user_info").Select("*", "1=1") 117 | gutil.Dump(res, err) 118 | } 119 | 120 | func TestShardTx(t *testing.T) { 121 | // db init 122 | db := C("user") 123 | defer db.Close() 124 | // tx begin 125 | db.Begin() 126 | // tx insert 127 | id, err := db.T("user_info").Insert("id=?,name=?,dtime=?", 106, "james", time.Now().Unix()) 128 | if err != nil { 129 | gutil.Dump("insert fail", id, err) 130 | db.Rollback() 131 | } 132 | // tx commit 133 | gutil.Dump("insert ok", id) 134 | db.Commit() 135 | } 136 | -------------------------------------------------------------------------------- /lib/logger/logger.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "os" 7 | "time" 8 | 9 | "github.com/jameschz/go-base/lib/gutil" 10 | ) 11 | 12 | type LogStruct struct { 13 | Prefix string 14 | LogInfo []interface{} 15 | } 16 | 17 | var ( 18 | _loggerFile *os.File 19 | // _loggerInfo *log.Logger 20 | // _loggerWarn *log.Logger 21 | // _loggerErr *log.Logger 22 | 23 | _logger *log.Logger 24 | 25 | _DataLog chan LogStruct 26 | ) 27 | 28 | func init() { 29 | 30 | _DataLog = make(chan LogStruct, 1024) 31 | 32 | go InitReceive() 33 | } 34 | 35 | func _openLoggerFile() error { 36 | 37 | date := time.Now().Format("20060102") 38 | logPath := gutil.GetRootPath() + "/log/logger_" + date + ".log" 39 | 40 | _, err := os.Open(logPath) 41 | if err != nil { 42 | if os.IsNotExist(err) { 43 | file, err := os.Create(logPath) 44 | if err != nil { 45 | log.Fatalln("> logger create file error:", err) 46 | 47 | logPath = gutil.GetRootPath() + "/log/logger.log" 48 | } 49 | file.Close() 50 | } 51 | } 52 | 53 | logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) 54 | if err != nil { 55 | log.Fatalln("> logger init error:", err) 56 | return err 57 | } 58 | _loggerFile = logFile 59 | 60 | return nil 61 | } 62 | 63 | func _closeLoggerFile() { 64 | if _loggerFile != nil { 65 | _loggerFile.Close() 66 | } 67 | } 68 | 69 | // Info : log info msg 70 | func Info(msgs ...interface{}) { 71 | // if err := _openLoggerFile(); err == nil { 72 | // _loggerInfo = log.New(io.MultiWriter(_loggerFile), "[Info]", log.Ldate|log.Ltime|log.Lshortfile) 73 | // _loggerInfo.Println(msgs...) 74 | // _closeLoggerFile() 75 | // } 76 | 77 | _DataLog <- LogStruct{"[Info]", msgs} 78 | } 79 | 80 | // Warn : log warn msg 81 | func Warn(msgs ...interface{}) { 82 | // if err := _openLoggerFile(); err == nil { 83 | // _loggerWarn = log.New(io.MultiWriter(_loggerFile), "[Warn]", log.Ldate|log.Ltime|log.Lshortfile) 84 | // _loggerWarn.Println(msgs...) 85 | // _closeLoggerFile() 86 | // } 87 | 88 | _DataLog <- LogStruct{"[Warn]", msgs} 89 | } 90 | 91 | // Error : log error msg 92 | func Error(msgs ...interface{}) { 93 | // if err := _openLoggerFile(); err == nil { 94 | // _loggerErr = log.New(io.MultiWriter(_loggerFile), "[Error]", log.Ldate|log.Ltime|log.Lshortfile) 95 | // _loggerErr.Println(msgs...) 96 | // _closeLoggerFile() 97 | // } 98 | 99 | _DataLog <- LogStruct{"[Error]", msgs} 100 | } 101 | 102 | func WriteLog(logs []LogStruct) { 103 | 104 | if err := _openLoggerFile(); err == nil { 105 | _logger = log.New(io.MultiWriter(_loggerFile), "", log.Ldate|log.Ltime|log.Lshortfile) 106 | 107 | for _, log := range logs { 108 | 109 | _logger.SetPrefix(log.Prefix) 110 | _logger.Println(log.LogInfo...) 111 | } 112 | 113 | _closeLoggerFile() 114 | } 115 | } 116 | 117 | func InitReceive() { 118 | 119 | go func() { 120 | 121 | for keepGoing := true; keepGoing; { 122 | var batch []LogStruct 123 | expire := time.After(30 * time.Second) 124 | for { 125 | select { 126 | 127 | case dataLog := <-_DataLog: 128 | 129 | batch = append(batch, dataLog) 130 | if len(batch) == cap(_DataLog) { 131 | goto done 132 | } 133 | 134 | case <-expire: 135 | goto done 136 | } 137 | } 138 | 139 | done: 140 | if len(batch) > 0 { 141 | WriteLog(batch) 142 | } 143 | } 144 | }() 145 | } 146 | -------------------------------------------------------------------------------- /doc/sql/test.sql: -------------------------------------------------------------------------------- 1 | -- gb_demo -- 2 | CREATE DATABASE IF NOT EXISTS `gb_base` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 3 | 4 | USE `gb_base`; 5 | 6 | CREATE TABLE IF NOT EXISTS `story` ( 7 | `id` int(10) NOT NULL AUTO_INCREMENT, 8 | `title` varchar(255) NOT NULL DEFAULT '', 9 | `content` text NOT NULL, 10 | `dtime` int(10) NOT NULL DEFAULT 0, 11 | PRIMARY KEY (`id`), 12 | KEY `dtime` (`dtime`) 13 | ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC; 14 | 15 | CREATE TABLE IF NOT EXISTS `seq_user` ( 16 | `id` int(10) NOT NULL AUTO_INCREMENT, 17 | `dtime` int(10) NOT NULL DEFAULT 0, 18 | PRIMARY KEY (`id`), 19 | KEY `dtime` (`dtime`) 20 | ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC; 21 | 22 | -- gb_user_shard_1 -- 23 | 24 | CREATE DATABASE IF NOT EXISTS `gb_user_shard_1` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 25 | 26 | USE `gb_user_shard_1`; 27 | 28 | CREATE TABLE IF NOT EXISTS `user_info` ( 29 | `id` int(10) NOT NULL AUTO_INCREMENT, 30 | `name` varchar(255) NOT NULL DEFAULT '', 31 | `dtime` int(10) NOT NULL DEFAULT 0, 32 | PRIMARY KEY (`id`), 33 | KEY `dtime` (`dtime`) 34 | ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC; 35 | 36 | -- gb_user_shard_2 -- 37 | 38 | CREATE DATABASE IF NOT EXISTS `gb_user_shard_2` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 39 | 40 | USE `gb_user_shard_2`; 41 | 42 | CREATE TABLE IF NOT EXISTS `user_info` ( 43 | `id` int(10) NOT NULL AUTO_INCREMENT, 44 | `name` varchar(255) NOT NULL DEFAULT '', 45 | `dtime` int(10) NOT NULL DEFAULT 0, 46 | PRIMARY KEY (`id`), 47 | KEY `dtime` (`dtime`) 48 | ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC; 49 | 50 | -- gb_user_shard_3 -- 51 | 52 | CREATE DATABASE IF NOT EXISTS `gb_user_shard_3` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 53 | 54 | USE `gb_user_shard_3`; 55 | 56 | CREATE TABLE IF NOT EXISTS `user_info` ( 57 | `id` int(10) NOT NULL AUTO_INCREMENT, 58 | `name` varchar(255) NOT NULL DEFAULT '', 59 | `dtime` int(10) NOT NULL DEFAULT 0, 60 | PRIMARY KEY (`id`), 61 | KEY `dtime` (`dtime`) 62 | ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC; 63 | 64 | -- gb_user_shard_4 -- 65 | 66 | CREATE DATABASE IF NOT EXISTS `gb_user_shard_4` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 67 | 68 | USE `gb_user_shard_4`; 69 | 70 | CREATE TABLE IF NOT EXISTS `user_info` ( 71 | `id` int(10) NOT NULL AUTO_INCREMENT, 72 | `name` varchar(255) NOT NULL DEFAULT '', 73 | `dtime` int(10) NOT NULL DEFAULT 0, 74 | PRIMARY KEY (`id`), 75 | KEY `dtime` (`dtime`) 76 | ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC; 77 | 78 | -- gb_log_shard_1 -- 79 | 80 | CREATE DATABASE IF NOT EXISTS `gb_log_shard_1` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 81 | 82 | USE `gb_log_shard_1`; 83 | 84 | -- gb_log_shard_2 -- 85 | 86 | CREATE DATABASE IF NOT EXISTS `gb_log_shard_2` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; 87 | 88 | USE `gb_log_shard_2`; -------------------------------------------------------------------------------- /lib/gcache/pool/gcache_pool.go: -------------------------------------------------------------------------------- 1 | package gcachepool 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | "time" 7 | 8 | "github.com/go-redis/redis" 9 | base "github.com/jameschz/go-base/lib/base" 10 | gcachebase "github.com/jameschz/go-base/lib/gcache/base" 11 | gcachedriver "github.com/jameschz/go-base/lib/gcache/driver" 12 | gcacheregion "github.com/jameschz/go-base/lib/gcache/region" 13 | gutil "github.com/jameschz/go-base/lib/gutil" 14 | ) 15 | 16 | var ( 17 | _debugStatus bool 18 | _cPoolInit bool 19 | _cPool *base.Hmap 20 | ) 21 | 22 | // private 23 | func debugPrint(vals ...interface{}) { 24 | if _debugStatus { 25 | gutil.Dump(vals...) 26 | } 27 | } 28 | 29 | // private 30 | func createDataSource(driver *gcachedriver.Driver, node string) *gcachebase.DataSource { 31 | ds := &gcachebase.DataSource{} 32 | ds.Name = driver.Name 33 | ds.Node = node 34 | ds.ID = gutil.UUID() 35 | switch driver.Type { 36 | case "redis": 37 | //add begin (db select) 38 | addr := node 39 | db := 0 40 | nodeArr := strings.Split(node, ":") 41 | if len(nodeArr) > 2 { 42 | var build strings.Builder 43 | build.WriteString(nodeArr[0]) 44 | build.WriteString(":") 45 | build.WriteString(nodeArr[1]) 46 | 47 | addr = build.String() 48 | db, _ = strconv.Atoi(nodeArr[2]) 49 | } 50 | //add end 51 | ds.RedisConn = redis.NewClient(&redis.Options{ 52 | // Basic Settings 53 | Addr: addr, 54 | DB: db, 55 | // Pool Settings 56 | PoolSize: driver.PoolInitSize, 57 | MinIdleConns: driver.PoolIdleMinSize, 58 | IdleTimeout: time.Duration(driver.PoolIdleTimeoutMin * int(time.Minute)), 59 | }) 60 | if ds.RedisConn == nil { 61 | panic("gcache> new redis client error") 62 | } 63 | _, err := ds.RedisConn.Ping().Result() 64 | if err != nil { 65 | panic("gcache> ping redis error : " + err.Error()) 66 | } 67 | } 68 | // for debug 69 | debugPrint("gcachepool.createDataSource", ds) 70 | return ds 71 | } 72 | 73 | // private 74 | func releaseDataSource(ds *gcachebase.DataSource) { 75 | if ds != nil { 76 | ds = nil 77 | } 78 | // for debug 79 | debugPrint("gcachepool.releaseDataSource", ds) 80 | } 81 | 82 | // private 83 | func getDataSourceKey(name string, node string) string { 84 | return name + ":" + node 85 | } 86 | 87 | // SetDebug : public 88 | func SetDebug(status bool) { 89 | _debugStatus = status 90 | } 91 | 92 | // Init : public 93 | func Init() (err error) { 94 | // init once 95 | if _cPoolInit { 96 | return nil 97 | } 98 | // init drivers 99 | gcachedriver.Init() 100 | // init pool by drivers 101 | _cPool = base.NewHmap() 102 | cDrivers := gcachedriver.GetDrivers() 103 | for cName, cDriver := range cDrivers { 104 | for _, cNode := range cDriver.Nodes { 105 | cKey := getDataSourceKey(cName, cNode) 106 | _cPool.Set(cKey, createDataSource(cDriver, cNode)) 107 | } 108 | } 109 | // heart beat 110 | go func() { 111 | time.Sleep(1 * time.Minute) 112 | for _, ds := range _cPool.Data() { 113 | ds.(*gcachebase.DataSource).RedisConn.Ping() 114 | } 115 | }() 116 | 117 | // init regions 118 | gcacheregion.Init() 119 | 120 | // for debug 121 | debugPrint("gcachepool.Init", _cPool) 122 | // init ok status 123 | if err == nil { 124 | _cPoolInit = true 125 | } 126 | return err 127 | } 128 | 129 | // Fetch : public 130 | func Fetch(cName string, cNode string) (ds *gcachebase.DataSource, err error) { 131 | // get data source key 132 | cKey := getDataSourceKey(cName, cNode) 133 | // get driver by name 134 | ds = _cPool.Get(cKey).(*gcachebase.DataSource) 135 | // return ds 0 136 | return ds, err 137 | } 138 | 139 | // Return : public 140 | func Return(ds *gcachebase.DataSource) (err error) { 141 | // release datasource 142 | releaseDataSource(ds) 143 | return err 144 | } 145 | -------------------------------------------------------------------------------- /lib/gcache/redis/gcache_redis.go: -------------------------------------------------------------------------------- 1 | package gcacheredis 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/go-redis/redis" 7 | gcachebase "github.com/jameschz/go-base/lib/gcache/base" 8 | gcachepool "github.com/jameschz/go-base/lib/gcache/pool" 9 | ) 10 | 11 | var ( 12 | _cTimeout = time.Hour 13 | ) 14 | 15 | // Redis : 16 | type Redis struct { 17 | gcachebase.Cache // extends base cache 18 | } 19 | 20 | // Connect : 21 | func (r *Redis) Connect(k string) error { 22 | // connect once 23 | if r.RedisConn == nil { 24 | // gdopool 25 | gcachepool.Init() 26 | name := r.Driver.Name 27 | node := r.Driver.GetShardNode(k) 28 | dataSource, err := gcachepool.Fetch(name, node) 29 | // init redis vars 30 | r.Node = node 31 | r.DataSource = dataSource 32 | r.RedisConn = dataSource.RedisConn 33 | return err 34 | } 35 | return nil 36 | } 37 | 38 | // Close : 39 | func (r *Redis) Close() error { 40 | // close once 41 | if r.RedisConn != nil { 42 | gcachepool.Return(r.DataSource) 43 | r.DataSource = nil 44 | r.RedisConn = nil 45 | r.Node = "" 46 | return nil 47 | } 48 | return nil 49 | } 50 | 51 | // Set : 52 | func (r *Redis) Set(k string, v string) error { 53 | // default exp 54 | exp := _cTimeout 55 | // use region exp 56 | if r.Region != nil { 57 | exp = r.Region.GetExp() 58 | } 59 | return r.SetTTL(k, v, exp) 60 | } 61 | 62 | // SetTTL : 63 | func (r *Redis) SetTTL(k string, v string, exp time.Duration) error { 64 | // use region key 65 | if r.Region != nil { 66 | k = r.Region.GetKey(k) 67 | } 68 | // connect 69 | r.Connect(k) 70 | // set kv 71 | _, err := r.RedisConn.Set(k, v, exp).Result() 72 | if err != nil { 73 | return err 74 | } 75 | return nil 76 | } 77 | 78 | // Get : 79 | func (r *Redis) Get(k string) (string, error) { 80 | // use region key 81 | if r.Region != nil { 82 | k = r.Region.GetKey(k) 83 | } 84 | // connect 85 | r.Connect(k) 86 | // get kv 87 | v, err := r.RedisConn.Get(k).Result() 88 | if err == redis.Nil { 89 | return "", nil 90 | } else if err != nil { 91 | return "", err 92 | } 93 | return v, nil 94 | } 95 | 96 | // Del : 97 | func (r *Redis) Del(k string) error { 98 | // use region key 99 | if r.Region != nil { 100 | k = r.Region.GetKey(k) 101 | } 102 | // connect 103 | r.Connect(k) 104 | // del kv 105 | _, err := r.RedisConn.Del(k).Result() 106 | if err == redis.Nil { 107 | return nil 108 | } else if err != nil { 109 | return err 110 | } 111 | return nil 112 | } 113 | 114 | // Incr : 115 | func (r *Redis) Incr(k string) (int64, error) { 116 | // use region key 117 | if r.Region != nil { 118 | k = r.Region.GetKey(k) 119 | } 120 | // connect 121 | r.Connect(k) 122 | // get kv 123 | v, err := r.RedisConn.Incr(k).Result() 124 | if err == redis.Nil { 125 | return 0, nil 126 | } else if err != nil { 127 | return 0, err 128 | } 129 | return v, nil 130 | } 131 | 132 | // IncrBy : 133 | func (r *Redis) IncrBy(k string, val int64) (int64, error) { 134 | // use region key 135 | if r.Region != nil { 136 | k = r.Region.GetKey(k) 137 | } 138 | // connect 139 | r.Connect(k) 140 | // get kv 141 | v, err := r.RedisConn.IncrBy(k, val).Result() 142 | if err == redis.Nil { 143 | return 0, nil 144 | } else if err != nil { 145 | return 0, err 146 | } 147 | return v, nil 148 | } 149 | 150 | // SetNX : 151 | func (r *Redis) SetNX(k string, v string, exp time.Duration) (bool, error) { 152 | // use region key 153 | if r.Region != nil { 154 | k = r.Region.GetKey(k) 155 | } 156 | // connect 157 | r.Connect(k) 158 | // set kv 159 | result, err := r.RedisConn.SetNX(k, v, exp).Result() 160 | if err != nil { 161 | return false, err 162 | } 163 | return result, nil 164 | } 165 | 166 | // TTL : 167 | func (r *Redis) TTL(k string) (int64, error) { 168 | // use region key 169 | if r.Region != nil { 170 | k = r.Region.GetKey(k) 171 | } 172 | // connect 173 | r.Connect(k) 174 | // get kv 175 | v, err := r.RedisConn.TTL(k).Result() 176 | if err == redis.Nil { 177 | return 0, nil 178 | } else if err != nil { 179 | return 0, err 180 | } 181 | return int64(v.Seconds()), nil 182 | } 183 | 184 | // Expire : 185 | func (r *Redis) Expire(k string, expiration time.Duration) (bool, error) { 186 | // use region key 187 | if r.Region != nil { 188 | k = r.Region.GetKey(k) 189 | } 190 | // connect 191 | r.Connect(k) 192 | // get kv 193 | v, err := r.RedisConn.Expire(k, expiration).Result() 194 | if err == redis.Nil { 195 | return v, nil 196 | } else if err != nil { 197 | return v, err 198 | } 199 | return v, nil 200 | } 201 | -------------------------------------------------------------------------------- /lib/gdo/pool/gdo_pool.go.old: -------------------------------------------------------------------------------- 1 | package gdopool 2 | 3 | import ( 4 | "database/sql" 5 | "errors" 6 | "sync" 7 | 8 | base "github.com/jameschz/go-base/lib/base" 9 | gdobase "github.com/jameschz/go-base/lib/gdo/base" 10 | gdodriver "github.com/jameschz/go-base/lib/gdo/driver" 11 | gutil "github.com/jameschz/go-base/lib/gutil" 12 | 13 | // import mysql lib 14 | _ "github.com/go-sql-driver/mysql" 15 | ) 16 | 17 | var ( 18 | _debugStatus bool 19 | _dbPoolInit bool 20 | _dbPoolIdle map[string]*base.Stack 21 | _dbPoolActive map[string]*base.Hmap 22 | _dbPoolLock sync.Mutex 23 | ) 24 | 25 | // private 26 | func debugPrint(vals ...interface{}) { 27 | if _debugStatus == true { 28 | gutil.Dump(vals...) 29 | } 30 | } 31 | 32 | // private 33 | func createDataSource(driver *gdodriver.Driver) *gdobase.DataSource { 34 | ds := &gdobase.DataSource{} 35 | ds.Name = driver.DbName 36 | ds.ID = gutil.UUID() 37 | // open db connection 38 | dsn := driver.User + ":" + 39 | driver.Pass + "@tcp(" + 40 | driver.Host + ":" + 41 | driver.Port + ")/" + 42 | driver.DbName + "?charset=" + 43 | driver.Charset 44 | switch driver.Type { 45 | case "mysql": 46 | dbc, err := sql.Open("mysql", dsn) 47 | if err != nil { 48 | panic("gdo> open db error") 49 | } 50 | err = dbc.Ping() 51 | if err != nil { 52 | panic("gdo> ping db error : " + err.Error()) 53 | } 54 | ds.Conn = dbc 55 | } 56 | // for debug 57 | debugPrint("gdopool.createDataSource", ds) 58 | return ds 59 | } 60 | 61 | // private 62 | func releaseDataSource(ds *gdobase.DataSource) { 63 | if ds != nil { 64 | ds.Conn.Close() 65 | ds = nil 66 | } 67 | // for debug 68 | debugPrint("gdopool.releaseDataSource", ds) 69 | } 70 | 71 | // SetDebug : public 72 | func SetDebug(status bool) { 73 | _debugStatus = status 74 | } 75 | 76 | // Init : public 77 | func Init() (err error) { 78 | // init once 79 | if _dbPoolInit == true { 80 | return nil 81 | } 82 | // init drivers 83 | gdodriver.Init() 84 | // init pool by drivers 85 | dbDrivers := gdodriver.GetDrivers() 86 | _dbPoolIdle = make(map[string]*base.Stack, 0) 87 | _dbPoolActive = make(map[string]*base.Hmap, 0) 88 | for dbName, dbDriver := range dbDrivers { 89 | _dbPoolIdle[dbName] = base.NewStack() 90 | _dbPoolActive[dbName] = base.NewHmap() 91 | for i := 0; i < dbDriver.PoolInitSize; i++ { 92 | _dbPoolIdle[dbName].Push(createDataSource(dbDriver)) 93 | } 94 | } 95 | // for debug 96 | debugPrint("gdopool.Init", _dbPoolIdle, _dbPoolActive) 97 | // init ok status 98 | if err == nil { 99 | _dbPoolInit = true 100 | } 101 | return err 102 | } 103 | 104 | // Fetch : public 105 | func Fetch(dbName string) (ds *gdobase.DataSource, err error) { 106 | // get driver by name 107 | dbDriver := gdodriver.GetDriver(dbName) 108 | // fetch start >>> lock 109 | _dbPoolLock.Lock() 110 | // reach to max active size 111 | activeSize := _dbPoolActive[dbName].Len() 112 | if dbDriver.PoolMaxActive <= activeSize { 113 | return nil, errors.New("gdopool : max active limit") 114 | } 115 | // add if not enough 116 | idleSize := _dbPoolIdle[dbName].Len() 117 | if dbDriver.PoolMinIdle >= idleSize { 118 | idleSizeAdd := dbDriver.PoolMaxIdle - idleSize 119 | for i := 0; i < idleSizeAdd; i++ { 120 | _dbPoolIdle[dbName].Push(createDataSource(dbDriver)) 121 | } 122 | // for debug 123 | debugPrint("gdopool.Fetch Add", _dbPoolIdle[dbName].Len(), _dbPoolActive[dbName].Len()) 124 | } 125 | // fetch from front 126 | if _dbPoolIdle[dbName].Len() >= 1 { 127 | ds = _dbPoolIdle[dbName].Pop().(*gdobase.DataSource) 128 | _dbPoolActive[dbName].Set(ds.ID, ds) 129 | } else { 130 | return nil, errors.New("gdopool : no enough ds") 131 | } 132 | // for debug 133 | debugPrint("gdopool.Fetch", _dbPoolIdle[dbName].Len(), _dbPoolActive[dbName].Len()) 134 | // fetch end >>> unlock 135 | _dbPoolLock.Unlock() 136 | // return ds 0 137 | return ds, err 138 | } 139 | 140 | // Return : public 141 | func Return(ds *gdobase.DataSource) (err error) { 142 | // get driver by name 143 | dbName := ds.Name 144 | dbDriver := gdodriver.GetDriver(dbName) 145 | // return start >>> lock 146 | _dbPoolLock.Lock() 147 | // delete from active list 148 | _dbPoolActive[dbName].Delete(ds.ID) 149 | // return or release 150 | idleSize := _dbPoolIdle[dbName].Len() 151 | if dbDriver.PoolMaxIdle <= idleSize { 152 | releaseDataSource(ds) 153 | } else { 154 | _dbPoolIdle[dbName].Push(ds) 155 | } 156 | 157 | // return end >>> unlock 158 | _dbPoolLock.Unlock() 159 | // for debug 160 | debugPrint("gdopool.Return", _dbPoolIdle[dbName].Len(), _dbPoolActive[dbName].Len()) 161 | return err 162 | } 163 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | 8 | "github.com/jameschz/go-base/lib/config" 9 | "github.com/jameschz/go-base/lib/gutil" 10 | 11 | // -- example code start 12 | examplegcacheclient "github.com/jameschz/go-base/lib/example/gcacheclient" 13 | examplegdoclient "github.com/jameschz/go-base/lib/example/gdoclient" 14 | examplegetcdclient "github.com/jameschz/go-base/lib/example/getcdclient" 15 | examplegmqclient "github.com/jameschz/go-base/lib/example/gmqclient" 16 | examplehttpserver "github.com/jameschz/go-base/lib/example/httpserver" 17 | examplesocketclient "github.com/jameschz/go-base/lib/example/socketclient" 18 | examplesocketserver "github.com/jameschz/go-base/lib/example/socketserver" 19 | "github.com/jameschz/go-base/lib/logger" 20 | // -- example code end 21 | ) 22 | 23 | func help() { 24 | fmt.Println("============================================") 25 | fmt.Println("> rootpath", gutil.GetRootPath()) 26 | fmt.Println("============================================") 27 | // -- example code start 28 | fmt.Println("> http_server : example http server") 29 | fmt.Println("> so_server : example socket server") 30 | fmt.Println("> so_client : example socket client") 31 | fmt.Println("> gdo_query : example gdo query test") 32 | fmt.Println("> gdo_insert : example gdo insert test") 33 | fmt.Println("> gdo_update : example gdo update test") 34 | fmt.Println("> gdo_delete : example gdo delete test") 35 | fmt.Println("> gdo_query_s : example gdo shard query test") 36 | fmt.Println("> gdo_insert_s : example gdo shard insert test") 37 | fmt.Println("> gdo_update_s : example gdo shard update test") 38 | fmt.Println("> gdo_delete_s : example gdo shard delete test") 39 | fmt.Println("> gdo_tx_basic : example gdo transaction test") 40 | fmt.Println("> gcache_all : example gcache all tests") 41 | fmt.Println("> gmq_pub : example gmq publish test") 42 | fmt.Println("> gmq_sub : example gmq comsume test") 43 | fmt.Println("> etcd_ka : example etcd keepalive test") 44 | fmt.Println("> etcd_sync : example etcd sync trans test") 45 | // -- example code end 46 | fmt.Println("============================================") 47 | os.Exit(0) 48 | } 49 | 50 | var ( 51 | _serverHTTPAddr string 52 | _serverSocketAddr string 53 | ) 54 | 55 | func init() { 56 | // init config 57 | _serverHTTPAddr = config.Load("config").GetString("server.http.addr") 58 | _serverSocketAddr = config.Load("config").GetString("server.socket.addr") 59 | } 60 | 61 | func main() { 62 | 63 | // get args 64 | args := os.Args 65 | if len(args) < 2 || args == nil { 66 | help() 67 | } 68 | 69 | // main logic 70 | action := args[1] 71 | switch action { 72 | // -- example code start 73 | case "http_server": 74 | { 75 | fmt.Println("> base http server") 76 | fmt.Println("> listening on", _serverHTTPAddr, "...") 77 | http.HandleFunc("/", examplehttpserver.HTTPIndex) 78 | http.HandleFunc("/hello", examplehttpserver.HTTPHello) 79 | err := http.ListenAndServe(_serverHTTPAddr, nil) 80 | if err != nil { 81 | logger.Error("base server err:", err) 82 | } 83 | } 84 | case "so_server": 85 | { 86 | fmt.Println("> base socket server") 87 | fmt.Println("> bind on " + _serverSocketAddr) 88 | examplesocketserver.Server(_serverSocketAddr) 89 | } 90 | case "so_client": 91 | { 92 | examplesocketclient.Client(_serverSocketAddr) 93 | } 94 | case "gdo_query": 95 | { 96 | examplegdoclient.MysqlQueryBasic() 97 | } 98 | case "gdo_insert": 99 | { 100 | examplegdoclient.MysqlInsertBasic() 101 | } 102 | case "gdo_update": 103 | { 104 | examplegdoclient.MysqlUpdateBasic() 105 | } 106 | case "gdo_delete": 107 | { 108 | examplegdoclient.MysqlDeleteBasic() 109 | } 110 | case "gdo_query_s": 111 | { 112 | examplegdoclient.MysqlQueryShard() 113 | } 114 | case "gdo_insert_s": 115 | { 116 | examplegdoclient.MysqlInsertShard() 117 | } 118 | case "gdo_update_s": 119 | { 120 | examplegdoclient.MysqlUpdateShard() 121 | } 122 | case "gdo_delete_s": 123 | { 124 | examplegdoclient.MysqlDeleteShard() 125 | } 126 | case "gdo_tx_basic": 127 | { 128 | examplegdoclient.MysqlTxBasic() 129 | } 130 | case "gcache_all": 131 | { 132 | examplegcacheclient.TestDriver() 133 | examplegcacheclient.TestRegion() 134 | } 135 | case "gmq_pub": 136 | { 137 | examplegmqclient.RabbitPub() 138 | } 139 | case "gmq_sub": 140 | { 141 | examplegmqclient.RabbitSub() 142 | } 143 | case "etcd_ka": 144 | { 145 | examplegetcdclient.TestKA() 146 | } 147 | case "etcd_sync": 148 | { 149 | examplegetcdclient.TestSync() 150 | } 151 | // -- example code end 152 | default: 153 | { 154 | help() 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /lib/gcache/pool/gcache_pool.go.old: -------------------------------------------------------------------------------- 1 | package gcachepool 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | "strings" 7 | "sync" 8 | 9 | "github.com/go-redis/redis" 10 | base "github.com/jameschz/go-base/lib/base" 11 | gcachebase "github.com/jameschz/go-base/lib/gcache/base" 12 | gcachedriver "github.com/jameschz/go-base/lib/gcache/driver" 13 | gutil "github.com/jameschz/go-base/lib/gutil" 14 | ) 15 | 16 | var ( 17 | _debugStatus bool 18 | _cPoolInit bool 19 | _cPoolIdle map[string]*base.Stack 20 | _cPoolActive map[string]*base.Hmap 21 | _cPoolLock sync.Mutex 22 | ) 23 | 24 | // private 25 | func debugPrint(vals ...interface{}) { 26 | if _debugStatus == true { 27 | gutil.Dump(vals...) 28 | } 29 | } 30 | 31 | // private 32 | func createDataSource(driver *gcachedriver.Driver, node string) *gcachebase.DataSource { 33 | ds := &gcachebase.DataSource{} 34 | ds.Name = driver.Name 35 | ds.Node = node 36 | ds.ID = gutil.UUID() 37 | switch driver.Type { 38 | case "redis": 39 | //add begin (db select) 40 | addr := node 41 | db := 0 42 | nodeArr := strings.Split(node, ":") 43 | if len(nodeArr) > 2 { 44 | var build strings.Builder 45 | build.WriteString(nodeArr[0]) 46 | build.WriteString(":") 47 | build.WriteString(nodeArr[1]) 48 | 49 | addr = build.String() 50 | db, _ = strconv.Atoi(nodeArr[2]) 51 | } 52 | //add end 53 | ds.RedisConn = redis.NewClient(&redis.Options{ 54 | // Addr: node, 55 | Addr: addr, 56 | DB: db, 57 | }) 58 | } 59 | // for debug 60 | debugPrint("gcachepool.createDataSource", ds) 61 | return ds 62 | } 63 | 64 | // private 65 | func releaseDataSource(ds *gcachebase.DataSource) { 66 | if ds.RedisConn != nil { 67 | ds.RedisConn.Close() 68 | ds = nil 69 | } 70 | // for debug 71 | debugPrint("gcachepool.releaseDataSource", ds) 72 | } 73 | 74 | // private 75 | func getDataSourceKey(name string, node string) string { 76 | return name + ":" + node 77 | } 78 | 79 | // SetDebug : public 80 | func SetDebug(status bool) { 81 | _debugStatus = status 82 | } 83 | 84 | // Init : public 85 | func Init() (err error) { 86 | // init once 87 | if _cPoolInit == true { 88 | return nil 89 | } 90 | // init drivers 91 | gcachedriver.Init() 92 | // init pool by drivers 93 | cDrivers := gcachedriver.GetDrivers() 94 | _cPoolIdle = make(map[string]*base.Stack, 0) 95 | _cPoolActive = make(map[string]*base.Hmap, 0) 96 | for cName, cDriver := range cDrivers { 97 | for _, cNode := range cDriver.Nodes { 98 | cKey := getDataSourceKey(cName, cNode) 99 | _cPoolIdle[cKey] = base.NewStack() 100 | _cPoolActive[cKey] = base.NewHmap() 101 | for i := 0; i < cDriver.PoolInitSize; i++ { 102 | _cPoolIdle[cKey].Push(createDataSource(cDriver, cNode)) 103 | } 104 | } 105 | } 106 | // for debug 107 | debugPrint("gcachepool.Init", _cPoolIdle, _cPoolActive) 108 | // init ok status 109 | if err == nil { 110 | _cPoolInit = true 111 | } 112 | return err 113 | } 114 | 115 | // Fetch : public 116 | func Fetch(cName string, cNode string) (ds *gcachebase.DataSource, err error) { 117 | // get data source key 118 | cKey := getDataSourceKey(cName, cNode) 119 | // get driver by name 120 | cDriver := gcachedriver.GetDriver(cName) 121 | // fetch start >>> lock 122 | _cPoolLock.Lock() 123 | // reach to max active size 124 | activeSize := _cPoolActive[cKey].Len() 125 | if cDriver.PoolMaxActive <= activeSize { 126 | return nil, errors.New("gcachepool : max active limit") 127 | } 128 | // add if not enough 129 | idleSize := _cPoolIdle[cKey].Len() 130 | if cDriver.PoolMinIdle >= idleSize { 131 | idleSizeAdd := cDriver.PoolMaxIdle - idleSize 132 | for i := 0; i < idleSizeAdd; i++ { 133 | _cPoolIdle[cKey].Push(createDataSource(cDriver, cNode)) 134 | } 135 | // for debug 136 | debugPrint("gcachepool.Fetch Add", _cPoolIdle[cKey].Len(), _cPoolActive[cKey].Len()) 137 | } 138 | // fetch from front 139 | if _cPoolIdle[cKey].Len() >= 1 { 140 | ds = _cPoolIdle[cKey].Pop().(*gcachebase.DataSource) 141 | _cPoolActive[cKey].Set(ds.ID, ds) 142 | } else { 143 | return nil, errors.New("gcachepool : no enough ds") 144 | } 145 | // for debug 146 | debugPrint("gcachepool.Fetch", _cPoolIdle[cKey].Len(), _cPoolActive[cKey].Len()) 147 | // fetch end >>> unlock 148 | _cPoolLock.Unlock() 149 | // return ds 0 150 | return ds, err 151 | } 152 | 153 | // Return : public 154 | func Return(ds *gcachebase.DataSource) (err error) { 155 | // get data source key 156 | cKey := getDataSourceKey(ds.Name, ds.Node) 157 | // get driver by name 158 | cDriver := gcachedriver.GetDriver(ds.Name) 159 | // return start >>> lock 160 | _cPoolLock.Lock() 161 | // delete from active list 162 | _cPoolActive[cKey].Delete(ds.ID) 163 | // return or release 164 | idleSize := _cPoolIdle[cKey].Len() 165 | if cDriver.PoolMaxIdle <= idleSize { 166 | releaseDataSource(ds) 167 | } else { 168 | _cPoolIdle[cKey].Push(ds) 169 | } 170 | // return end >>> unlock 171 | _cPoolLock.Unlock() 172 | // for debug 173 | debugPrint("gcachepool.Return", _cPoolIdle[cKey].Len(), _cPoolActive[cKey].Len()) 174 | return err 175 | } 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-base 2 | 3 | A simple go framework for development 4 | 5 | > This framework could be easliy copy & create a new project as a project template. And you can also sync the "src/base" libraries to another project by using build-in command line tools. 6 | 7 | ## Features 8 | 9 | * Full Stack. Use Makefile as development & build command line tools, across platform support. 10 | 11 | * NOT only for WEB but for ALL. Proper framework offers the advantage of being extremely scalable. 12 | 13 | * Operational Friendly. Could be simply configured in different environments, such as local, test, release ... 14 | 15 | * Rich Components Support. Provides GDO (GO Database OOP Libs), GMO (Go MQ OOP Libs), GCACHE (Go Cache OOP Libs), and also support Etcd, Protobuf ... 16 | 17 | * Simple to Start. EASY to use, EASY to expand Libs. EASY to copy & create new project. EASY to sync codes between projects. 18 | 19 | ## Installing 20 | 21 | **Set system ENV (Linux & Mac)** 22 | 23 | ``` 24 | vi ~/.bash_profile 25 | ... 26 | export GO_BASE_ENV=[local|test|...|release] 27 | export GO_BASE_ROOT=/path/to/go-base 28 | ... 29 | ``` 30 | 31 | **Run basic examples** 32 | 33 | ``` 34 | git clone https://github.com/jameschz/go-base.git 35 | cd go-base 36 | go mod download 37 | make # Build in Linux & Mac 38 | build.bat # Build in Windows 39 | bin/go-base 40 | ... 41 | ============================================ 42 | > rootpath /data/code/go-base 43 | ============================================ 44 | base http_server : example http server 45 | base so_server : example socket server 46 | base so_client : example socket client 47 | base gdo_query : example gdo query test 48 | base gdo_insert : example gdo insert test 49 | base gdo_update : example gdo update test 50 | base gdo_delete : example gdo delete test 51 | base gdo_tx_basic : example gdo transaction test 52 | base gcache_all : example gcache all tests 53 | base gmq_pub : example gmq publish test 54 | base gmq_sub : example gmq comsume test 55 | base etcd_ka : example etcd keepalive test 56 | base etcd_sync : example etcd sync trans test 57 | ============================================ 58 | ... 59 | bin/go-base http_server 60 | ... 61 | > base http server 62 | > listening on 0.0.0.0:80 ... 63 | ... 64 | bin/go-base so_server 65 | ... 66 | > base socket server 67 | > bind on 0.0.0.0:8080 68 | ... 69 | bin/go-base so_client 70 | ... 71 | > connect success! 72 | > send: 29 text: 1 Hello I'm heartbeat client. 73 | ... 74 | ``` 75 | 76 | ## Development 77 | 78 | **Path introduction** 79 | 80 | * introductions of go-base framework's paths are as follow 81 | 82 | Path|Introduction 83 | --|-- 84 | bin/|run path 85 | log/|log files 86 | etc/local/|default config files 87 | etc/{BASE_ENV}/|config files in GO_BASE_ENV enviornment 88 | src/base/config|config package 89 | src/base/logger|logger package 90 | src/base/gcache|cache package, support : redis ... 91 | src/base/gmq|mq package, support : rabbitmq ... 92 | src/base/gdo|db package, support : mysql ... 93 | src/base/getcd|etcd support package 94 | src/base/gutil|utils functions 95 | src/base/proto|protobuf base package 96 | src/base/example|examples package 97 | 98 | 99 | **How to start new project** 100 | 101 | * use "make create" command to create a new project as follow 102 | 103 | ``` 104 | make create path=/path/to/project-name 105 | ... 106 | make> Create project 'project-name' at /path/to/project-name ... 107 | make> Change project base ENV to PROJECT_NAME_ENV ... 108 | ... 109 | cd /path/to/project-name 110 | make 111 | ... 112 | make> Prepare dirs ... 113 | make> Cleaning build cache ... 114 | make> Checking missing dependencies ... 115 | make> Building binary files ... 116 | ... 117 | bin/project-name 118 | ... 119 | dump> "gutil.GetEnv()" "local" 120 | dump> "gutil.GetRootPath()" "/path/to/project-name" 121 | ... 122 | ``` 123 | 124 | **How to use base library** 125 | 126 | * use "make synclib" command to sync libraries between projects 127 | 128 | ``` 129 | make synclib path=/path/to/project-name 130 | ... 131 | make> Sync all libraries to /path/to/project-name ... 132 | make> Change project base ENV to PROJECT_NAME_ENV ... 133 | ... 134 | cd /path/to/project-name 135 | git status 136 | ... 137 | ``` 138 | 139 | ## Examples 140 | 141 | **main.go** 142 | 143 | > Main examples console, including the example how to use config library. 144 | 145 | **lib/example/gdoclient/example_gdoclient.go** 146 | 147 | > Including db CRUD function examples, now support mysql, keep upgrading. 148 | 149 | **lib/example/gmqclient/example_gmqclient.go** 150 | 151 | > Including message queue basic examples, now support rabbitmq, keep upgrading. 152 | 153 | **lib/example/gcacheclient/example_gcacheclient.go** 154 | 155 | > Including cache basic examples, now support redis, keep upgrading. 156 | 157 | **lib/example/etcdclient/example_etcdclient.go** 158 | 159 | > Including etcd basic examples, keep upgrading. 160 | 161 | **lib/example/socketserver/example_socketserver.go** 162 | 163 | > A simple socket server demo, including heartbeating implemention and how to use logger library. 164 | 165 | **lib/example/socketclient/example_socketclient.go** 166 | 167 | > A simple socket client demo, including heartbeating implemention. 168 | 169 | **lib/example/httpserver/example_httpserver.go** 170 | 171 | > A simple http server demo. 172 | -------------------------------------------------------------------------------- /lib/gutil/gutil.go: -------------------------------------------------------------------------------- 1 | package gutil 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "io/ioutil" 7 | "math/rand" 8 | "net" 9 | "os" 10 | "path/filepath" 11 | "reflect" 12 | "runtime/debug" 13 | "strings" 14 | "time" 15 | "unsafe" 16 | 17 | "github.com/bwmarrin/snowflake" 18 | uuid "github.com/satori/go.uuid" 19 | ) 20 | 21 | ///////////////////////////////////////////////////////////////// 22 | // debug funcs 23 | 24 | // Dump : gutil.Dump 25 | func Dump(vals ...interface{}) { 26 | fmt.Print("dump> ") 27 | for _, v := range vals { 28 | fmt.Printf("%#v ", v) 29 | } 30 | fmt.Printf("\n") 31 | } 32 | 33 | // Throw : gutil.Throw 34 | func Throw(s string, p ...interface{}) { 35 | panic(fmt.Sprintf(s, p...)) 36 | } 37 | 38 | // Catch : gutil.Catch 39 | func Catch() { 40 | if err := recover(); err != nil { 41 | fmt.Println("catch>", err) 42 | debug.PrintStack() 43 | } 44 | } 45 | 46 | ///////////////////////////////////////////////////////////////// 47 | // base funcs 48 | 49 | var ( 50 | _baseEnv = os.Getenv("GO_baseEnv") 51 | _baseRoot = os.Getenv("GO_baseRoot") 52 | ) 53 | 54 | // SetEnv : gutil.SetEnv 55 | func SetEnv(env string) { 56 | _baseEnv = env 57 | } 58 | 59 | // GetEnv : gutil.GetEnv 60 | func GetEnv() string { 61 | // get from file 62 | if len(_baseEnv) == 0 { 63 | _baseEnv = GetFileContent(GetRootPath() + "/etc/env.txt") 64 | _baseEnv = strings.Trim(_baseEnv, "\n") 65 | } 66 | // default local 67 | if len(_baseEnv) == 0 { 68 | _baseEnv = "local" 69 | } 70 | return _baseEnv 71 | } 72 | 73 | // SetRootPath : gutil.SetRootPath 74 | func SetRootPath(path string) { 75 | _baseRoot = path 76 | } 77 | 78 | // GetRootPath : gutil.GetRootPath 79 | func GetRootPath() string { 80 | if len(_baseRoot) == 0 { 81 | _baseRoot, _ = filepath.Abs(".") 82 | } 83 | return _baseRoot 84 | } 85 | 86 | // GetFileContent : gutil.GetFileContent 87 | func GetFileContent(file string) string { 88 | f, err := os.Open(file) 89 | defer f.Close() 90 | if err != nil { 91 | return "" 92 | } 93 | c, err := ioutil.ReadAll(f) 94 | if err != nil { 95 | return "" 96 | } 97 | return string(c) 98 | } 99 | 100 | ///////////////////////////////////////////////////////////////// 101 | // call funcs 102 | 103 | // CallFunc : gutil.CallFunc 104 | func CallFunc(fn interface{}, args []interface{}) { 105 | // check func type 106 | fnType := reflect.TypeOf(fn).String() 107 | // if strings.Contains(fnType, "func") == false { 108 | if !strings.Contains(fnType, "func") { 109 | Throw("CallFunc error: first param expected to be a func") 110 | } 111 | // fill func args 112 | var as = make([]reflect.Value, len(args)) 113 | i := 0 114 | for _, v := range args { 115 | as[i] = reflect.ValueOf(v) 116 | i++ 117 | } 118 | // call func with args 119 | reflect.ValueOf(fn).Call(as) 120 | } 121 | 122 | ///////////////////////////////////////////////////////////////// 123 | // encrypt funcs 124 | 125 | func XOR(bPlain []byte, bKey []byte) []byte { 126 | bCipher := make([]byte, len(bPlain)) 127 | keyLength := len(bKey) 128 | for i, k := range bPlain { 129 | bCipher[i] = k ^ bKey[i%keyLength] 130 | } 131 | return bCipher 132 | } 133 | 134 | func EncryptXOR(sPlain string, bKey []byte) string { 135 | pPlain := *(*reflect.StringHeader)(unsafe.Pointer(&sPlain)) 136 | bPlain := *(*[]byte)(unsafe.Pointer(&pPlain)) 137 | bCipher := XOR(bPlain, bKey) 138 | sCipher := base64.StdEncoding.EncodeToString(bCipher) 139 | return sCipher 140 | } 141 | 142 | func DecryptXOR(sEncypted string, bKey []byte) string { 143 | bEncypted, err := base64.StdEncoding.DecodeString(sEncypted) 144 | if err != nil { 145 | panic("DecryptXOR base64 decode error") 146 | } 147 | bDecypted := XOR(bEncypted, bKey) 148 | return string(bDecypted) 149 | } 150 | 151 | ///////////////////////////////////////////////////////////////// 152 | // util funcs 153 | 154 | // GetMACs : gutil.GetMACs 155 | func GetMACs() (macs []string) { 156 | netInterfaces, err := net.Interfaces() 157 | if err != nil { 158 | Throw("GetMACs error: %v", err) 159 | return macs 160 | } 161 | for _, netInterface := range netInterfaces { 162 | macAddr := netInterface.HardwareAddr.String() 163 | if len(macAddr) == 0 { 164 | continue 165 | } 166 | macs = append(macs, macAddr) 167 | } 168 | return macs 169 | } 170 | 171 | // GetIPs : gutil.GetIPs 172 | func GetIPs() (ips []string) { 173 | interfaceAddrs, err := net.InterfaceAddrs() 174 | if err != nil { 175 | Throw("GetIPs error: %v", err) 176 | return ips 177 | } 178 | for _, interfaceAddr := range interfaceAddrs { 179 | ipNet, isValidIPNet := interfaceAddr.(*net.IPNet) 180 | if isValidIPNet && !ipNet.IP.IsLoopback() { 181 | if ipNet.IP.To4() != nil { 182 | ips = append(ips, ipNet.IP.String()) 183 | } 184 | } 185 | } 186 | return ips 187 | } 188 | 189 | // UUID : gutil.UUID 190 | func UUID() string { 191 | uuid, err := uuid.NewV4() 192 | if err != nil { 193 | Throw("UUID error: %v", err) 194 | } 195 | return uuid.String() 196 | } 197 | 198 | // SFID : gutil.SFID 199 | func SFID() int64 { 200 | rand.Seed(time.Now().UnixNano()) 201 | node, err := snowflake.NewNode(rand.Int63n(1023)) 202 | if err != nil { 203 | Throw("SFID error: %v", err) 204 | } 205 | return node.Generate().Int64() 206 | } 207 | 208 | // RangeInt : gutil.RangeInt 209 | func RangeInt(f int, t int) []int { 210 | a := []int{} 211 | if f <= t { 212 | for { 213 | a = append(a, f) 214 | f++ 215 | if f > t { 216 | break 217 | } 218 | } 219 | } else { 220 | for { 221 | a = append(a, f) 222 | f-- 223 | if f < t { 224 | break 225 | } 226 | } 227 | } 228 | return a 229 | } 230 | 231 | // RangeInt64 : gutil.RangeInt64 232 | func RangeInt64(f int64, t int64) []int64 { 233 | a := []int64{} 234 | if f <= t { 235 | for { 236 | a = append(a, f) 237 | f++ 238 | if f > t { 239 | break 240 | } 241 | } 242 | } else { 243 | for { 244 | a = append(a, f) 245 | f-- 246 | if f < t { 247 | break 248 | } 249 | } 250 | } 251 | return a 252 | } 253 | -------------------------------------------------------------------------------- /lib/getcd/getcd.go: -------------------------------------------------------------------------------- 1 | package getcd 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/coreos/etcd/clientv3" 9 | "github.com/coreos/etcd/clientv3/concurrency" 10 | "github.com/jameschz/go-base/lib/config" 11 | ) 12 | 13 | // Conf : 14 | type Conf struct { 15 | Timeout int 16 | Endpoints []string 17 | } 18 | 19 | // Etcd : 20 | type Etcd struct { 21 | Client *clientv3.Client 22 | } 23 | 24 | // global 25 | var ( 26 | _etcdConf *Conf 27 | _etcdTimeout = 5 * time.Second 28 | ) 29 | 30 | // private 31 | func _sortStrategy(strategy string) (op clientv3.OpOption) { 32 | switch strategy { 33 | case "key|asc": 34 | op = clientv3.WithSort(clientv3.SortByKey, clientv3.SortAscend) 35 | case "key|desc": 36 | op = clientv3.WithSort(clientv3.SortByKey, clientv3.SortDescend) 37 | case "value|asc": 38 | op = clientv3.WithSort(clientv3.SortByValue, clientv3.SortAscend) 39 | case "value|desc": 40 | op = clientv3.WithSort(clientv3.SortByValue, clientv3.SortDescend) 41 | } 42 | if op == nil { 43 | panic("etcd> bad sort strategy") 44 | } 45 | return op 46 | } 47 | 48 | // Init : 49 | func Init() bool { 50 | // init once 51 | if _etcdConf != nil { 52 | return true 53 | } 54 | // init timeout 55 | confs := config.Load("etcd").GetStringMap("config") 56 | _etcdConf = &Conf{Timeout: confs["timeout"].(int)} 57 | // init endpoints 58 | endpoints := confs["endpoints"].([]interface{}) 59 | _etcdConf.Endpoints = make([]string, len(endpoints)) 60 | for id, conf := range endpoints { 61 | _etcdConf.Endpoints[id] = conf.(string) 62 | } 63 | return true 64 | } 65 | 66 | // Client : 67 | func Client() *Etcd { 68 | Init() // init config 69 | c, err := clientv3.New(clientv3.Config{ 70 | Endpoints: _etcdConf.Endpoints, 71 | DialTimeout: time.Duration(_etcdConf.Timeout) * time.Second, 72 | }) 73 | if err != nil { 74 | panic("etcd> init client error") 75 | } 76 | etcd := &Etcd{Client: c} 77 | return etcd 78 | } 79 | 80 | // Close : 81 | func (c *Etcd) Close() { 82 | c.Client.Close() 83 | } 84 | 85 | // Put : 86 | func (c *Etcd) Put(k string, v string) error { 87 | ctx, cancel := context.WithTimeout(context.Background(), _etcdTimeout) 88 | _, err := c.Client.Put(ctx, k, v) 89 | cancel() 90 | if err != nil { 91 | return err 92 | } 93 | return nil 94 | } 95 | 96 | // PutWithLease : 97 | func (c *Etcd) PutWithLease(k string, v string, ts int64) error { 98 | ctx, cancel := context.WithTimeout(context.Background(), _etcdTimeout) 99 | resp, err := c.Client.Grant(ctx, ts) 100 | cancel() 101 | if err != nil { 102 | return err 103 | } 104 | ctx, cancel = context.WithTimeout(context.Background(), _etcdTimeout) 105 | _, err = c.Client.Put(ctx, k, v, clientv3.WithLease(resp.ID)) 106 | cancel() 107 | if err != nil { 108 | return err 109 | } 110 | return nil 111 | 112 | } 113 | 114 | // Incr : 115 | func (c *Etcd) Incr(k string) error { 116 | _, err := concurrency.NewSTM(c.Client, func(s concurrency.STM) error { 117 | v := s.Get(k) 118 | vInt := 0 119 | fmt.Sscanf(v, "%d", &vInt) 120 | s.Put(k, fmt.Sprintf("%d", (vInt+1))) 121 | return nil 122 | }) 123 | if err != nil { 124 | return err 125 | } 126 | return nil 127 | } 128 | 129 | // Decr : 130 | func (c *Etcd) Decr(k string) error { 131 | _, err := concurrency.NewSTM(c.Client, func(s concurrency.STM) error { 132 | v := s.Get(k) 133 | vInt := 0 134 | fmt.Sscanf(v, "%d", &vInt) 135 | s.Put(k, fmt.Sprintf("%d", (vInt-1))) 136 | return nil 137 | }) 138 | if err != nil { 139 | return err 140 | } 141 | return nil 142 | } 143 | 144 | // Get : 145 | func (c *Etcd) Get(k string) (res map[string]string, err error) { 146 | ctx, cancel := context.WithTimeout(context.Background(), _etcdTimeout) 147 | resp, err := c.Client.Get(ctx, k, clientv3.WithPrefix()) 148 | cancel() 149 | if err != nil { 150 | return res, err 151 | } 152 | res = make(map[string]string, len(resp.Kvs)) 153 | for _, v := range resp.Kvs { 154 | res[string(v.Key)] = string(v.Value) 155 | } 156 | return res, err 157 | } 158 | 159 | // GetWithSort : 160 | func (c *Etcd) GetWithSort(k string, strategy string) (res map[string]string, err error) { 161 | ctx, cancel := context.WithTimeout(context.Background(), _etcdTimeout) 162 | resp, err := c.Client.Get(ctx, k, clientv3.WithPrefix(), _sortStrategy(strategy)) 163 | cancel() 164 | if err != nil { 165 | return res, err 166 | } 167 | res = make(map[string]string, len(resp.Kvs)) 168 | for _, v := range resp.Kvs { 169 | res[string(v.Key)] = string(v.Value) 170 | } 171 | return res, err 172 | } 173 | 174 | // GetWithSortLimit : 175 | func (c *Etcd) GetWithSortLimit(k string, strategy string, limit int64) (res map[string]string, err error) { 176 | ctx, cancel := context.WithTimeout(context.Background(), _etcdTimeout) 177 | resp, err := c.Client.Get(ctx, k, clientv3.WithPrefix(), _sortStrategy(strategy), clientv3.WithLimit(limit)) 178 | cancel() 179 | if err != nil { 180 | return res, err 181 | } 182 | res = make(map[string]string, len(resp.Kvs)) 183 | for _, v := range resp.Kvs { 184 | res[string(v.Key)] = string(v.Value) 185 | } 186 | return res, err 187 | } 188 | 189 | // Del : 190 | func (c *Etcd) Del(k string) error { 191 | ctx, cancel := context.WithTimeout(context.Background(), _etcdTimeout) 192 | _, err := c.Client.Delete(ctx, k, clientv3.WithPrefix()) 193 | cancel() 194 | if err != nil { 195 | return err 196 | } 197 | return nil 198 | } 199 | 200 | // Sync : 201 | func (c *Etcd) Sync(mutexStr string, callback func() error) (err error) { 202 | // new session 203 | sess, err := concurrency.NewSession(c.Client) 204 | if err != nil { 205 | return err 206 | } 207 | defer sess.Close() 208 | // new mutex 209 | mutex := concurrency.NewMutex(sess, "/_base/sync/"+mutexStr) 210 | // do lock 211 | if err = mutex.Lock(context.TODO()); err != nil { 212 | return err 213 | } 214 | // do callback 215 | if err = callback(); err != nil { 216 | return err 217 | } 218 | // do unlock 219 | if err = mutex.Unlock(context.TODO()); err != nil { 220 | return err 221 | } 222 | return nil 223 | } 224 | 225 | // WatchWithPrefix : 226 | func (c *Etcd) WatchWithPrefix(k string) clientv3.WatchChan { 227 | return c.Client.Watch(context.Background(), k, clientv3.WithPrefix()) 228 | } 229 | 230 | // KeepAlive : 231 | func (c *Etcd) KeepAlive(k string, v string, ts int64) error { 232 | // grant for one more second 233 | ctx, cancel := context.WithTimeout(context.Background(), _etcdTimeout) 234 | resp, err := c.Client.Grant(ctx, ts+1) 235 | cancel() 236 | if err != nil { 237 | return err 238 | } 239 | ctx, cancel = context.WithTimeout(context.Background(), _etcdTimeout) 240 | _, err = c.Client.Put(ctx, k, v, clientv3.WithLease(resp.ID)) 241 | cancel() 242 | if err != nil { 243 | return err 244 | } 245 | // keep alive forever 246 | var ch = make(chan int) 247 | go func() { 248 | for { 249 | _, err := c.Client.KeepAliveOnce(context.TODO(), resp.ID) 250 | if err != nil { 251 | panic("etcd> keep alive error") 252 | } 253 | sec := time.Duration(ts) 254 | time.Sleep(sec * time.Second) 255 | } 256 | }() 257 | <-ch // wait error 258 | return err 259 | } 260 | -------------------------------------------------------------------------------- /lib/example/gdoclient/example_gdoclient.go: -------------------------------------------------------------------------------- 1 | package examplegdoclient 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "time" 7 | 8 | "github.com/jameschz/go-base/lib/gdo" 9 | gdopool "github.com/jameschz/go-base/lib/gdo/pool" 10 | "github.com/jameschz/go-base/lib/gutil" 11 | ) 12 | 13 | // MysqlQueryBasic : 14 | func MysqlQueryBasic() { 15 | // print debug info 16 | gdopool.SetDebug(true) 17 | // init by driver 18 | db := gdo.D("base") 19 | defer db.Close() 20 | // init result struct 21 | type story struct { 22 | ID int 23 | Title string 24 | Content string 25 | Dtime string 26 | } 27 | // test FetchMaps 28 | rows, err := db.T("story").Select("id,title,content,dtime", "1=1") 29 | if err != nil { 30 | fmt.Println("> mysql query basic err", err) 31 | } else { 32 | r, _ := db.T("story").FetchMaps(rows) 33 | for k, v := range *r { 34 | fmt.Println("> mysql query basic : FetchMaps - id :", k) 35 | gutil.Dump(v) 36 | } 37 | } 38 | // test FetchMap 39 | rows, err = db.T("story").Select("id,title,content,dtime", "1=1") 40 | if err != nil { 41 | fmt.Println("> mysql query basic err", err) 42 | } else { 43 | r, _ := db.T("story").FetchMap(rows) 44 | fmt.Println("> mysql query basic : FetchMap") 45 | gutil.Dump(r) 46 | } 47 | // test FetchStructs 48 | rows, err = db.T("story").Select("id,title,content,dtime", "1=1") 49 | if err != nil { 50 | fmt.Println("> mysql query basic err", err) 51 | } else { 52 | s := &story{} 53 | r, _ := db.T("story").FetchStructs(rows, s) 54 | for k, v := range *r { 55 | fmt.Println("> mysql query basic : FetchStructs - id :", k) 56 | gutil.Dump(v.(story)) 57 | } 58 | } 59 | // test FetchStruct 60 | rows, err = db.T("story").Select("id,title,content,dtime", "1=1") 61 | if err != nil { 62 | fmt.Println("> mysql query basic err", err) 63 | } else { 64 | s := &story{} 65 | r, _ := db.T("story").FetchStruct(rows, s) 66 | fmt.Println("> mysql query basic : FetchStruct") 67 | gutil.Dump(r.(story)) 68 | } 69 | } 70 | 71 | // MysqlInsertBasic : 72 | func MysqlInsertBasic() { 73 | // print debug info 74 | gdopool.SetDebug(true) 75 | // init by driver 76 | db := gdo.D("base") 77 | defer db.Close() 78 | // test insert 79 | id, err := db.T("story").Insert("title=?,content=?,dtime=?", "title N", "content N", time.Now().Unix()) 80 | if err != nil { 81 | fmt.Println("> mysql insert basic err", err) 82 | } else { 83 | fmt.Println("> mysql insert basic id", id) 84 | } 85 | } 86 | 87 | // MysqlUpdateBasic : 88 | func MysqlUpdateBasic() { 89 | // print debug info 90 | gdopool.SetDebug(true) 91 | // init by driver 92 | db := gdo.D("base") 93 | defer db.Close() 94 | // test max 95 | maxID, _ := db.T("story").Max("id") 96 | if maxID > 0 { 97 | // test update 98 | title := "title " + strconv.FormatInt(maxID, 10) 99 | content := "content " + strconv.FormatInt(maxID, 10) 100 | affect, err := db.T("story").Update("title=?,content=? where id=?", title, content, maxID) 101 | if err != nil { 102 | fmt.Println("> mysql update basic err", err) 103 | } else { 104 | fmt.Println("> mysql update basic affect", affect) 105 | } 106 | } 107 | } 108 | 109 | // MysqlDeleteBasic : 110 | func MysqlDeleteBasic() { 111 | // print debug info 112 | gdopool.SetDebug(true) 113 | // init by driver 114 | db := gdo.D("base") 115 | defer db.Close() 116 | // test max 117 | maxID, _ := db.T("story").Max("id") 118 | if maxID > 0 { 119 | // test delete 120 | affect, err := db.T("story").Delete("id=?", maxID) 121 | if err != nil { 122 | fmt.Println("> mysql delete basic err", err) 123 | } else { 124 | fmt.Println("> mysql delete basic affect", affect) 125 | } 126 | } 127 | } 128 | 129 | // MysqlTxBasic : 130 | func MysqlTxBasic() { 131 | // init by driver 132 | db := gdo.D("base") 133 | defer db.Close() 134 | // tx begin 135 | db.Begin() 136 | // tx select 137 | id, err := db.T("story").Max("id") 138 | if err != nil { 139 | gutil.Dump(err) 140 | } else { 141 | gutil.Dump("before insert", id) 142 | } 143 | // tx insert 144 | id, err = db.T("story").Insert("title=?,content=?,dtime=?", "title N", "content N", time.Now().Unix()) 145 | if err != nil { 146 | gutil.Dump("insert fail", id, err) 147 | db.Rollback() 148 | } 149 | // tx select 150 | id, err = db.T("story").Max("id") 151 | if err != nil { 152 | gutil.Dump(err) 153 | } else { 154 | gutil.Dump("before commit", id) 155 | } 156 | // tx commit 157 | gutil.Dump("insert ok", id) 158 | db.Commit() 159 | // tx select 160 | id, err = db.T("story").Max("id") 161 | if err != nil { 162 | gutil.Dump(err) 163 | } else { 164 | gutil.Dump("after commit", id) 165 | } 166 | } 167 | 168 | // MysqlQueryShard : 169 | func MysqlQueryShard() { 170 | // print debug info 171 | gdopool.SetDebug(true) 172 | // init by driver 173 | db := gdo.C("user") 174 | defer db.Close() 175 | // init result struct 176 | type UserInfo struct { 177 | ID int 178 | Name string 179 | Dtime string 180 | } 181 | // test update (get latest seq id from gb_base.seq_user) 182 | maxID := MysqlMaxUserSeqId() 183 | // test FetchMaps 184 | rows, err := db.T("user_info").Select("id,name,dtime", "id=?", maxID) 185 | if err != nil { 186 | fmt.Println("> mysql query shard err", err) 187 | } else { 188 | r, _ := db.T("user_info").FetchMaps(rows) 189 | for k, v := range *r { 190 | fmt.Println("> mysql query shard : FetchMaps - id :", k) 191 | gutil.Dump(v) 192 | } 193 | } 194 | // test FetchMap 195 | rows, err = db.T("user_info").Select("id,name,dtime", "id=?", maxID) 196 | if err != nil { 197 | fmt.Println("> mysql query shard err", err) 198 | } else { 199 | r, _ := db.T("user_info").FetchMap(rows) 200 | fmt.Println("> mysql query shard : FetchMap") 201 | gutil.Dump(r) 202 | } 203 | // test FetchStructs 204 | rows, err = db.T("user_info").Select("id,name,dtime", "id=?", maxID) 205 | if err != nil { 206 | fmt.Println("> mysql query shard err", err) 207 | } else { 208 | s := &UserInfo{} 209 | r, _ := db.T("user_info").FetchStructs(rows, s) 210 | for k, v := range *r { 211 | fmt.Println("> mysql query shard : FetchStructs - id :", k) 212 | gutil.Dump(v.(UserInfo)) 213 | } 214 | } 215 | // test FetchStruct 216 | rows, err = db.T("user_info").Select("id,name,dtime", "id=?", maxID) 217 | if err != nil { 218 | fmt.Println("> mysql query shard err", err) 219 | } else { 220 | s := &UserInfo{} 221 | r, _ := db.T("user_info").FetchStruct(rows, s) 222 | fmt.Println("> mysql query shard : FetchStruct") 223 | gutil.Dump(r.(UserInfo)) 224 | } 225 | } 226 | 227 | // MysqlInsertShard : 228 | func MysqlInsertShard() { 229 | // print debug info 230 | gdopool.SetDebug(true) 231 | // init by driver 232 | db := gdo.C("user") 233 | defer db.Close() 234 | // test insert (push seq id to gb_base.seq_user) 235 | id, err := db.T("user_info").Insert("id=?,name=?,dtime=?", MysqlUserSeqId(), "Name N", time.Now().Unix()) 236 | if err != nil { 237 | fmt.Println("> mysql insert shard err", err) 238 | } else { 239 | fmt.Println("> mysql insert shard id", id) 240 | } 241 | } 242 | 243 | // MysqlUpdateShard : 244 | func MysqlUpdateShard() { 245 | // print debug info 246 | gdopool.SetDebug(true) 247 | // init by driver 248 | db := gdo.C("user") 249 | defer db.Close() 250 | // test update (get latest seq id from gb_base.seq_user) 251 | maxID := MysqlMaxUserSeqId() 252 | if maxID > 0 { 253 | // test update 254 | name := "name " + strconv.FormatInt(maxID, 10) 255 | affect, err := db.T("user_info").Update("name=? where id=?", name, maxID) 256 | if err != nil { 257 | fmt.Println("> mysql update shard err", err) 258 | } else { 259 | fmt.Println("> mysql update shard affect", affect) 260 | } 261 | } 262 | } 263 | 264 | // MysqlDeleteShard : 265 | func MysqlDeleteShard() { 266 | // print debug info 267 | gdopool.SetDebug(true) 268 | // init by driver 269 | db := gdo.C("user") 270 | defer db.Close() 271 | // test delete (get latest seq id from gb_base.seq_user) 272 | maxID := MysqlMaxUserSeqId() 273 | if maxID > 0 { 274 | // test delete 275 | affect, err := db.T("user_info").Delete("id=?", maxID) 276 | if err != nil { 277 | fmt.Println("> mysql delete shard err", err) 278 | } else { 279 | fmt.Println("> mysql delete shard affect", affect) 280 | } 281 | } 282 | } 283 | 284 | func MysqlUserSeqId() int64 { 285 | // init by driver 286 | db := gdo.D("base") 287 | defer db.Close() 288 | // test insert 289 | id, err := db.T("seq_user").Insert("dtime=?", time.Now().Unix()) 290 | if err != nil { 291 | fmt.Println("> mysql user seq err", err) 292 | } else { 293 | fmt.Println("> mysql user seq id", id) 294 | } 295 | // return 296 | return int64(id) 297 | } 298 | 299 | func MysqlMaxUserSeqId() int64 { 300 | // init by driver 301 | db := gdo.D("base") 302 | defer db.Close() 303 | // test insert 304 | maxID, _ := db.T("seq_user").Max("id") 305 | // return 306 | return int64(maxID) 307 | } 308 | -------------------------------------------------------------------------------- /lib/gdo/mysql/gdo_mysql.go: -------------------------------------------------------------------------------- 1 | package gdomysql 2 | 3 | import ( 4 | "database/sql" 5 | "reflect" 6 | 7 | gdobase "github.com/jameschz/go-base/lib/gdo/base" 8 | gdodriver "github.com/jameschz/go-base/lib/gdo/driver" 9 | gdoparser "github.com/jameschz/go-base/lib/gdo/parser" 10 | gdopool "github.com/jameschz/go-base/lib/gdo/pool" 11 | 12 | // import mysql lib 13 | _ "github.com/go-sql-driver/mysql" 14 | ) 15 | 16 | // Mysql : 17 | type Mysql struct { 18 | gdobase.Db 19 | } 20 | 21 | // T : 22 | func (db *Mysql) T(table string) gdobase.IDb { 23 | db.TableName = table 24 | return db 25 | } 26 | 27 | // GetTable : 28 | func (db *Mysql) GetTable() (string, error) { 29 | if len(db.TableName) == 0 { 30 | panic("gdo.mysql> can not find table") 31 | } 32 | return db.TableName, nil 33 | } 34 | 35 | // CleanTable : 36 | func (db *Mysql) CleanTable() *Mysql { 37 | db.TableName = "" 38 | return db 39 | } 40 | 41 | // Connect : 42 | func (db *Mysql) Connect(driver *gdodriver.Driver) error { 43 | // connect once 44 | if db.Conn == nil { 45 | // gdopool 46 | gdopool.Init() 47 | dataSource, err := gdopool.Fetch(driver.DbName) 48 | // init db vars 49 | db.Driver = driver 50 | db.DataSource = dataSource 51 | db.Conn = dataSource.Conn 52 | db.TableName = "" 53 | return err 54 | } 55 | return nil 56 | } 57 | 58 | // Close : 59 | func (db *Mysql) Close() error { 60 | // close once 61 | if db.Conn != nil { 62 | err := gdopool.Return(db.DataSource) 63 | db.DataSource = nil 64 | db.Conn = nil 65 | db.Tx = nil 66 | return err 67 | } 68 | return nil 69 | } 70 | 71 | // Begin : 72 | func (db *Mysql) Begin() error { 73 | // see begin tx logic in Shard() 74 | db.TxBegin = true 75 | return nil 76 | } 77 | 78 | // Commit : 79 | func (db *Mysql) Commit() error { 80 | // commit tx logic 81 | if db.Tx != nil { 82 | if err := db.Tx.Commit(); err != nil { 83 | return err 84 | } 85 | db.Tx = nil 86 | db.TxBegin = false 87 | } 88 | return nil 89 | } 90 | 91 | // Rollback : 92 | func (db *Mysql) Rollback() error { 93 | // rollback tx logic 94 | if db.Tx != nil { 95 | if err := db.Tx.Rollback(); err != nil { 96 | return err 97 | } 98 | db.Tx = nil 99 | db.TxBegin = false 100 | } 101 | return nil 102 | } 103 | 104 | // Shard : 105 | func (db *Mysql) Shard(sqlStr string, params ...interface{}) error { 106 | // connect for sharding 107 | if db.Conn == nil { 108 | pos := gdoparser.GetSeqIDPos(sqlStr, db.Cluster.SeqID) 109 | if pos < 0 { 110 | panic("gdo> can not find seq value") 111 | } 112 | dbs := db.Cluster.Shard(params[pos].(int64)) 113 | driver := gdodriver.GetDriver(dbs) 114 | if len(driver.Type) == 0 { 115 | panic("gdo> db driver error") 116 | } 117 | if err := db.Connect(driver); err != nil { 118 | return err 119 | } 120 | } 121 | // begin tx logic 122 | // if db.TxBegin == true { 123 | if db.TxBegin { 124 | tx, err := db.Conn.Begin() 125 | if err != nil { 126 | return err 127 | } 128 | db.Tx = tx 129 | } 130 | return nil 131 | } 132 | 133 | // Query : 134 | func (db *Mysql) Query(sqlStr string, params ...interface{}) (rows *sql.Rows, err error) { 135 | // do sharding 136 | if err = db.Shard(sqlStr, params...); err != nil { 137 | return nil, err 138 | } 139 | // do prepare, don't prepare in tx for buffer error 140 | var stmt *sql.Stmt 141 | stmt, err = db.Conn.Prepare(sqlStr) 142 | if err != nil { 143 | return nil, err 144 | } 145 | // do query 146 | defer stmt.Close() 147 | if rows, err = stmt.Query(params...); err != nil { 148 | return nil, err 149 | } 150 | return rows, nil 151 | } 152 | 153 | // Exec : 154 | func (db *Mysql) Exec(sqlStr string, params ...interface{}) (res sql.Result, err error) { 155 | // do sharding 156 | if err = db.Shard(sqlStr, params...); err != nil { 157 | return nil, err 158 | } 159 | // do prepare 160 | var stmt *sql.Stmt 161 | if db.Tx != nil { 162 | stmt, err = db.Tx.Prepare(sqlStr) 163 | } else { 164 | stmt, err = db.Conn.Prepare(sqlStr) 165 | } 166 | if err != nil { 167 | return nil, err 168 | } 169 | // do exec 170 | defer stmt.Close() 171 | if res, err = stmt.Exec(params...); err != nil { 172 | return res, err 173 | } 174 | return res, nil 175 | } 176 | 177 | // Max : 178 | func (db *Mysql) Max(field string) (val int64, err error) { 179 | table, err := db.GetTable() 180 | defer db.CleanTable() 181 | if err != nil { 182 | return -1, err 183 | } 184 | sql := "select max(" + field + ") from " + table 185 | rows, err := db.Query(sql) 186 | if err != nil { 187 | return -1, err 188 | } 189 | for rows.Next() { 190 | rows.Scan(&val) 191 | } 192 | return val, nil 193 | } 194 | 195 | // Min : 196 | func (db *Mysql) Min(field string) (val int64, err error) { 197 | table, err := db.GetTable() 198 | defer db.CleanTable() 199 | if err != nil { 200 | return -1, err 201 | } 202 | sql := "select min(" + field + ") from " + table 203 | rows, err := db.Query(sql) 204 | if err != nil { 205 | return -1, err 206 | } 207 | for rows.Next() { 208 | rows.Scan(&val) 209 | } 210 | return val, nil 211 | } 212 | 213 | // Select : 214 | func (db *Mysql) Select(field string, where string, params ...interface{}) (rows *sql.Rows, err error) { 215 | table, err := db.GetTable() 216 | defer db.CleanTable() 217 | if err != nil { 218 | return nil, err 219 | } 220 | sql := "select " + field + " from " + table + " where " + where 221 | rows, err = db.Query(sql, params...) 222 | if err != nil { 223 | return nil, err 224 | } 225 | return rows, nil 226 | } 227 | 228 | // FetchStructs : 229 | func (db *Mysql) FetchStructs(rows *sql.Rows, data interface{}) (*[]interface{}, error) { 230 | var err error 231 | stu := reflect.ValueOf(data).Elem() 232 | len := stu.NumField() 233 | list := make([]interface{}, 0) 234 | cols := make([]interface{}, len) 235 | for i := 0; i < len; i++ { 236 | cols[i] = stu.Field(i).Addr().Interface() 237 | } 238 | // scan all rows into cols 239 | for rows.Next() { 240 | err = rows.Scan(cols...) 241 | if err != nil { 242 | break 243 | } 244 | // add item into list 245 | list = append(list, stu.Interface()) 246 | } 247 | // close rows 248 | _ = rows.Close() 249 | // return list 250 | return &list, err 251 | } 252 | 253 | // FetchStruct : 254 | func (db *Mysql) FetchStruct(rows *sql.Rows, data interface{}) (interface{}, error) { 255 | res, err := db.FetchStructs(rows, data) 256 | for _, v := range *res { 257 | return v, err 258 | } 259 | return nil, nil 260 | } 261 | 262 | // FetchMaps : 263 | func (db *Mysql) FetchMaps(rows *sql.Rows) (*[]map[string]interface{}, error) { 264 | var err error 265 | // init cols cache 266 | columns, _ := rows.Columns() 267 | columnLen := len(columns) 268 | cols := make([]interface{}, columnLen) 269 | for i := range cols { 270 | var a interface{} 271 | cols[i] = &a 272 | } 273 | var list []map[string]interface{} 274 | // scan all rows into cols 275 | for rows.Next() { 276 | err = rows.Scan(cols...) 277 | if err != nil { 278 | break 279 | } 280 | // build item data 281 | item := make(map[string]interface{}) 282 | for i, v := range cols { 283 | c := *v.(*interface{}) 284 | switch c.(type) { 285 | case []uint8: 286 | item[columns[i]] = string(c.([]uint8)) 287 | break 288 | case nil: 289 | item[columns[i]] = "" 290 | break 291 | default: 292 | item[columns[i]] = c 293 | break 294 | } 295 | } 296 | // add item into list 297 | list = append(list, item) 298 | } 299 | // close rows 300 | _ = rows.Close() 301 | // return list 302 | return &list, err 303 | } 304 | 305 | // FetchMap : 306 | func (db *Mysql) FetchMap(rows *sql.Rows) (map[string]interface{}, error) { 307 | res, err := db.FetchMaps(rows) 308 | for _, v := range *res { 309 | return v, err 310 | } 311 | return nil, nil 312 | } 313 | 314 | // Insert : 315 | func (db *Mysql) Insert(insert string, params ...interface{}) (id int64, err error) { 316 | table, err := db.GetTable() 317 | defer db.CleanTable() 318 | if err != nil { 319 | return -1, err 320 | } 321 | sql := "insert " + table + " set " + insert 322 | res, err := db.Exec(sql, params...) 323 | if err != nil { 324 | return 0, err 325 | } 326 | id, err = res.LastInsertId() 327 | return id, err 328 | } 329 | 330 | // Update : 331 | func (db *Mysql) Update(update string, params ...interface{}) (affect int64, err error) { 332 | table, err := db.GetTable() 333 | defer db.CleanTable() 334 | if err != nil { 335 | return -1, err 336 | } 337 | sql := "update " + table + " set " + update 338 | res, err := db.Exec(sql, params...) 339 | if err != nil { 340 | return 0, err 341 | } 342 | affect, err = res.RowsAffected() 343 | return affect, err 344 | } 345 | 346 | // Delete : 347 | func (db *Mysql) Delete(where string, params ...interface{}) (affect int64, err error) { 348 | table, err := db.GetTable() 349 | defer db.CleanTable() 350 | if err != nil { 351 | return -1, err 352 | } 353 | sql := "delete from " + table + " where " + where 354 | res, err := db.Exec(sql, params...) 355 | if err != nil { 356 | return 0, err 357 | } 358 | affect, err = res.RowsAffected() 359 | return affect, err 360 | } 361 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 9 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 10 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 11 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 12 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 13 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 14 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 15 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 16 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 17 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 18 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 19 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 20 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 21 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 22 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 23 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 24 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 25 | github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= 26 | github.com/bwmarrin/snowflake v0.3.1-0.20190412223032-c09e69ae5993 h1:W7UZZUNOoTTsd9xy0IpzTujkbygCtoQOxMEhbEUemYs= 27 | github.com/bwmarrin/snowflake v0.3.1-0.20190412223032-c09e69ae5993/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= 28 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 29 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 30 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 31 | github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 32 | github.com/coreos/etcd v3.3.22+incompatible h1:AnRMUyVdVvh1k7lHe61YEd227+CLoNogQuAypztGSK4= 33 | github.com/coreos/etcd v3.3.22+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 34 | github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= 35 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 36 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 37 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= 38 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 39 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 40 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 41 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 42 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 43 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 44 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 45 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 46 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 47 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= 48 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 49 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 50 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 51 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 52 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 53 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 54 | github.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o= 55 | github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= 56 | github.com/go-sql-driver/mysql v1.5.1-0.20200720071143-73dc904a9ece h1:q/X4KeYkE2+1JHatu/TRIAke7m5ibks422AH9JdDXPk= 57 | github.com/go-sql-driver/mysql v1.5.1-0.20200720071143-73dc904a9ece/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 58 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 59 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 60 | github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= 61 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 62 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 63 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 64 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 65 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 66 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 67 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 68 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 69 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 70 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 71 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 72 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 73 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 74 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 75 | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= 76 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 77 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 78 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 79 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 80 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 81 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 82 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 83 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 84 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 85 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 86 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 87 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 88 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 89 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 90 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 91 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 92 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 93 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 94 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 95 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 96 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 97 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 98 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 99 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 100 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 101 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 102 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 103 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 104 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 105 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 106 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 107 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 108 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 109 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 110 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 111 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 112 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 113 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 114 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 115 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 116 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 117 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 118 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 119 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 120 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 121 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 122 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 123 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 124 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 125 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 126 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 127 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 128 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 129 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 130 | github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= 131 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 132 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 133 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 134 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 135 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 136 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 137 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 138 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 139 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 140 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 141 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 142 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 143 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 144 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 145 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 146 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 147 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 148 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 149 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 150 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 151 | github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 152 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 153 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 154 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 155 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 156 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 157 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 158 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 159 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 160 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 161 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 162 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 163 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 164 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 165 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 166 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 167 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 168 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 169 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 170 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 171 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 172 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 173 | github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM= 174 | github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 175 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 176 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 177 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 178 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 179 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 180 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 181 | github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= 182 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 183 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= 184 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 185 | github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= 186 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 187 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 188 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 189 | github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= 190 | github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 191 | github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= 192 | github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 193 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 194 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 195 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 196 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 197 | github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= 198 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 199 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 200 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 201 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 202 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 203 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 204 | go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= 205 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 206 | go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= 207 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 208 | go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= 209 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 210 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 211 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 212 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 213 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 214 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 215 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 216 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 217 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 218 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 219 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 220 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 221 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 222 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 223 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 224 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 225 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 226 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 227 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 228 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 229 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 230 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 231 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 232 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 233 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 234 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 235 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 236 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 237 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 238 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 239 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 240 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 241 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 242 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 243 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 244 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 245 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 246 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 247 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= 248 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 249 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= 250 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 251 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 252 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 253 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 254 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 255 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 256 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 257 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 258 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 259 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 260 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 261 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 262 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 263 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 264 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 265 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 266 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 267 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 268 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 269 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 270 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 271 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 272 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 273 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 274 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 275 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 276 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 277 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 278 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 279 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 280 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 281 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 282 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 283 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 284 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 285 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 286 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 287 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 288 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 289 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 290 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 291 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 292 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 293 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 294 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 295 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 296 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 297 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 298 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 299 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 300 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 301 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 302 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 303 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 304 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 305 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 306 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 307 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 308 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 309 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 310 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 311 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 312 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 313 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 314 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 315 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 316 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 317 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 318 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 319 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 320 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= 321 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 322 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 323 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 324 | google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= 325 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 326 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 327 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 328 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 329 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 330 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 331 | google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= 332 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 333 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 334 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 335 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 336 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 337 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 338 | gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= 339 | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 340 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 341 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 342 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 343 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 344 | gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= 345 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 346 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 347 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 348 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 349 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 350 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 351 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 352 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 353 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 354 | sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= 355 | --------------------------------------------------------------------------------