game_sync/etcd/client.go

248 lines
6.4 KiB
Go

package etcd
import (
"context"
"time"
"go.etcd.io/etcd/client/v3"
"mongo.games.com/goserver/core/logger"
)
/*
etcd常用操作和数据监听
*/
type (
InitFunc func() int64
WatchFunc func(context.Context, int64)
FuncPair struct {
initFunc InitFunc
watchFunc WatchFunc
}
)
type Client struct {
cli *clientv3.Client
functions []FuncPair
closed bool
}
func (c *Client) IsClosed() bool {
return c.closed
}
func (c *Client) Ctx() context.Context {
if c.cli != nil {
return c.cli.Ctx()
}
return context.TODO()
}
func (c *Client) Open(etcdUrl []string, userName, passWord string, dialTimeout time.Duration) error {
var err error
c.cli, err = clientv3.New(clientv3.Config{
Endpoints: etcdUrl,
Username: userName,
Password: passWord,
DialTimeout: dialTimeout,
DialKeepAliveTime: 5 * time.Second,
DialKeepAliveTimeout: 30 * time.Second,
})
if err != nil {
logger.Logger.Errorf("EtcdClient.open(%v) err:%v", etcdUrl, err)
return err
}
c.closed = false
return err
}
func (c *Client) Close() error {
logger.Logger.Warn("EtcdClient.close")
c.closed = true
if c.cli != nil {
return c.cli.Close()
}
return nil
}
// PutValue 添加键值对
func (c *Client) PutValue(key, value string) (*clientv3.PutResponse, error) {
resp, err := c.cli.Put(context.TODO(), key, value)
if err != nil {
logger.Logger.Warnf("EtcdClient.PutValue(%v,%v) error:%v", key, value, err)
}
return resp, err
}
// GetValue 查询
func (c *Client) GetValue(key string) (*clientv3.GetResponse, error) {
resp, err := c.cli.Get(context.TODO(), key)
if err != nil {
logger.Logger.Warnf("EtcdClient.GetValue(%v) error:%v", key, err)
}
return resp, err
}
// DelValue 返回删除了几条数据
func (c *Client) DelValue(key string) (*clientv3.DeleteResponse, error) {
res, err := c.cli.Delete(context.TODO(), key)
if err != nil {
logger.Logger.Warnf("EtcdClient.DelValue(%v) error:%v", key, err)
}
return res, err
}
// DelValueWithPrefix 按照前缀删除
func (c *Client) DelValueWithPrefix(prefix string) (*clientv3.DeleteResponse, error) {
res, err := c.cli.Delete(context.TODO(), prefix, clientv3.WithPrefix())
if err != nil {
logger.Logger.Warnf("EtcdClient.DelValueWithPrefix(%v) error:%v", prefix, err)
}
return res, err
}
// GetValueWithPrefix 获取前缀
func (c *Client) GetValueWithPrefix(prefix string) (*clientv3.GetResponse, error) {
resp, err := c.cli.Get(context.TODO(), prefix, clientv3.WithPrefix())
if err != nil {
logger.Logger.Warnf("EtcdClient.GetValueWIthPrefix(%v) error:%v", prefix, err)
}
return resp, err
}
// WatchWithPrefix 监测前缀创建事件
func (c *Client) WatchWithPrefix(prefix string, revision int64) clientv3.WatchChan {
if c.cli != nil {
opts := []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithCreatedNotify()}
if revision > 0 {
opts = append(opts, clientv3.WithRev(revision))
}
return c.cli.Watch(clientv3.WithRequireLeader(context.Background()), prefix, opts...)
}
return nil
}
// Compact 压缩数据
//func (this *Client) Compact() {
// if this.closed {
// return
// }
//
// resp, err := this.GetValue("@@@GET_LASTEST_REVISION@@@")
// if err == nil {
// ctx, _ := context.WithCancel(this.cli.Ctx())
// start := time.Now()
// compactResponse, err := this.cli.Compact(ctx, resp.Header.Revision, clientv3.WithCompactPhysical())
// if err == nil {
// logger.Logger.Infof("EtcdClient.Compact From %v CompactResponse %v take %v", resp.Header.Revision, compactResponse.Header, time.Now().Sub(start))
// } else {
// logger.Logger.Errorf("EtcdClient.Compact From %v CompactResponse:%v take:%v err:%v", resp.Header.Revision, compactResponse, time.Now().Sub(start), err)
// }
// endpoints := this.cli.Endpoints()
// for _, endpoint := range endpoints {
// ctx1, _ := context.WithCancel(this.cli.Ctx())
// start := time.Now()
// defragmentResponse, err := this.cli.Defragment(ctx1, endpoint)
// if err == nil {
// logger.Logger.Infof("EtcdClient.Defragment %v,%v take %v", endpoint, defragmentResponse.Header, time.Now().Sub(start))
// } else {
// logger.Logger.Errorf("EtcdClient.Defragment DefragmentResponse:%v take:%v err:%v", defragmentResponse, time.Now().Sub(start), err)
// }
// }
// }
//}
// AddFunc 添加监听函数
func (c *Client) AddFunc(initFunc InitFunc, watchFunc WatchFunc) {
funcPair := FuncPair{
initFunc: initFunc,
watchFunc: watchFunc,
}
c.functions = append(c.functions, funcPair)
}
// ReInitAndWatchAll 重新监听
func (c *Client) ReInitAndWatchAll() {
if c.closed {
return
}
oldFunc := c.functions
c.functions = nil
for i := 0; i < len(oldFunc); i++ {
c.InitAndWatch(oldFunc[i].initFunc, oldFunc[i].watchFunc)
}
}
// InitAndWatch 开始监听
func (c *Client) InitAndWatch(initFunc InitFunc, watchFunc WatchFunc) {
funcPair := FuncPair{
initFunc: initFunc,
watchFunc: watchFunc,
}
c.functions = append(c.functions, funcPair)
lastRevision := initFunc()
ctx, _ := context.WithCancel(c.cli.Ctx())
watchFunc(ctx, lastRevision+1)
}
// GoWatch 异步监听
func (c *Client) GoWatch(ctx context.Context, revision int64, prefix string, f func(res clientv3.WatchResponse) error) {
go func() {
defer func() {
if err := recover(); err != nil {
logger.Logger.Errorf("etcd watch WithPrefix(%v) panic:%v", prefix, err)
}
logger.Logger.Warnf("etcd watch WithPrefix(%v) quit!!!", prefix)
}()
var times int64
for !c.closed {
times++
logger.Logger.Warnf("etcd watch WithPrefix(%v) base revision %v start[%v]!!!", prefix, revision, times)
rch := c.WatchWithPrefix(prefix, revision)
for {
skip := false
select {
case _, ok := <-ctx.Done():
if !ok {
return
}
case resp, ok := <-rch:
if !ok {
logger.Logger.Warnf("etcd watch WithPrefix(%v) be closed", prefix)
skip = true
break
}
if resp.Header.Revision > revision {
revision = resp.Header.Revision
}
if resp.Canceled {
logger.Logger.Warnf("etcd watch WithPrefix(%v) be closed, reason:%v", prefix, resp.Err())
skip = true
break
}
if err := resp.Err(); err != nil {
logger.Logger.Warnf("etcd watch WithPrefix(%v) err:%v", prefix, resp.Err())
continue
}
if len(resp.Events) == 0 {
continue
}
logger.Logger.Tracef("@goWatch %v changed, header:%#v", prefix, resp.Header)
f(resp)
}
if skip {
break
}
}
time.Sleep(time.Second)
}
}()
}