etcd簡介
淺談etcd服務(wù)注冊與發(fā)現(xiàn)
etcd官網(wǎng)
etcd中文文檔
apt安裝etcd,啟動命令十分簡單etcd
。
etcd分為v2版本和v3版本,命令有所不一樣,使用命令etcdctl h
查看
如上圖所示并沒有出現(xiàn)API的版本,此時(shí)是使用默認(rèn)的v2版本,但是v2版本很多命令使用不了,因此切換為v3版本,命令如下:
# 設(shè)置命令為v3
export ETCDCTL_API=3
# 查看所有的key,會出現(xiàn)兩行,第一行key,第二行value
etcdctl get --prefix ""
etcd是一個(gè)k-v存儲的格式和redis類似,使用etcdctl set k v
存儲數(shù)據(jù),使用etcdctl get k
獲取數(shù)據(jù)。
go中使用etcd
安裝
go get go.etcd.io/etcd/clientv3
客戶端代碼
package main
import (
"context"
"fmt"
"go.etcd.io/etcd/clientv3"
"time"
)
// etcd client put/get demo
// use etcd/clientv3
func main() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: []string{"127.0.0.1:2379"},
DialTimeout: 5 * time.Second,
})
if err != nil {
// handle error!
fmt.Printf("connect to etcd failed, err:%v\n", err)
return
}
fmt.Println("connect to etcd success")
defer cli.Close()
//put
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
_, err = cli.Put(ctx, "zhangsan", "yes")
cancel()
if err != nil {
fmt.Printf("put to etcd failed, err:%v\n", err)
return
}
// get
ctx, cancel = context.WithTimeout(context.Background(), time.Second)
resp, err := cli.Get(ctx, "zhangsan")
cancel()
if err != nil {
fmt.Printf("get from etcd failed, err:%v\n", err)
return
}
for _, ev := range resp.Kvs {
fmt.Printf("%s:%s\n", ev.Key, ev.Value)
}
}
啟動etcd服務(wù)器
etcd --listen-client-urls 'http://0.0.0.0:2379' --advertise-client-urls 'http://0.0.0.0:2379'
不要直接
etcd
啟動,會報(bào)錯(cuò)context deadline exceeded
運(yùn)行客戶端代碼
可以看出etcd是鍵值對存儲的。
go-zero使用etcd
etcd最具特色的功能是訂閱與發(fā)布和監(jiān)測變更,在etcd作為服務(wù)注冊與查找時(shí),etcd提供了檢測功能,開啟該功能后會持續(xù)想etcd服務(wù)器發(fā)送心跳,如果服務(wù)停掉或者心跳停止,etcd服務(wù)器就會刪除該次記錄(服務(wù)器自主完成)。
一般來說,rpc服務(wù)連接etcd服務(wù)器發(fā)送心跳,實(shí)現(xiàn)服務(wù)到服務(wù)調(diào)度中心的注冊,服務(wù)名稱和地址被存儲到etcd服務(wù)器,另一個(gè)服務(wù)調(diào)用時(shí)不是直接連接rpc服務(wù)器,而是先根據(jù)服務(wù)名稱在etcd中找到ip地址,在通過ip地址調(diào)用服務(wù)。這樣以來服務(wù)名稱和其對于的地址就與源代碼解耦,在部署到不同ip的服務(wù)器時(shí)無需修改源代碼。
- 開啟etcd服務(wù)器
etcd --listen-client-urls 'http://0.0.0.0:2379' --advertise-client-urls 'http://0.0.0.0:2379'
- key存儲并開啟心跳檢測
package main
import (
"context"
"fmt"
"go.etcd.io/etcd/clientv3"
"time"
)
func main() {
config := clientv3.Config{
Endpoints: []string{"localhost:2379"},
DialTimeout: 5 * time.Second,
}
client, err := clientv3.New(config)
if err != nil {
fmt.Println(err)
return
}
defer client.Close()
lease := clientv3.NewLease(client) // 創(chuàng)建一個(gè)心跳檢測
leaseResp, err := lease.Grant(context.Background(), 10) // 配置檢測超時(shí) 10秒,查過檢測時(shí)間etcd服務(wù)器自動刪除k-v鍵值對
if err != nil {
fmt.Println(err)
return
}
leaseRespChan, _ := lease.KeepAlive(context.Background(), leaseResp.ID) // 通過keeplive方法定期發(fā)送心跳
kv := clientv3.NewKV(client)
key := fmt.Sprintf("/services/http/%d", leaseResp.ID)
value := `{"host": "localhost", "port": 2379}`
fmt.Println("key", key)
// 綁定帶有心跳的服務(wù)并將自身服務(wù)信息put到etcd中心
_, err = kv.Put(context.Background(), key, value, clientv3.WithLease(leaseResp.ID))
if err != nil {
fmt.Println(err)
return
}
fmt.Println("register service success")
for {
select {
case i := <-leaseRespChan:
fmt.Println("heart beat", i.String()) // 心跳信息
}
}
}
如上圖所示,服務(wù)etcd心跳會一直發(fā)送到etcd服務(wù)器,那么可以在rpc服務(wù)下啟動一個(gè)心跳,一直檢測服務(wù)的狀態(tài),rpc服務(wù)如果宕機(jī)心跳停止,etcd服務(wù)器就會刪除該服務(wù)的k-v記錄。
etcd服務(wù)器的心跳檢測時(shí)etcd服務(wù)器自動完成的,開發(fā)者只需要配置心跳時(shí)間,調(diào)用心跳方法就可以了,服務(wù)器會根據(jù)心跳自動管理數(shù)據(jù)。
在go-zero中已經(jīng)集成了etcd,通過配置文件的形式,在goctl下都會有,如下配置:
Etcd:
Hosts:
- 127.0.0.1:2379
Key: rpcservice.rpc
該部分配置etcd服務(wù)器的接口和key。配置etcd服務(wù)器后需要設(shè)置心跳,如下步驟:
- 下載etcd客戶端
go get go.etcd.io/etcd/clientv3
- 配置客戶端調(diào)用心跳函數(shù)
//etcd服務(wù)端啟動
etcdConfig := clientv3.Config{
Endpoints: []string{"localhost:2379"},
DialTimeout: 5 * time.Second,
}
//etcd客戶端
etcdClient, err := clientv3.New(etcdConfig)
if err != nil {
println(err)
return
}
defer etcdClient.Close()
//配置心跳
lease := clientv3.NewLease(etcdClient)
lgr, err := lease.Grant(context.Background(), 10) //10秒發(fā)送一次心跳
if err != nil {
println(err)
return
}
c2, _ := lease.KeepAlive(context.Background(), lgr.ID)
//通過客戶端獲取k-v存儲對象
k := clientv3.NewKV(etcdClient)
key := "order.rpc" //可以同配置文件中獲取
value := "localhost:2379" //不同服務(wù)器的ip不一樣,這里設(shè)置為localhost或者通過方法獲取本機(jī)ip也可以
k.Put(context.Background(), key, value, clientv3.WithLease(lgr.ID))
//打印心跳檢測返回的數(shù)據(jù)
for {
if len(c2) != 0 {
fmt.Println("heart beat ", <-c2)
}
}
報(bào)錯(cuò)如下:
# github.com/coreos/etcd/clientv3/balancer/picker
C:\Users\sspaas\go\pkg\mod\github.com\coreos\etcd@v3.3.27+incompatible\clientv3\balancer\picker\err.go:37:53: undefined: balancer.PickOptions
C:\Users\sspaas\go\pkg\mod\github.com\coreos\etcd@v3.3.27+incompatible\clientv3\balancer\picker\roundrobin_balanced.go:55:63: undefined: balancer.PickOptions
# github.com/coreos/etcd/clientv3/balancer/resolver/endpoint
C:\Users\sspaas\go\pkg\mod\github.com\coreos\etcd@v3.3.27+incompatible\clientv3\balancer\resolver\endpoint\endpoint.go:114:87: undefined: resolver.BuildOption
C:\Users\sspaas\go\pkg\mod\github.com\coreos\etcd@v3.3.27+incompatible\clientv3\balancer\resolver\endpoint\endpoint.go:182:40: undefined: resolver.ResolveNowOption
原因時(shí)grpc和etcd版本沖突問題參考
又報(bào)錯(cuò):
原因主要是etcd中使用的bbolt和grpc版本沖突引起參考
解決沖突后,在服務(wù)氣短通過檢測心跳的方式將rpc的名稱為ip的k-v存儲到rpc服務(wù)器中,然后通過其他客戶端通過服務(wù)名稱獲取值后進(jìn)行rpc遠(yuǎn)程調(diào)用即可。
客戶端程序
如果遇到zero集成的etcd和grpc有沖突的話,使用獨(dú)立的etcd,不要在使用zero集成的etcd。代碼如下:
package main
import (
"context"
"fmt"
"time"
"go.etcd.io/etcd/clientv3"
)
// etcd client put/get demo
// use etcd/clientv3
func main() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: []string{"127.0.0.1:2379"},
DialTimeout: 5 * time.Second,
})
if err != nil {
// handle error!
fmt.Printf("connect to etcd failed, err:%v\n", err)
return
}
fmt.Println("connect to etcd success")
defer cli.Close()
//put
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
_, err = cli.Put(ctx, "zhangsan", "yes")
cancel()
if err != nil {
fmt.Printf("put to etcd failed, err:%v\n", err)
return
}
// get
ctx, cancel = context.WithTimeout(context.Background(), time.Second)
resp, err := cli.Get(ctx, "demo.rpc")
cancel()
if err != nil {
fmt.Printf("get from etcd failed, err:%v\n", err)
return
}
for _, ev := range resp.Kvs {
fmt.Printf("%s:%s\n", ev.Key, ev.Value)
}
}
服務(wù)端程序
package main
import (
"context"
"fmt"
"time"
"go.etcd.io/etcd/clientv3"
)
func main() {
config := clientv3.Config{
Endpoints: []string{"localhost:2379"},
DialTimeout: 5 * time.Second,
}
client, err := clientv3.New(config)
if err != nil {
fmt.Println(err)
return
}
defer client.Close()
lease := clientv3.NewLease(client) // 創(chuàng)建一個(gè)租約
leaseResp, err := lease.Grant(context.Background(), 10) // 設(shè)置租約超時(shí) 10秒
if err != nil {
fmt.Println(err)
return
}
leaseRespChan, _ := lease.KeepAlive(context.Background(), leaseResp.ID) // 通過keeplive定期發(fā)送心跳
kv := clientv3.NewKV(client)
key := "demo.rpc"
value := "127.0.0.1:9000"
fmt.Println("key", key)
// 綁定帶有心跳的租約并將自身服務(wù)信息put到etcd中心
_, err = kv.Put(context.Background(), key, value, clientv3.WithLease(leaseResp.ID))
if err != nil {
fmt.Println(err)
return
}
fmt.Println("register service success")
for {
select {
case i := <-leaseRespChan:
fmt.Println("heart beat", i.String()) // 心跳信息
}
}
}
別忘了啟動etcd服務(wù)器
etcd --listen-client-urls 'http://0.0.0.0:2379' --advertise-client-urls 'http://0.0.0.0:2379'
服務(wù)端心跳
獲取鍵值對文章來源:http://www.zghlxwxcb.cn/news/detail-661525.html
go語言學(xué)習(xí)網(wǎng)站文章來源地址http://www.zghlxwxcb.cn/news/detail-661525.html
到了這里,關(guān)于go-zero微服務(wù)實(shí)戰(zhàn)——etcd服務(wù)注冊與發(fā)現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!