目前市面上常見的服務治理有consul,etcd,zookeeper,euerka,我們需要根據自己的服務特點選擇自己相對合適的服務治理工具。
| Feature | Consul | zookeeper | etcd | euerka |
|---|---|---|---|---|
| 服務健康檢查 | 服務狀態,內存,硬盤等 | (弱)長連接,keepalive | 連接心跳 | 可配支持 |
| 多數據中心 | 支持 | — | — | — |
| kv存儲服務 | 支持 | 支持 | 支持 | — |
| 一致性 | raft | paxos | raft | — |
| cap | ca | cp | cp | ap |
| 使用接口(多語言能力) | 支持http和dns | 客戶端 | http/grpc | http(sidecar) |
| watch支持 | 全量/支持long polling | 支持 | 支持 long polling | 支持 long polling/大部分增量 |
| 自身監控 | metrics | — | metrics | metrics |
| 安全 | acl /https | acl | https支持(弱) | — |
| spring cloud集成 | 已支持 | 已支持 | 已支持 | 已支持 |
調研一個工具需要看到其優點,更需要看到其缺點,當服務優點大於自身業務需求缺點,且缺點有對應的解決方案時,我們可以傾向於考慮。
euerka 據說現在已停止維護,決定不考慮使用。
zookeeper 為java開發的,需要java環境,相對比較復雜,優先級較低。
etcd 與consul為go開發,部署簡單,功能相對更符合自身業務的需求。
consul監控檢查更為豐富,支持多數據中心,webui查看等,配合consul-template實現nginx動態負載均衡等特點更符合自身業務需求,因為決定選用consul作為業務的服務治理工具。
使用consul,其主要有四大特性:
1. 服務發現:利用服務注冊,服務發現功能來實現服務治理。
2. 健康檢查:利用consul注冊的檢查檢查函數或腳本來判斷服務是否健康,若服務不存在則從注冊中心移除該服務,減少故障服務請求。
3. k/v數據存儲:存儲kv數據,可以作為服務配置中心來使用。
4. 多數據中心:可以建立多個consul集群通過inter網絡進行互聯,進一步保證數據可用性。
本人也是剛開始學習consul,感覺使用consul主要也就是兩大作用,服務注冊發現,配置共享。
現在開始學習consul:
啟動單機consul:consul agent -server -bootstrap-expect=1 -data-dir=data -node=consul -bind=x172.16.242.129-ui -client=0.0.0.0
我們可已通過consul --help 修改一些配置信息,比如集群個數,綁定地址,數據存放地址,配置文件地址,加入集群等配置
啟動consul后我們可以通過web查看consul運行狀況:http://172.16.242.129:8500/ui/dc1/nodes
服務啟動后,現在開始測試consul的第一個功能:服務注冊發現,本人使用的為go語言, 下面主要為服務注冊,發現的一個demo, 需要注意的是,根據自身的實際配置去修改consul的地址信息和要注冊服務的地址信息
package main
import (
"github.com/gin-gonic/gin"
consulapi "github.com/hashicorp/consul/api"
"net/http"
"fmt"
"log"
)
func main() {
r := gin.Default()
// consul健康檢查回調函數
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "ok",
})
})
// 注冊服務到consul
ConsulRegister()
// 從consul中發現服務
ConsulFindServer()
// 取消consul注冊的服務
//ConsulDeRegister()
http.ListenAndServe(":8081", r)
}
// 注冊服務到consul
func ConsulRegister() {
// 創建連接consul服務配置
config := consulapi.DefaultConfig()
config.Address = "172.16.242.129:8500"
client, err := consulapi.NewClient(config)
if err != nil {
log.Fatal("consul client error : ", err)
}
// 創建注冊到consul的服務到
registration := new(consulapi.AgentServiceRegistration)
registration.ID = "111"
registration.Name = "go-consul-test"
registration.Port = 8081
registration.Tags = []string{"go-consul-test"}
registration.Address = "10.13.153.128"
// 增加consul健康檢查回調函數
check := new(consulapi.AgentServiceCheck)
check.HTTP = fmt.Sprintf("http://%s:%d", registration.Address, registration.Port)
check.Timeout = "5s"
check.Interval = "5s"
check.DeregisterCriticalServiceAfter = "30s" // 故障檢查失敗30s后 consul自動將注冊服務刪除
registration.Check = check
// 注冊服務到consul
err = client.Agent().ServiceRegister(registration)
}
// 取消consul注冊的服務
func ConsulDeRegister() {
// 創建連接consul服務配置
config := consulapi.DefaultConfig()
config.Address = "172.16.242.129:8500"
client, err := consulapi.NewClient(config)
if err != nil {
log.Fatal("consul client error : ", err)
}
client.Agent().ServiceDeregister("111")
}
// 從consul中發現服務
func ConsulFindServer() {
// 創建連接consul服務配置
config := consulapi.DefaultConfig()
config.Address = "172.16.242.129:8500"
client, err := consulapi.NewClient(config)
if err != nil {
log.Fatal("consul client error : ", err)
}
// 獲取所有service
services, _ := client.Agent().Services()
for _, value := range services{
fmt.Println(value.Address)
fmt.Println(value.Port)
}
fmt.Println("=================================")
// 獲取指定service
service, _, err := client.Agent().Service("111", nil)
if err == nil{
fmt.Println(service.Address)
fmt.Println(service.Port)
}
}
func ConsulCheckHeath() {
// 創建連接consul服務配置
config := consulapi.DefaultConfig()
config.Address = "172.16.242.129:8500"
client, err := consulapi.NewClient(config)
if err != nil {
log.Fatal("consul client error : ", err)
}
// 健康檢查
a, b, _ := client.Agent().AgentHealthServiceByID("111")
fmt.Println(a)
fmt.Println(b)
}
func ConsulKVTest() {
// 創建連接consul服務配置
config := consulapi.DefaultConfig()
config.Address = "172.16.242.129:8500"
client, err := consulapi.NewClient(config)
if err != nil {
log.Fatal("consul client error : ", err)
}
// KV, put值
values := "test"
key := "go-consul-test/172.16.242.129:8100"
client.KV().Put(&consulapi.KVPair{Key:key, Flags:0, Value: []byte(values)}, nil)
// KV get值
data, _, _ := client.KV().Get(key, nil)
fmt.Println(string(data.Value))
// KV list
datas, _ , _:= client.KV().List("go", nil)
for _ , value := range datas{
fmt.Println(value)
}
keys, _ , _ := client.KV().Keys("go", "", nil)
fmt.Println(keys)
}
我們可根據上述功能去實現一個服務注冊,服務發現,配置共享的功能,我們可以圍繞這些根據自身業務需求進行靈活的變動,個人想法:根據此功能專門做一個服務管理的模塊,客戶端注冊服務到服務模塊,服務管理去提供其他模塊的服務發現的功能,同時跟監控結合,當服務不可用時,或服務不存在時,通過監控通知相關人員,我們也可以使用頁面跟我們服務管理結合,通過前台服務錄入形式進行注冊服務等等。
假如公司服務使用nginx負載均衡的話,我們可與consul和consul-template結合,動態生成nginx的配置文件,然后重新reload nginx,實現nginx的動態負載均衡。
當然還有其他方式實現nginx的動態負載均衡,如使用nginx-upsync-module和consul,openResty的lua腳本實現nginx動態負載均衡。
