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"))
}