golang 使用etcd

SEO

etcd编译时,报undefined: strings.Builder错误,如何解决?
etcd镜像版本过低,如何拿到高版本的etcd镜像?
etcd为什么本机能连,容器连不上?
etcd如何进行服务注册,服务发现?

重点

  • 解决etcd本机能连,容器连不上的问题。
  • 解决etcd容器版本过低(2.0 – > 3.3)
  • 解决通过etcd进行服务发现的工具操作

简介

etcd 是一个非常好用的中间件服务,职能很多,这里只介绍服务发现,其它的不多说了~

etcd作服务发现时,整个工程可以分为四个角色:
客户端app主服务节点app子服务节点etcd中间件,
它们是怎么运作服务发现的呢?

  1. app服务节点由开发者开发并部署完成,得到一个 ip:端口(172.21.41.11), 存入etcd,并被一个url-path形式的key索引("/login-srv/node/1")。,同理分布式部署,有好多个通业务节点,各自对应如下:
{
    "/login-srv/node/1": "172.21.41.11:8080",
    "/login-srv/node/2": "172.21.41.12:8080",
    "/login-srv/node/3": "172.21.41.13:8080",
    "/login-srv/node/4": "172.21.41.14:8080",
}
  1. 客户端到达app主服务节点,主服务节点通过etcd,按照key "/login-srv" 索引(匹配到了上述所有),拿到所有子服务的对应端口和ip列表,并通过一定hash来抉择使用哪个结点。
  2. 子服务节点需要定期发送续期指令到etcd,告诉它你活着,不然服务索引会被剔除。

最新版的etcd镜像

通过docker search etcd,最多人start的是v2,版本很久。所以我们需要从官网从新拉取最新版的编译并打包进容器里。

官网: https://github.com/etcd-io/etcd
通过git clone / go get / download zip/ 下载至%GOPATH%/src/go.etcd.io/etcd

  • cd %GOPATH%/src/go.etcd.io/etcd 跳至下好的etcd文件夹
  • GOOS=linux GOARCH=amd64 go build -o etcd 交叉编译成镜像环境的可执行etcd程序,产出一个etcd文件
  • cd %GOPATH%/src/go.etcd.io/etcd/etcdctl 跳至etcdctl文件夹
  • GOOS=linux GOARCH=amd64 go build -o etcdctl交叉编译成镜像环境的可执行etcdctl程序,产出一个etcdctl文件
  • %GOPATH%/src/go.etcd.io/etcd 下的Dockerfile-release和前两步准备好的etcd,etcdctl,复制并放在一个新文件夹里
  • 在该新文件夹下: docker build -f Dockerfile-release .
  • docker image ls 后,可以看到一个刚打包好的双none镜像,docker tag <双none的镜像id> etcd:latest
  • docker run -itd -p 2379:2379 -p 2380:2380 --rm -e ETCD_ADVERTISE_CLIENT_URLS=http://0.0.0.0:2379 -e ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379 etcd:latest 运行该容器

golang进行测试是否连接上

main.go

package main

import (
	"github.com/coreos/etcd/clientv3"
	"fmt"
	"time"
	"context"
)

func main() {

	cli, err := clientv3.New(clientv3.Config{
		Endpoints:   []string{"localhost:2379", "localhost:22379", "localhost:32379"},
		// Endpoints:   []string{"localhost:4001"},
		DialTimeout: 5 * time.Second,
	})
	if err != nil {
		fmt.Println("connect failed, err:", err)
		return
	}

	fmt.Println("connect succ")

	defer cli.Close()
	//设置1秒超时,访问etcd有超时控制
	t1:=time.Now()
	ctx, _ := context.WithCancel(context.TODO())
	//操作etcd
	_, err = cli.Put(ctx, "key", "v")
	//操作完毕,取消etcd
	// cancel()

	t2 :=time.Now()
	fmt.Println("put耗时",t2.Sub(t1))
	if err != nil {
		fmt.Println("put failed, err:", err)
		return
	}
	//取值,设置超时为1秒
	ctx, _ = context.WithTimeout(context.Background(), 10*time.Second)
	t1= time.Now()
	resp, err := cli.Get(ctx, "key")
	fmt.Println("get 耗时:",time.Now().Sub(t1))
// 	cancel()
	if err != nil {
		fmt.Println("get failed, err:", err)
		return
	}
	for _, ev := range resp.Kvs {
		fmt.Printf("%s : %s\n", ev.Key, ev.Value)
	}

	//测试redis
}

输出:

connect succ
put耗时 9.0007ms
get 耗时: 3.998ms
key : v

服务发现

使用了 https://www.jianshu.com/p/7c0d23c818a5 这里的工具,作了一点路径和包的修改。
程序比较简单,封装后,只有app子服务方添加自己的ip和端口进etcd,和客户端从etcd获取满足的ip和端口。

package main

import (
	"fmt"
	core "github.com/mistaker/etcdTool"
	"time"
)

// 测试前,确保etcd run in 2379
func main() {
	// 模拟服务方将服务注册进etcd
	ser, e := core.NewServiceReg([]string{"localhost:2379"}, 5)
	if e!=nil {
		panic(e)
	}
	if e := ser.PutService("/user-login/node/111/", "10.0.1.1:8081"); e != nil {
		panic(e)
	}
	if e := ser.PutService("/user-login/node/112/", "10.0.1.1:8082"); e != nil {
		panic(e)
	}

	time.Sleep(7 * time.Second)

	// 模仿客户端从etcd获取服务路径
	cli, e := core.NewClientDis([]string{"localhost:2379"})
	if e!=nil {
		panic(e)
	}
	rs, e := cli.GetService("/user-login/")
	fmt.Println(rs, e)
	select {}
}

输出

续租成功
[10.0.1.1:8081 10.0.1.1:8082] <nil>
2019-07-03 18:23:46.995901 I | set data key : /user-login/node/111/ val: 10.0.1.1:8081
2019-07-03 18:23:46.995901 I | set data key : /user-login/node/112/ val: 10.0.1.1:8082

FAQ

为什么容器连不上,本机连的上

关键点是容器的环境变量,需要设定
ETCD_ADVERTISE_CLIENT_URLS=http://0.0.0.0:2379
ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379
默认发起的主服务是监听127.0.0.1,这个ip在容器里,只会听内环,听不到宿主机的一些网段,改为0.0.0.0则通听!

解决undefined: strings.Builder

本地编译时,需要使用go版本1.12↑,这个错误,在我用1.9时出现了,查了一下百度,是1.9之后添加的东西~~

你可能感兴趣的:(golang 使用etcd)