Golang - ETCD操作 - 服務註冊與發現

ETCD - V2 版本的服務註冊

// 	"github.com/coreos/etcd/pkg/transport"
//	"github.com/coreos/etcd/client"

type ServiceReg struct {
	kApi        client.KeysAPI
	serviceList map[string]string
}

func NewServiceReg(addr []string, serverList map[string]string, timeout int64) (ser *ServiceReg, err error) {
	ser = &ServiceReg{serviceList: make(map[string]string, 16)}

	tr, err := transport.NewTransport(transport.TLSInfo{}, 5*time.Second)
	if err != nil {
		return
	}

	conf := client.Config{Endpoints: addr, Transport: tr}

	var c client.Client
	if c, err = client.New(conf); err != nil {
		return
	}

	ser.kApi = client.NewKeysAPI(c)

	for key, val := range serverList {
		_, err = ser.kApi.Set(context.Background(), key, val, nil)
		if err != nil {
			return
		}

		ser.serviceList[key] = val
	}

	go func() {
		o := client.SetOptions{
			TTL:       time.Second * time.Duration(timeout),
			PrevExist: client.PrevExist,
		}

		for {
			for s, addr := range ser.serviceList {
				_, err := ser.kApi.Set(context.Background(), s, addr, &o)
				if etErr, ok := err.(client.Error); ok && etErr.Code == client.ErrorCodeKeyNotFound {
					fmt.Printf("已經關閉續租功能\n")
					return
				} else if err != nil {
					fmt.Printf("續租失敗: %v\n", err)
				} else {
					fmt.Printf("續租成功\n")
				}
			}

			time.Sleep(time.Second * time.Duration(math.Ceil(float64(timeout)*0.3)))
		}
	}()

	return
}

// 關閉租約服務
func (this *ServiceReg) Close() {
	for key, _ := range this.serviceList {
		_, _ = this.kApi.Delete(context.Background(), key, nil)
	}
}

// 使用方法
func main() {
	etcdAddr := []string{"http://192.168.200.129:2379"}
	servAddr := map[string]string{
		"/node/192.168.200.1": "192.168.200.1:50051",
		"/node/192.168.200.2": "192.168.200.2:50051",
	}
	ser, err := NewServiceReg(etcdAddr, servAddr, 10)
	if err != nil {
		log.Fatal(err)
	}
	defer ser.Close()
}

ETCD - V3 版本的服務註冊

// import github.com/coreos/etcd/clientv3

//創建租約註冊服務
type ServiceReg struct {
	client        *clientv3.Client
	lease         clientv3.Lease
	leaseResp     *clientv3.LeaseGrantResponse
	cancelFunc    func()
	keepAliveChan <-chan *clientv3.LeaseKeepAliveResponse
}

func NewServiceReg(addr []string, serverList map[string]string, timeout int64) (ser *ServiceReg, err error) {
	ser = &ServiceReg{}

	conf := clientv3.Config{Endpoints: addr, DialTimeout: 5 * time.Second}

	if ser.client, err = clientv3.New(conf); err != nil {
		return
	}

	ser.lease = clientv3.NewLease(ser.client)

	//設置租約時間
	if ser.leaseResp, err = ser.lease.Grant(context.TODO(), timeout); err != nil {
		return
	}

	//設置續租
	var ctx context.Context
	ctx, ser.cancelFunc = context.WithCancel(context.TODO())
	if ser.keepAliveChan, err = ser.lease.KeepAlive(ctx, ser.leaseResp.ID); err != nil {
		return
	}

	// 監聽 續租情況
	go func() {
		for {
			select {
			case ch := <-ser.keepAliveChan:
				if ch == nil {
					fmt.Printf("已經關閉續租功能\n")
					return
				} else {
					fmt.Printf("續租成功\n")
				}
			}
		}
	}()

	// 註冊服務
	kv := clientv3.NewKV(ser.client)
	for key, val := range serverList {
		_, err = kv.Put(context.TODO(), key, val, clientv3.WithLease(ser.leaseResp.ID))
		if err != nil {
			return
		}
	}

	return
}

// 關閉租約服務
func (this *ServiceReg) Close() {
	this.cancelFunc()
	time.Sleep(2 * time.Second)
	_, _ = this.lease.Revoke(context.TODO(), this.leaseResp.ID)
}

// 使用方法
func main() {
	etcdAddr := []string{"http://192.168.200.129:2379"}
	servAddr := map[string]string{
		"/node/192.168.200.1": "192.168.200.1:50051",
		"/node/192.168.200.2": "192.168.200.2:50051",
	}
	ser, err := NewServiceReg(etcdAddr, servAddr, 10)
	if err != nil {
		log.Fatal(err)
	}
	defer ser.Close()
}

ETCD - V2 服務發現

type ClientDis struct {
	client     client.KeysAPI
	serverList map[string]string
	lock       sync.Mutex
}

func NewClientDis(addr []string, prefixes []string) (cli *ClientDis, err error) {
	cli = &ClientDis{serverList: make(map[string]string)}

	tr, err := transport.NewTransport(transport.TLSInfo{}, 5*time.Second)
	if err != nil {
		return
	}

	conf := client.Config{Endpoints: addr, Transport: tr}

	var c client.Client
	if c, err = client.New(conf); err != nil {
		return
	}

	cli.client = client.NewKeysAPI(c)

	for _, p := range prefixes {
		resp, err := cli.client.Get(context.Background(), p, &client.GetOptions{Recursive: true})
		if err == nil && resp != nil && resp.Node.Dir && len(resp.Node.Nodes) > 0 {
			for _, n := range resp.Node.Nodes {
				if !n.Dir {
					cli.SetServiceList(n.Key, n.Value)
				}
			}
		}

		go func() {
			watcher := cli.client.Watcher(p, &client.WatcherOptions{Recursive: true, AfterIndex: 0})

			for {
				resp, err = watcher.Next(context.Background())

				if err != nil {
					fmt.Printf("%v", err)
					continue
				}

				if !resp.Node.Dir {
					switch resp.Action {
					case "set", "update":
						cli.SetServiceList(resp.Node.Key, resp.Node.Value)
					case "expire", "delete":
						cli.DelServiceList(resp.Node.Key)
					default:
						fmt.Println("watchMe!!!", "resp ->", resp)
					}
				}
			}

		}()
	}
	return
}

func (this *ClientDis) SetServiceList(key, val string) {
	this.lock.Lock()
	defer this.lock.Unlock()

	this.serverList[key] = string(val)
}

func (this *ClientDis) DelServiceList(key string) {
	this.lock.Lock()
	defer this.lock.Unlock()

	delete(this.serverList, key)
}

func (this *ClientDis) ServiceOne(prefix string) string {
	this.lock.Lock()
	defer this.lock.Unlock()

	addrArr := make([]string, 0, 16)
	for k, v := range this.serverList {
		if strings.HasPrefix(k, prefix) {
			addrArr = append(addrArr, v)
		}
	}

	if len(addrArr) == 0 {
		return ""
	}

	// 隨機獲取一個服務
	r := rand.New(rand.NewSource(time.Now().Unix()))
	return addrArr[r.Intn(len(addrArr))]
}

// 使用方法
func main() {
	// 服務發現
	cli, err := NewClientDis([]string{"http://192.168.200.129:2379"}, []string{"/node"})
	if err == nil {
		fmt.Print(cli.ServiceOne("/node"))
	} else {
		fmt.Printf("%v", err)
	}

	time.Sleep(10000 * time.Second)
}

ETCD - V3 服務發現

// import github.com/coreos/etcd/mvcc/mvccpb
// import github.com/coreos/etcd/clientv3

type ClientDis struct {
	client     *clientv3.Client
	serverList map[string]string
	lock       sync.Mutex
}

func NewClientDis(addr []string, prefixes []string) (cli *ClientDis, err error) {
	cli = &ClientDis{serverList: make(map[string]string)}

	conf := clientv3.Config{Endpoints: addr, DialTimeout: 5 * time.Second}

	cli.client, err = clientv3.New(conf)

	for _, p := range prefixes {
		resp, err := cli.client.Get(context.Background(), p, clientv3.WithPrefix())
		if err == nil && resp != nil && resp.Kvs != nil {
			for i := range resp.Kvs {
				if v := resp.Kvs[i].Value; v != nil {
					cli.SetServiceList(string(resp.Kvs[i].Key), string(resp.Kvs[i].Value))
				}
			}
		}

		go func() {
			rch := cli.client.Watch(context.Background(), p, clientv3.WithPrefix())
			for wResp := range rch {
				for _, ev := range wResp.Events {
					switch ev.Type {
					case mvccpb.PUT:
						cli.SetServiceList(string(ev.Kv.Key), string(ev.Kv.Value))
					case mvccpb.DELETE:
						cli.DelServiceList(string(ev.Kv.Key))
					}
				}
			}
		}()
	}
	return
}

func (this *ClientDis) SetServiceList(key, val string) {
	this.lock.Lock()
	defer this.lock.Unlock()

	this.serverList[key] = string(val)
}

func (this *ClientDis) DelServiceList(key string) {
	this.lock.Lock()
	defer this.lock.Unlock()

	delete(this.serverList, key)
}

func (this *ClientDis) ServiceOne(prefix string) string {
	this.lock.Lock()
	defer this.lock.Unlock()

	addrArr := make([]string, 0, 16)
	for k, v := range this.serverList {
		if strings.HasPrefix(k, prefix) {
			addrArr = append(addrArr, v)
		}
	}

	if len(addrArr) == 0 {
		return ""
	}

	// 隨機獲取一個服務
	r := rand.New(rand.NewSource(time.Now().Unix()))
	return addrArr[r.Intn(len(addrArr))]
}

// 使用方法
func main() {
	// 服務發現
	cli, _ := NewClientDis([]string{"192.168.200.129:2379"}, []string{"/node"})
	fmt.Print(cli.ServiceOne("/node"))
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章