etcd学习笔记

博客参考:K8s组件:etcd安装、使用及原理(Linux)

分布式系统架构中对一致性要求很高,etcd就满足了分布式系统中的一致性要求,实现了分布式一致性键值对存储的中间件。

etcd完整的集群至少需要3台,这样才能选出一个master和两个node,具有强一致性,常用于注册中心(配置共享和服务发现)。现目前是分布式和云原生下的基础组件,如:k8s。

内部使用的raft算法,算法具体内容见我的另外一篇博客,在此描述几个关键点:
	Leader、Candidate、Follower、任期、心跳探测。
	脑裂:当网络恢复之后,旧的Leader发现集群中的新Leader的Term比自己大,则自动降级为Follower,并从新Leader处同步数据达成集群数据一致。
	数据同步流程:当Client发起数据更新请求,请求会先到领袖节点C,节点C会更新日志数据,然后通知群众节点也更新日志,当群众节点更新日志成功后,会返回成功通知给领袖C;当领袖C收到通知后,会更新本地数据,并通知群众也更新本地数据,同时会返回成功通知给Client。
	
结构定义:从raftLog结构体可以看到,里面有两个存储位置,一个是storage,是保存已经持久化过的日志条目,unstable是保存的尚未持久化的日志条目。storage结构中字段的定义,它实际上就是包含一个WAL来保存日志条目,一个Snapshotter负责保存日志快照的。

简易操作:
etcdctl put myKey "this is etcd"
etcdctl get myKey
etcdctl del myKey

etcd实现服务注册与发现:
1docker pull bitnami/etcd

2docker run

3)编写proto文件:

message HelloResponse {
    string hello = 1;
}
message RegisterRequest {
    string name = 1;
    string password = 2;
}
message RegisterResponse {
    string uid = 1;
}
service Server {
    rpc Hello(Empty) returns(HelloResponse);
    rpc Register(RegisterRequest) returns(RegisterResponse);
}

4)通过这个文件和一个.sh脚本生成server和client代码。

生成服务端的etcd.go:

func etcdRegister(addr string) error {
	log.Printf("etcdRegister %s\b", addr)
	etcdClient, err := clientv3.NewFromURL(etcdUrl)
	if err != nil {
		return err
	}
	em, err := endpoints.NewManager(etcdClient, serviceName)
	if err != nil {
		return err
	}
	
	err = em.AddEndpoint(context.TODO(), fmt.Sprintf("%s/%s", serviceName, addr), endpoints.Endpoint{Addr: addr})
	if err != nil {
		return err
	}

	return nil
}

func etcdUnRegister(addr string) error {
	log.Printf("etcdUnRegister %s\b", addr)
	if etcdClient != nil {
		em, err := endpoints.NewManager(etcdClient, serviceName)
		if err != nil {
			return err
		}
		err = em.DeleteEndpoint(context.TODO(), fmt.Sprintf("%s/%s", serviceName, addr))
		if err != nil {
			return err
		}
		return err
	}

	return nil
}

生成的服务端的server.go:

func (s Server) Hello(ctx context.Context, request *rpc.Empty) (*rpc.HelloResponse, error) {
	resp := rpc.HelloResponse{Hello: "hello client."}
	return &resp, nil
}

func (s Server) Register(ctx context.Context, request *rpc.RegisterRequest) (*rpc.RegisterResponse, error) {
	resp := rpc.RegisterResponse{}
	resp.Uid = fmt.Sprintf("%s.%s", request.GetName(), request.GetPassword())
	return &resp, nil
}

生成的服务端的main.go:

func main() {
	var port int
	flag.IntVar(&port, "port", 8001, "port")
	flag.Parse()
	addr := fmt.Sprintf("localhost:%d", port)

	ch := make(chan os.Signal, 1)
	go func() {
		//开启协程从ch管道中读取,如果有服务停止,则注销etcd中的服务
		s := <-ch
		etcdUnRegister(addr)
		os.Exit(0)
	}()

	//注册服务
	err := etcdRegister(addr)

	if err != nil {
		panic(err)

	}
	
	lis, err := net.Listen("tcp", addr)

	if err != nil {
		panic(err)
	}

	grpcServer := grpc.NewServer(grpc.UnaryInterceptor(UnaryInterceptor()))

	rpc.RegisterServerServer(grpcServer, Server{})

	log.Printf("service start port %d\n", port)
	
	if err := grpcServer.Serve(lis); err != nil {
		panic(err)
	}
}

func UnaryInterceptor() grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
		log.Printf("call %s\n", info.FullMethod)
		resp, err = handler(ctx, req)
		return resp, err
	}
}

生成的客户端的代码client.go:

func main() {
	//获取etcd客户端
	etcdClient, err := clientv3.NewFromURL(etcdUrl)
	if err != nil {
		panic(err)
	}
	etcdResolver, err := resolver.NewBuilder(etcdClient)

	//通过grpc与服务建立连接
	conn, err := grpc.Dial(fmt.Sprintf("etcd:///%s", serviceName), grpc.WithResolvers(etcdResolver), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, roundrobin.Name)))

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

	ServerClient := rpc2.NewServerClient(conn)

	for {
		helloRespone, err := ServerClient.Hello(context.Background(), &rpc2.Empty{})
		if err != nil {
			fmt.Printf("err: %v", err)
			return
		}

		log.Println(helloRespone, err)
		time.Sleep(500 * time.Millisecond)
	}

}

5)启动三个server,并向etcd注册服务

// 进入server/main.go所在目录
go run . --port 8081
go run . --port 8082
go run . --port 8083

6)启动一个client端,通过etcd拉取服务
go run .\main.go

观察三个server的打印,可以发现,client端的请求时负载均衡的,每个server都有可能被访问到

7)我们停止server3,发现client的请求被均衡的分发到server1、server2

8)进入docker部署的etcd内部,查询所有的key:发现只有8081和8082的

相关推荐

  1. etcd学习笔记

    2024-06-05 22:42:03       30 阅读
  2. 一起学习ETCD系列——简单安装

    2024-06-05 22:42:03       58 阅读
  3. loki etcd

    2024-06-05 22:42:03       71 阅读
  4. etcd安装

    2024-06-05 22:42:03       52 阅读
  5. etcd备份

    2024-06-05 22:42:03       44 阅读
  6. Etcd实战(一)-部署etcd集群

    2024-06-05 22:42:03       51 阅读
  7. docker安装etcdetcd客户端

    2024-06-05 22:42:03       29 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-05 22:42:03       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-05 22:42:03       106 阅读
  3. 在Django里面运行非项目文件

    2024-06-05 22:42:03       87 阅读
  4. Python语言-面向对象

    2024-06-05 22:42:03       96 阅读

热门阅读

  1. AGI系列(4):提示词的优化、打分、迭代

    2024-06-05 22:42:03       24 阅读
  2. 【Python】异常处理

    2024-06-05 22:42:03       33 阅读
  3. Flutter开发指南

    2024-06-05 22:42:03       35 阅读
  4. K8s:Pod初识

    2024-06-05 22:42:03       30 阅读
  5. vue父组件如何向子组件传递数据?

    2024-06-05 22:42:03       32 阅读
  6. GLFW按键及鼠标事件等

    2024-06-05 22:42:03       31 阅读
  7. K-均值聚类算法及其优缺点

    2024-06-05 22:42:03       34 阅读
  8. Linux系统管理磁盘管理003

    2024-06-05 22:42:03       25 阅读
  9. Qt QML学习资料链接

    2024-06-05 22:42:03       25 阅读
  10. docker和虚拟机的异同

    2024-06-05 22:42:03       28 阅读
  11. Redis单线程

    2024-06-05 22:42:03       26 阅读
  12. 基于单片机的电子万年历设计

    2024-06-05 22:42:03       28 阅读
  13. 第十五届蓝桥杯总结

    2024-06-05 22:42:03       29 阅读
  14. cesium学习6-相机camera

    2024-06-05 22:42:03       29 阅读
  15. Pytorch常用函数用法归纳:Tensor张量之间的计算

    2024-06-05 22:42:03       23 阅读