consul简介
官网介绍地址 还有视频介绍不过是应为的
学习参考的资料
官网
中文文档
环境搭建
说明
# 虚拟机操作系统
> uname -a
> Linux n248-174-155 4.14.81.bm.15-amd64 #1 SMP Debian 4.14.81.bm.15 Sun Sep 8 05:02:31 UTC 2019 x86_64 GNU/Linux
下载安装
# 下载
wget https://releases.hashicorp.com/consul/1.11.1/consul_1.11.1_linux_amd64.zip
# 解压
unzip consul_1.11.1_linux_amd64.zip
解压后只有一个可执行文件,将这个可执行文件加入到环境变量PATH中即可,记得使用source 命令让PATH的变量重新加载
测试是否安装成功
# consul version 查看版本号
root@n248-174-155:~/file$ consul version
Consul v1.11.1
Revision 2c56447e
Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)
另外由于启动时,指定了参数开始 web-ui 所以可以在web端可视化的展示服务的状态,将ip替换成你的ip即可
http://10.248.174.155:8500/
命令实战
启动 consul
# 后台启动方式,指定了日志文件路径/home/root/consul/consul.log
nohup consul agent -dev -client 0.0.0.0 -ui > /home/root/consul/consul.log 2>&1 &
查看进程
root@n248-174-155:~/file$ netstat -ntpl|grep consul
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 127.0.0.1:8300 0.0.0.0:* LISTEN 793138/consul
tcp 0 0 127.0.0.1:8301 0.0.0.0:* LISTEN 793138/consul
tcp 0 0 127.0.0.1:8302 0.0.0.0:* LISTEN 793138/consul
tcp6 0 0 :::8500 :::* LISTEN 793138/consul
tcp6 0 0 :::8502 :::* LISTEN 793138/consul
tcp6 0 0 :::8600 :::* LISTEN 793138/consul
新增kv
consul kv put services/svr1/001 '{"Addr":"192.168.4.5","Port":3358}'
consul kv put services/svr1/002 '{"Addr":"192.164.45.22","Port":3458}'
consul kv put services/svr2/001 '{"Addr":"192.138.44.55","Port":3456}'
新增key 指定额外flags
consul kv put -flags=25 service/svr1/003 '{"Addr":"192.174.35.67","Port":8890}'
查询kv
consul kv get service/svr1/001
查询kv详情
consul kv get -detailed service/svr1/001
查询所有kv 递归查询
consul kv get -recurse
根据key前缀查询,返回所有kv
consul kv get -recurse service/svr1/
根据key前缀查询,只返回key
consul kv get -keys service/svr1/
删除kv
consul kv delete service/svr1/001
根据前缀删除
consul kv delete -recurse service/svr1/
watch 某个前缀的结点,会一次性返回所有结点的信息
consul watch -type=keyprefix -prefix=/services/svr1/001
服务注册
# 指定服务ip地址、端口号、服务唯一id、服务名、服务的多个tag
consul services register -address=124.3.2.4 -port=4458 -id=1234001 -name=mysvr-rpc -tag=v.1.1 -tag=user
为服务注册添加健康检查
heath.json
{
"ID": "service:1234001",
"Name": "自定义结点健康检查",
"Notes": "回调http接口查看",
"DeregisterCriticalServiceAfter": "30s",
"HTTP": "http://ip:port/consul/health/?id=555",
"Method": "GET",
"Interval": "10s",
"Timeout": "5s",
"ServiceID":"1234001"
}
- ID: 健康检查的唯一id,类似于db里面的主键
- Name: 健康检查的名字,后续可以在自带的web面板展示出来
- DeregisterCriticalServiceAfter: 代表服务停止多久后进行注销该服务
- HTTP: 去哪个地址检测服务的健康状态
- Method: 发送请求的方式
- Interval:健康检查时间间隔,即心跳间隔
- Timeout:请求超时时间
- ServiceID:额外参数,如果指定则是检测具体某个服务的健康状态,不指定则是consul的agent健康状态
# heath.json 定义的是只针对某个服务结点的健康检查,直接从文件中读取具体的数据
curl --request PUT --data @heath.json http://127.0.0.1:8500/v1/agent/check/register
服务卸载
# 根据服务的唯一id卸载服务
consul services deregister -id=1234001
获取所有健康检查条目
curl http://127.0.0.1:8500/v1/agent/checks
获取服务信息
# 只返回健康没问题的
curl http://127.0.0.1:8500/v1/health/service/mysvr-rpc?passing=1
go-consul实战
服务注册
package consul
import (
"context"
"fmt"
"google.golang.org/grpc/health/grpc_health_v1"
"log"
"strings"
"github.com/hashicorp/consul/api"
uuid "github.com/satori/go.uuid"
)
const (
consulIp = "10.248.174.155"
consulPort = "8500"
)
func RandomStr(len int) string {
nUid := uuid.NewV4().String()
str := strings.Replace(nUid, "-", "", -1)
if len < 0 || len >= 32 {
return str
}
return str[:len]
}
// HttpReg 服务注册时候提供一个健康检查地址,支持http,grpc
func HttpReg(svrName string, ipAddr string, port int) string {
// 创建Consul客户端连接
client, err := api.NewClient(&api.Config{
Address: fmt.Sprintf("http://%v:%v", consulIp, consulPort),
})
if err != nil {
log.Fatalf("client 创建失败,退出:%v\n", err)
}
svrID := RandomStr(32)
// 设置Consul对服务健康检查的参数
check := api.AgentServiceCheck{
HTTP: fmt.Sprintf("http://%v:%v/consul/health/?id=%v", "127.0.0.1", 80, svrID), // 健康检查地址,自定义ip和端口
Interval: "3s", // 健康检查频率
Timeout: "2s", // 健康检查超时
Notes: "Consul 代码健康检查",
DeregisterCriticalServiceAfter: "5s", // 健康检查失败30s后 consul自动将注册服务删除
Name: "代码自定义检查svr1",
//GRPC: fmt.Sprintf("%v:%v/%v", svcHost, svcPort, "svrName"),
}
//设置微服务向Consul注册信息
reg := &api.AgentServiceRegistration{
ID: svrID, // 服务节点的ID
Name: svrName, // 服务名称
Address: ipAddr, // 服务IP
Port: port, // 服务端口
Tags: []string{"v1.1", "backup"}, // 标签,可在服务发现时筛选服务,类似v1.1
Check: &check, // 健康检查
}
// 执行注册
if err := client.Agent().ServiceRegister(reg); err != nil {
log.Fatalln(err)
}
return svrID
}
//HttpUnReg 服务卸载
func HttpUnReg(svrID string) {
// 创建Consul客户端连接
client, err := api.NewClient(&api.Config{
Address: fmt.Sprintf("http://%v:%v", consulIp, consulPort),
})
if err != nil {
log.Fatalf("client 创建失败,退出:%v\n", err)
}
// 执行服务卸载
if err := client.Agent().ServiceDeregister(svrID); err != nil {
log.Fatalln(err)
}
}
//========如果使用grpc接口实现健康检查,则需要实现HealthServer 接口,服务启动时候注册这个pb==========
// HealthImpl 健康检查实现
type HealthImpl struct{}
// Check 实现健康检查接口,这里直接返回健康状态
func (h *HealthImpl) Check(ctx context.Context, req *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) {
return &grpc_health_v1.HealthCheckResponse{
Status: grpc_health_v1.HealthCheckResponse_SERVING,
}, nil
}
// Watch 让HealthImpl实现RegisterHealthServer内部的interface接口
func (h *HealthImpl) Watch(req *grpc_health_v1.HealthCheckRequest, w grpc_health_v1.Health_WatchServer) error {
return nil
}
服务注册单元测试
package consul
import (
"fmt"
"github.com/hashicorp/consul/api"
"log"
"testing"
"time"
)
func TestClient(t *testing.T) {
// Get a new client
client, err := api.NewClient(&api.Config{
Address: "http://10.248.174.155:8500",
})
if err != nil {
log.Fatalln(err)
}
// Get a handle to the KV API
kv := client.KV()
// PUT a new KV pair
p := &api.KVPair{Key: "my-svr", Value: []byte("1000"), Flags: 32}
_, err = kv.Put(p, nil)
if err != nil {
log.Fatalln(err)
}
// Lookup the pair
pair, _, err := kv.Get("my-svr", nil)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("KV: %v %s\n", pair.Key, pair.Value)
}
func TestHttpReg(t *testing.T) {
svrID := HttpReg("mysvr-rpc", "127.0.0.1", 3456)
log.Printf("服务注册成功:%v\n", svrID)
for true {
// 制定一个定时器,模拟20s后摘除服务
t := time.NewTicker(20 * time.Second)
select {
case tt := <-t.C:
HttpUnReg(svrID)
log.Printf("服务卸载,tt:%v\n", tt)
return
}
}
}
服务发现
package consul
import (
"fmt"
"github.com/hashicorp/consul/api"
"log"
)
// GetNode 没有watch的api 无法感知结点的变更,如果做本地缓存则需要启动协程去定时轮询,全量更新到本地缓存
func GetNode(svrName string) {
// 创建Consul客户端连接
client, err := api.NewClient(&api.Config{
Address: fmt.Sprintf("http://%v:%v", consulIp, consulPort),
})
if err != nil {
log.Fatalf("client 创建失败,退出:%v\n", err)
}
// 根据服务名和tag 是否健康检查通过 以及其它option 筛选service实例
service, meta, serr := client.Health().Service(svrName, "", true, nil)
log.Printf("结束svr%+v,meta:%+v, err:%v\n", service, meta, serr)
for _, s := range service {
fmt.Printf("服务:%+v\n", s.Service)
}
}
服务发现单元测试
package consul
import "testing"
func TestGetNode(t *testing.T) {
GetNode("mysvr-rpc")
}