etcdの紹介
etcdは、2013年6月にCoreOSチームによって開始されたオープンソースプロジェクトです。その目標は、可用性の高い分散型Key-Valueデータベースを構築することです。etcdは内部的にraft
コンセンサスアルゴリズムとしてプロトコルを使用し、etcdはGo言語に基づいて実装されます。
サービス検出システムなどとして、etcdには次の特徴があります。
- シンプル:インストールと構成がシンプルで、対話用にHTTP APIが提供されており、使用も非常に簡単です。
- セキュリティ:SSL証明書の検証をサポート
- 高速:公式のベンチマークデータによると、1つのインスタンスが1秒あたり2k以上の読み取り操作をサポートします
- 信頼性:分散システムデータの可用性と一貫性を実現するために、いかだアルゴリズムが採用されています
etcdプロジェクトアドレス:https://github.com/etcd-io/etcd
etcdアプリケーションシナリオ
etcdのより多くのアプリケーションシナリオがサービスディスカバリに使用されます。サービスディスカバリ(サービスディスカバリ)は、分散システムで最も一般的な問題の1つ、つまり、同じ分散クラスタ内のプロセスまたはサービスが相互に検出して接続を確立する方法を解決することです。
本質的に、サービスディスカバリは、updポートまたはtcpポートでリッスンしているプロセスがクラスター内にあるかどうかを認識し、名前で検索してリンクすることです。
サービスディスカバリの問題を解決するには、次の3つの柱が必要ですが、いずれも必須ではありません。
- 一貫性が高く、可用性の高いサービスストレージカタログ。
Ralfアルゴリズムに基づくEtcdは、このように一貫性が高く可用性の高いサービスストレージディレクトリとして生まれました。
- サービスとヘルスサービスのヘルスステータスを登録するためのメカニズム。
ユーザーは、etcdにサービスを登録し、登録されたサービスのキーTTLを構成し、サービスのハートビートを定期的に維持して、ヘルスステータスを監視する効果を実現できます。
- サービスを見つけて接続するためのメカニズム。
etcdインストールパッケージのダウンロードアドレス
etcdアドレス:https://github.com/etcd-io/etcd/releases
ダウンロードする対応するバージョンを選択します。Windowsバージョンの解凍後のファイルディレクトリは次のとおりです。
etcdはサーバー、etcdctlは組み込みクライアントです
バージョン番号etcdctl--versionを表示します
APIバージョンのバージョンを3に設定することをお勧めします
Windowsでバージョンを設定するには、次を使用します。setETCDCTL_API = 3
etcdctlは、バージョン3と2のコマンドと機能の違いを確認するのに役立ちます
すべてのキーを表示するetcdctlのV3バージョン。コマンドは次のとおりです。
etcdctl get "" --prefix --keys-only
同じディレクトリに次の3つの起動スクリプトを作成します。
デフォルトでは、ポート2379はHTTP APIサービスを提供するために使用され、ポート2380はピアとの通信に使用されます。
start01.bat
.\etcd.exe --name etcd01 ^
--data-dir .\data\etcd01 ^
--advertise-client-urls http://127.0.0.1:2379 ^
--listen-client-urls http://127.0.0.1:2379 ^
--listen-peer-urls http://127.0.0.1:2380 ^
--initial-advertise-peer-urls http://127.0.0.1:2380 ^
--initial-cluster-token etcd-cluster-1 ^
--initial-cluster etcd01=http://127.0.0.1:2380,etcd02=http://127.0.0.1:2381,etcd03=http://127.0.0.1:2382 ^
--initial-cluster-state new
pause
start02.bat
.\etcd.exe --name etcd02 ^
--data-dir .\data\etcd02 ^
--advertise-client-urls http://127.0.0.1:3379 ^
--listen-client-urls http://127.0.0.1:3379 ^
--listen-peer-urls http://127.0.0.1:2381 ^
--initial-advertise-peer-urls http://127.0.0.1:2381 ^
--initial-cluster-token etcd-cluster-1 ^
--initial-cluster etcd01=http://127.0.0.1:2380,etcd02=http://127.0.0.1:2381,etcd03=http://127.0.0.1:2382 ^
--initial-cluster-state new
pause
start03.bat
.\etcd.exe --name etcd03 ^
--data-dir .\data\etcd03 ^
--advertise-client-urls http://127.0.0.1:4379 ^
--listen-client-urls http://127.0.0.1:4379 ^
--listen-peer-urls http://127.0.0.1:2382 ^
--initial-advertise-peer-urls http://127.0.0.1:2382 ^
--initial-cluster-token etcd-cluster-1 ^
--initial-cluster etcd01=http://127.0.0.1:2380,etcd02=http://127.0.0.1:2381,etcd03=http://127.0.0.1:2382 ^
--initial-cluster-state new
pause
次に、/ data / etcd01、/ data / etcd02、/ data / etcd03などの同じレベルのディレクトリに対応するdata-dirを作成します。作成する必要はありません。起動後に自動的に作成されます。
start01.bat、start02.bat、start03.batの3つのスクリプトを順番に起動し、etcdctl.exeメンバーリストを使用します。以下の情報が出力された場合は、クラスターが正常に作成されたことを意味します。
etcdクラスター起動パラメーターの説明
パラメータ |
使用説明書 |
|
---|---|---|
-名前etcd0 | このメンバーの名前 | |
--initial-advertise-peer-urls http://192.168.2.55:2380 | 他のメンバーが使用する他のメンバーは、このアドレスを介してこのメンバーと情報を交換します。他のメンバーからアドレスにアクセスできることを確認してください。静的構成モードでは、このパラメーターの値は--initial-clusterパラメーターにも存在する必要があります。 memberIDの生成は、-initial-cluster-tokenおよび--initial-advertise-peer-urlsの影響を受けます。 |
|
--listen-peer-urls http://0.0.0.0:2380 | このメンバー側は、他のメンバーから送信されたアドレスを監視するために使用されます。ipはすべて0です。これは、メンバー側のすべてのインターフェイスを監視することを意味します | |
--listen-client-urls http://0.0.0.0:2379 | このメンバー側は、etcdクライアントによって送信されたアドレスを監視するために使用されます。ipはすべて0です。これは、メンバー側のすべてのインターフェイスを監視することを意味します | |
--advertise-client-urls http://192.168.2.55:2379 | etcdの顧客が使用する顧客は、このアドレスを介してこのメンバーと情報を交換します。クライアント側からアドレスにアクセスできることを確認する必要があります | |
--initial-cluster-token etcd-cluster-2 | 異なるクラスターを区別するために使用されます。ローカルに複数のクラスターがある場合は、それらを異なるものに設定します。 | |
--initial-cluster etcd0 = http://192.168.2.55:2380、 |
このメンバー側で使用されます。クラスタ内のすべてのノードの情報を記述します。このメンバーは、この情報に基づいて他のメンバーに連絡します。 memberIDの生成は、-initial-cluster-tokenおよび--initial-advertise-peer-urlsの影響を受けます。 |
|
--initial-cluster-state new | これが新しいクラスターであるかどうかを示すために使用されます。新規と既存の2つの値があります。既存として入力されている場合、メンバーは開始時に他のメンバーと対話しようとします。 クラスターを初めて作成するときは、新規として入力する必要があります。最後のノードの既存の入力を試みた後は正常であり、他のノードを既存のノードとして入力することはできません。 クラスターの運用中に、障害後にメンバーが回復すると、メンバーは既存のもので満たされ、試行後に新しいもので満たされるのが通常です。 |
|
-data-dir | ノードのデータストレージディレクトリを指定します。これらのデータには、ノードID、クラスターID、クラスター初期化構成、スナップショットファイルが含まれます。-wal-dirが指定されていない場合はWALファイルが保存され、指定されていない場合はデフォルトのディレクトリが使用されます。 |
|
-discovery http://192.168.1.163:20003/v2/keys/discovery/78b12ad7-2c1d-40db-9416-3727baf686cb | 用于自发现模式下,指定第三方etcd上key地址,要建立的集群各member都会向其注册自己的地址。 |
- —data-dir 指定节点的数据存储目录,这些数据包括节点ID,集群ID,集群初始化配置,Snapshot文件,若未指定—wal-dir,还会存储WAL文件;
- —wal-dir 指定节点的was文件的存储目录,若指定了该参数,wal文件会和其他数据文件分开存储。
- —name 节点名称
- —initial-advertise-peer-urls 告知集群其他节点url.
- — listen-peer-urls 监听URL,用于与其他节点通讯
- — advertise-client-urls 告知客户端url, 也就是服务的url
- — initial-cluster-token 集群的ID
- — initial-cluster 集群中所有节点
检查etcd服务列表
使用 etcdctl.exe member list 命令查看集群列表:
D:\etcd\etcd-v3.3.25-windows-amd64>etcdctl.exe member list
19ac17627e3e396f: name=etcd03 peerURLs=http://127.0.0.1:2382 clientURLs=http://127.0.0.1:4379 isLeader=false
bf9071f4639c75cc: name=etcd01 peerURLs=http://127.0.0.1:2380 clientURLs=http://127.0.0.1:2379 isLeader=true
e7b968b9fb1bc003: name=etcd02 peerURLs=http://127.0.0.1:2381 clientURLs=http://127.0.0.1:3379 isLeader=false
如果出现如下的信息,代表可能etcd启动过程阻塞住了,只要在cmd窗口里按下回车键就ok了
D:\etcd\etcd-v3.3.25-windows-amd64>etcdctl.exe member list
client: etcd cluster is unavailable or misconfigured; error #0: dial tcp 127.0.0.1:4001: connectex: No connection could be made because the target machine actively refused it.
; error #1: client: endpoint http://127.0.0.1:2379 exceeded header timeout
或者使用curl访问或网页输入查看http://127.0.0.1:2379/v2/members:
curl http://127.0.0.1:2379/v2/members
返回以下结果(3个节点):
{
"members": [{
"id": "19ac17627e3e396f",
"name": "etcd03",
"peerURLs": ["http://127.0.0.1:2382"],
"clientURLs": []
}, {
"id": "bf9071f4639c75cc",
"name": "etcd01",
"peerURLs": ["http://127.0.0.1:2380"],
"clientURLs": ["http://127.0.0.1:2379"]
}, {
"id": "e7b968b9fb1bc003",
"name": "etcd02",
"peerURLs": ["http://127.0.0.1:2381"],
"clientURLs": ["http://127.0.0.1:3379"]
}]
}
在任意节点执行健康检查,查看集群状态:
(注:仅etcdctl 有v2和v3两种api,注意区别),关于v3 api的用法,参见etcdctl的使用[v3版本]:https://blog.csdn.net/huwh_/article/details/80225902
etcdctl cluster-health
etcd的go客户端的简单操作:
package main
import (
"context"
"fmt"
"time"
"github.com/coreos/etcd/clientv3"
)
func main() {
//客户端配置
config := clientv3.Config{
Endpoints: []string{"127.0.0.1:2379"},
DialTimeout: 5 * time.Second,
}
//建立连接
client, err := clientv3.New(config)
defer client.Close()
if err != nil {
fmt.Println(err)
return
}
fmt.Println("connect success")
//控制超时
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
//1. 增-存值
_, err = client.Put(ctx, "/demo/demo1_key", "demo1_value")
//操作完毕,cancel掉
cancel()
if err != nil {
fmt.Println("put failed, err:", err)
return
}
//2. 查-获取值, 也设置超时
ctx, cancel = context.WithTimeout(context.Background(), time.Second)
resp, err := client.Get(ctx, "/demo/demo1_key")
// Get查询还可以增加WithPrefix选项,获取某个目录下的所有子元素
//eg: resp, err := client.Get(ctx, "/demo/", clientv3.WithPrefix())
cancel()
if err != nil {
fmt.Println("get failed err:", err)
return
}
for _, item := range resp.Kvs { //Kvs 返回key的列表
fmt.Printf("%s : %s \n", item.Key, item.Value)
}
//3. 改-修改值
ctx, _ = context.WithTimeout(context.Background(), time.Second)
_,err = client.Put(ctx, "/demo/demo1_key", "update_value", clientv3.WithPrevKV())
if err != nil {
fmt.Println("get failed err: ", err)
}
//fmt.Println(string(resp.PrevKv.Value))
//4. 删-删除值
ctx, _ = context.WithTimeout(context.Background(), time.Second)
_, err = client.Delete(ctx, "/demo/demo1_key")
if err != nil {
fmt.Println(err)
}
//fmt.Println(resp.PrevKvs)
}
zRPC的简单使用
zRPC来自于最近比较火的一个微服务框架go-zero。go-zero是一个集成了各种工程实践的包含了Web和RPC协议的功能完善的微服务框架,zRPC是其中的一个可独立使用的模块。
zRPC地址:https://github.com/tal-tech/go-zero/tree/master/zrpc
zRPC底层依赖gRPC,内置了服务注册、负载均衡、拦截器等模块,其中还包括自适应降载,自适应熔断,限流等微服务治理方案,是一个简单易用的可直接用于生产的企业级RPC框架。
zRPC支持直连和基于etcd服务发现两种方式,我们以基于etcd做服务发现为例演示zRPC的基本使用:
配置
创建hello.yaml配置文件,配置如下:
Name: hello.rpc // 服务名
ListenOn: 127.0.0.1:9090 // 服务监听地址
Etcd:
Hosts:
- 127.0.0.1:2379 // etcd服务地址
Key: hello.rpc // 服务注册key
注意文件编码必须为utf-8,格式也要正确。
创建hello.proto文件,并生成对应的go代码。
生成go代码:
protoc --go_out=plugins=grpc:. hello.proto
protoc的安装,https://github.com/protocolbuffers/protobuf/releases
从 Protobuf Releases 下载最先版本的发布包安装即可,可放到go的bin目录内全局使用。
下载protobuf编译器所需插件
用git下载protoc在go下运行所需插件(执行): go get github.com/golang/protobuf(gopath的bin目录会生成protoc-gen-go.exe)
proto文件标量类型
proto类型 | go类型 | 备注 | proto类型 | go类型 | 备注 |
---|---|---|---|---|---|
double | float64 | float | float32 | ||
int32 | int32 | int64 | int64 | ||
uint32 | uint32 | uint64 | uint64 | ||
sint32 | int32 | 适合负数 | sint64 | int64 | 适合负数 |
fixed32 | uint32 | 固长编码,适合大于2^28的值 | fixed64 | uint64 | 固长编码,适合大于2^56的值 |
sfixed32 | int32 | 固长编码 | sfixed64 | int64 | 固长编码 |
bool | bool | string | string | UTF8 编码,长度不超过 2^32 | |
bytes | []byte | 任意字节序列,长度不超过 2^32 |
标量类型如果没有被赋值,则不会被序列化,解析时,会赋予默认值。
- strings:空字符串
- bytes:空序列
- bools:false
- 数值类型:0
简单示例:
syntax = "proto3";
package pb;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
服务端:
Server端代码:
package main
import (
"context"
"flag"
"log"
"testzrpc/msgs/zrpc/pb"
"github.com/tal-tech/go-zero/core/conf"
"github.com/tal-tech/go-zero/zrpc"
"google.golang.org/grpc"
)
type Config struct {
zrpc.RpcServerConf
}
var cfgFile = flag.String("f", "./hello.yaml", "cfg file")
func main() {
flag.Parse()
var cfg Config
conf.MustLoad(*cfgFile, &cfg)
srv, err := zrpc.NewServer(cfg.RpcServerConf, func(s *grpc.Server) {
pb.RegisterGreeterServer(s, &Hello{})
})
if err != nil {
log.Fatal(err)
}
srv.Start()
}
type Hello struct{}
func (h *Hello) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "server say,hello " + in.Name}, nil
}
需要注意的是:
需要更改下go.mod文件中的grpc版本号。默认最新的1.34,启动服务端会报错的,可改为使用v1.29.1
etcd/clientv3库跟最新的grpc版本不兼容,需要降低 grpc
版本,如1.26.0。
客户端:
package main
import (
"context"
"log"
"testzrpc/msgs/zrpc/pb"
"github.com/tal-tech/go-zero/core/discov"
"github.com/tal-tech/go-zero/zrpc"
)
func main() {
client := zrpc.MustNewClient(zrpc.RpcClientConf{
Etcd: discov.EtcdConf{
Hosts: []string{"127.0.0.1:2379"},
Key: "hello.rpc",
},
})
conn := client.Conn()
hello := pb.NewGreeterClient(conn)
reply, err := hello.SayHello(context.Background(), &pb.HelloRequest{Name: "go-zero aaaa"})
if err != nil {
log.Fatal(err)
}
log.Println(reply.Message)
}
多个微服务如何使用?如何实现负载均衡和容灾。做个试验,把server.go复制一份,改为server1.go,
把hello.yaml复制一份改为hello1.yaml,改hello1.yaml中的ListenOn,端口变为9091,这时候把server.go和server1.go同时运行起来,看一下:
此时,运行下客户端,发现输出为:
再把server1.go服务停掉,运行下客户端试试输出为:
综上,使用zRPC挺简单的,并且zRPC内置了服务注册、负载均衡、拦截器等模块。
其中还包括自适应降载,自适应熔断,限流等微服务治理方案,是一个简单易用的可直接用于生产的企业级RPC框架。