etcd注册与发现(使用grpc)

1.安装etcd

拉取镜像

docker pull bitnami/etcd

创建网络

docker network create app-tier --driver bridge

 

运行容器

docker run -d --name etcd-server --network app-tier --publish 2379:2379 --publish 2380:2380 --env ALLOW_NONE_AUTHENTICATION=yes --env ETCD_ADVERTISE_CLIENT_URLS=http://etcd-server:2379 bitnami/etcd:latest

 

Etcd-keeper(也称为etcd-keeperd)是一个用于管理和监控etcd集群的工具。

拉取Etcd-keeper镜像

docker pull evildecay/etcdkeeper

 

检查etcd容器ip

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' etcd-server

 

使用获取到的ip放在下面,比如我是172.18.0.2

docker run -d -p 8080:8080 -e ETCD_SERVERS=http://172.18.0.2:2379 --network=app-tier --name etcd-keeper evildecay/etcdkeeper

 

查看etcd帮助信息

docker exec etcd-server etcdctl -h

 

2.代码演示

目录结构如下

新建个项目grpc-etcd,再这之下新建个目录chitchat,再这之下新建client,proto,server

grpc基础调用

proto包下新建helloworld.proto

//指定版本 默认版本2
syntax = "proto3";

// 指定等会文件生成出来的package
package server;

//代码生成目录
option go_package = "grpc-etcd/chitchat/proto";

service SayService{
    //rpc服务的函数名 (传人参数)返回(返回参数)
    //一元调用
    rpc SayHello(SayRequest) returns(SayResponse){}
}

//定义消息
message SayRequest{
    string name = 1;
    string msg = 5;
}

message SayResponse{
    string msg = 5;
}

生成代码

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relati
ve .\chitchat\proto\helloworld.proto

 

下载缺失依赖

go mod tidy

 

在server包下新建server.go

package main

import (
    "context"
    "google.golang.org/grpc"
    "grpc/chitchat/proto"
    "log"
    "net"
)

func main() {
    //tcp监听9090
    listen, err := net.Listen("tcp", "localhost:9090")
    if err != nil {
        log.Fatal("出现错误", err)
        return
    }
    newServer := grpc.NewServer()
    proto.RegisterSayServiceServer(newServer, &server{})
    log.Printf("server listen : %d", listen.Addr())
    err = newServer.Serve(listen)
    if err != nil {
        log.Fatal(err)
    }
}

type server struct {
    proto.UnimplementedSayServiceServer
}

func (server) SayHello(ctx context.Context, in *proto.SayRequest) (*proto.SayResponse, error) {
    log.Printf("服务端接收到 : %v\n", in)
    return &proto.SayResponse{
        Msg: "Hello client , This is server!",
    }, nil
}

一个简单的一元调用服务端代码,不再阐述。

同样在client包下新建client.go

代码如下:

package main

import (
    "context"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
    "grpc/chitchat/proto"
    "log"
)

func main() {
    //建立与服务器的连接
    connect, err := grpc.Dial("localhost:9090", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        log.Fatal(err)
        return
    }
    defer connect.Close()
    //创建客户端对象 client
    client := proto.NewSayServiceClient(connect)
    sayHello(client)

}

func sayHello(client proto.SayServiceClient) {
    //创建一个上下文对象 ctx
    ctx := context.Background()
    sayRequest := &proto.SayRequest{
        Name: "Dreams",
        Msg: "Hello server , This is client!",
    }
    sayHello, err := client.SayHello(ctx, sayRequest)
    if err != nil {
    log.Fatal(err)
        return
    }
    log.Println("客户端接收到 :", sayHello.Msg)
}

一个简单的grpc客户端,,不再阐述。

运行如下:

 

 

etcd注册

功能正常,现在来编写etcd注册和发现

下载etcd依赖

go get go.etcd.io/etcd/client/v3

 

新建一个etcd包

在etcd下新建etcd.go

package etcd

import clientv3 "go.etcd.io/etcd/client/v3"

func GetEtcdClient() (*clientv3.Client, error) {
    client, err := clientv3.New(clientv3.Config{
        Endpoints: []string{"localhost:2379"},
    })
    return client, err
}

上述代码用于获取 etcd 客户端。

clientv3.New 是一个函数,用于创建一个 etcd 客户端。它接受一个 clientv3.Config 类型的参数,并返回一个 *clientv3.Client 类型的指针和一个 error 类型的值。

clientv3.Config 结构体中的字段包含了与 etcd 客户端相关的配置信息,例如要连接的端点地址、认证信息等。

 

在etcd下新建grpc_etcd.go

package etcd

import (
    "context"
    "fmt"
    clientv3 "go.etcd.io/etcd/client/v3"
)

func GrpcRegister(serviceName, addr string) error {
    client, err := GetEtcdClient()
    //可以对serviceName做处理
    key := serviceName

    ctx := context.Background()

    //创建租约
    leaseRes, err := client.Grant(ctx, 10)
    if err != nil {
        return err
    }

    //向etcd注册
    _, err = client.Put(ctx, key, addr, clientv3.WithLease(leaseRes.ID))
    if err != nil {
        return err
    }

    alive, err := client.KeepAlive(ctx, leaseRes.ID)
    if err != nil {
        return err
    }
    go func() {
        for item := range alive {
            fmt.Printf("leaseID :%x , TTl :%v\n", item.ID, item.TTL)
        }
    }()
    return nil
}

调用 GetEtcdClient() 函数获取 etcd 客户端对象。根据服务名构建注册到 etcd 中的 key。创建一个租约,设置租约的过期时间为 10 秒,并获取租约 ID。调用 client.Put() 函数将服务地址注册到 etcd 中,同时指定该记录的租约 ID。调用 client.KeepAlive() 函数续租,以确保服务在 etcd 中的记录不会被删除。在一个新的 goroutine 中,打印租约 ID 和 TTL,以便观察租约的状态。如果函数执行过程中出现错误,直接返回错误信息。

client.Grant 是 etcd 用于创建一个新的租约。它接受一个 context.Context 参数和一个过期时间(以秒为单位)参数。

client.Put 方法将键值对存储到 etcd 中,其中 key 是键名,value 是键值。

clientv3.WithLease 选项指定了租约 ID,以关联该键值对与特定的租约。

client.KeepAlive() 函数续租,以确保服务在 etcd 中的记录不会被删除。

 

在server.go的main函数加入注册代码

func main() {
    //tcp监听9090
    listen, err := net.Listen("tcp", "localhost:9090")
    if err != nil {
        log.Fatal("出现错误", err)
        return
    }
    newServer := grpc.NewServer()
    proto.RegisterSayServiceServer(newServer, &server{})
    log.Printf("server listen : %d", listen.Addr())
    
    //服务注册进etcd
    etcd.GrpcRegister("hello-server", "localhost:9090")

    err = newServer.Serve(listen)

    if err != nil {
        log.Fatal(err)
    }
}

 

运行

同时etcd也成功注册

docker exec etcd-server etcdctl get "hello" --prefix

如果将server.go关闭

etcd因为没有续约,所以会自动删除key

 

为了不干扰显示,这里将协程打印id代码注释

在etcd下修改grpc_etcd.go

package etcd

import (
    "context"
    "fmt"
    clientv3 "go.etcd.io/etcd/client/v3"
)

func GrpcRegister(serviceName, addr string) error {
    client, err := GetEtcdClient()
    //可以对serviceName做处理
    key := serviceName

    ctx := context.Background()

    //创建租约
    leaseRes, err := client.Grant(ctx, 10)
    if err != nil {
        return err
    }

    //向etcd注册
    _, err = client.Put(ctx, key, addr, clientv3.WithLease(leaseRes.ID))
    if err != nil {
        return err
    }

    _, err = client.KeepAlive(ctx, leaseRes.ID)
    if err != nil {
        return err
    }
    //go func() {
        //for item := range alive {
            //fmt.Printf("leaseID :%x , TTl :%v\n", item.ID, item.TTL)
        //}
    //}()
    return nil
}

 

 

etcd发现

在grpc_etcd.go添加发现函数

func GrpcDiscover(serviceName string) string {
    client, err := GetEtcdClient()
    //可以对serviceName做处理
    key := serviceName

    // 获取 etcd client.Get 方法获取指定键名的值
    getKey, err := client.Get(context.Background(), key)
    if err != nil {
        log.Fatal(err)
    }

    //
    for _, item := range getKey.Kvs {
        if string(item.Key) == serviceName {
            return string(item.Value)
        }
    }
    return ""
}

 

再在client发现

修改client.go的main函数,从etcd获取地址

func main() {

    addr := etcd.GrpcDiscover("hello-server")
    if addr == "" {
        log.Fatal("不存在该地址")
    }

    //建立与服务器的连接
    connect, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        log.Fatal(err)
        return
    }
    defer connect.Close()
    //创建客户端对象 client
    client := proto.NewSayServiceClient(connect)
    sayHello(client)

}

 

成功

 

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇