Golang|go使用Consul实用指南

Consul教程 Consul是一个服务发现软件, 提供了服务发现\键值存储\健康检查等功能
常用指令

  • agent指令
    • -bind=0.0.0.0 指定consul所在机器的ip地址
    • -http-port 指定web接口服务端口
    • -client 指定哪些机器可以访问consul, 0.0.0.0表示所有机器
    • -data-dir=path 指定服务数据文件存储位置
    • -dev 开发者模式,直接以默认模式启动consul
    • -node=hostname 服务发现的名字
    • -rejoin consul启动的时候,加入到的consul集群
    • -server 以服务方式开启server, 允许其他的consul连接到开启的consul上,不加则以客户端方式开启
    • -ui 可以谁用web页面来查看服务发现的详情
    • -config-dir 配置文件地址, 如果是文件夹会将所有文件合并,里面可以配置自己所在节点提供的服务
    • -bootstrap-expect 在一个集群中期望提供的server节点数目,当该值提供时, consul一直等到达到指定的server数目才会参加选举,推举自己为leader。这个可以避免不一致和脑裂现象
    启动
    consul agent -server -bootstrap-expect 1 -ui -rejoin -http-port=8080 -bind="127.0.0.1" -data-dir C:\Windows\tmp
  • members
    查看集群中有多少成员
  • info
    查看当前系欸但信息
  • leave
    优雅关闭consul
  • reload
    重新加载配置文件
常用功能 Consul最重要的的功能就是定义服务, 服务管理和健康检查
定义服务
要定义一个服务, 有两种方式:
  1. 通过写配置文件, 让Consul读取
  2. 通过接口向Consul服务进行注册
一般我们采用第二种方式, 但我们可以通过第一种方式的书写规则来了解服务定义的一些选项
我们可以通过启动时指定-config-dir来指定配置文件所在位置, 配置文件时json格式, 有多个会合并在一起
例子:
{ "service": { "name": "redis", // 在consul面板上显示的服务名 "id": "redis", // 服务id, 一般通过服务id对服务进行管理, 若不指定则会使用name作为id "address": "127.0.0.1", // 服务地址 "port": 80, // 服务运行的端口 "tags": [ "primary" ], // 服务的标签, 可以作为服务的额外信息 //服务健康检查, 后面介绍 "checks": [ { "args": [ "/bin/check_redis", "-p", "7000" ], "interval": "30s", "timeout" : "60s" } ] } }

服务管理
所谓服务管理就是指服务发现\服务注册\服务注销
consul服务端均提供了相应的http\dns等方式的接口可以方便地操作,
一般我们用go是用官方提供的一个api库进行操作, 对这些http的接口就不再过多赘述, 可以查阅官方文档
Consul API
服务健康检查
健康检查是为了避免服务突然宕机而调用方不知道的情况发生, 原理就是隔一段时间通信一次
consul提供了 tcp\ssl\ttl\udp\script\http等多种方式来进行健康检查,可以在服务注册时指定健康检查的方式
consul根据interval变量来决定多少时间间隔来通讯
根据timeout变量来决定发送请求后多久未收到回应算作失败
这里我们介绍三种, http|tcp|script方式
  1. http方式
{ "check": { "id": "api", "name": "HTTP API on port 5000", "http": "https://localhost:5000/health",//调用接口 "method": "POST", "header": { "Content-Type": ["application/json"] }, "body": "{\"method\":\"health\"}", "interval": "10s",// "timeout": "1s" } }

  1. tcp方式
{ "check": { "id": "ssh", "name": "SSH TCP on port 22", "tcp": "localhost:22", "interval": "10s", "timeout": "1s" } }

  1. script脚本方式
【Golang|go使用Consul实用指南】脚本方式略有不同,原理是通过调用脚本,查看脚本的返回值来判断健康检查是否通过
脚本退出代码为 0 : 通过健康检查
1 : 警告
其他: 健康检查失败
{ "check": { "id": "mem-util", "name": "Memory utilization", "args": ["/usr/local/bin/check_mem.py", "-limit", "256MB"],// 会被拼成 /usr/local/bin/check_mem.py -limit 256MB 来进行调用 "interval": "10s", "timeout": "1s" } }

  1. grpc方式
检查grpc整个server状态
{ "check": { "id": "mem-util", "name": "Service health status", "grpc": "127.0.0.1:12345", "grpc_use_tls": true,//是否使用tls, 默认不使用 "interval": "10s" } }

只检查服务器上的某一个服务
{ "check": { "id": "mem-util", "name": "Service health status", "grpc": "127.0.0.1:12345/my_service", "grpc_use_tls": true, "interval": "10s" } }

go使用官方api包来定义服务\查询服务 console的github官网提供了一个go包操作consul服务端的实现, 我们可以使用这个包来实现服务发现
那么编写一个使用consul注册服务的服务端程序的核心逻辑是这样:
  1. 创建consul客户端
  2. 使用创建的consul客户端连接相应的consul agent
  3. 向consul注册服务
  4. 运行服务
  5. 运行完毕后向consul注销服务
示例代码:
package mainimport ( "log" "net" "github.com/hashicorp/consul/api" )func main() { // 使用默认配置创建consul客户端 consulClient, err := api.NewClient(api.DefaultConfig()) if err != nil { log.Fatal(err) } // 注册服务 // consulClient.Agent()先获取当前机器上的consul agent节点 consulClient.Agent().ServiceRegister(&api.AgentServiceRegistration{ ID:"MyService", Name:"My Service", Address: "127.0.0.1", Port:5050, Check: &api.AgentServiceCheck{ CheckID:"MyService", TCP:"127.0.0.1:5050", Interval: "10s", Timeout:"1s", }, }) // 运行完成后注销服务 defer consulClient.Agent().ServiceDeregister("MyService") l, err := net.Listen("tcp", ":5050") if err != nil { log.Fatal(err) } for { conn, err := l.Accept() if err != nil { log.Fatal(err) } go func() { log.Printf("Ip: %s connected", conn.RemoteAddr().String()) }() } }

我们通过tcp每10秒进行健康检查, 输出窗口每10秒就会输出有新连接到来, 这是consul做的
$ go run . 2022/06/09 20:17:51 Ip: 127.0.0.1:53011 connected 2022/06/09 20:18:01 Ip: 127.0.0.1:53038 connected 2022/06/09 20:18:11 Ip: 127.0.0.1:53045 connected

那么服务的请求端(客户端)需要通过consul来获取服务的地址和端口,则需要这么几步:
  1. 创建consul客户端
  2. 使用创建的consul客户端连接相应的consul agent
  3. 向consul请求相应服务id的注册信息
  4. 如果获取到了相应的注册信息, 就通过地址和端口请求服务
代码示例:
package mainimport ( "fmt" "log" "github.com/hashicorp/consul/api" )func main() { consulClient, err := api.NewClient(api.DefaultConfig()) if err != nil { log.Fatal(err) } service, _, err := consulClient.Agent().Service("MyService", nil) if err != nil { log.Fatal(err) } fmt.Printf("Got Service: ip-%s, port-%d", service.Address, service.Port) }

保证服务端运行的情况运行客户端:
$ go run . Got Service: ip-127.0.0.1, port-5050

Consul使用Raft算法保证集群一致性 未完待续

    推荐阅读