consul理解與簡單使用

1. 服務(wù)發(fā)現(xiàn)是什么?

我們在做微服務(wù)開發(fā)的時候,客戶端的一個接口可能需要調(diào)用N個服務(wù),客戶端必須知道所有服務(wù)的網(wǎng)絡(luò)位置(ip+port),如下圖所示

傳統(tǒng)模式.png

以往的做法是把服務(wù)的地址放在配置文件活數(shù)據(jù)庫中,這樣就有以下幾個問題:

  • 需要配置N個服務(wù)的網(wǎng)絡(luò)位置,加大配置的復(fù)雜性
  • 服務(wù)的網(wǎng)絡(luò)位置變化,需要改變每個調(diào)用者的配置
  • 集群的情況下,難以做負(fù)載(反向代理的方式除外)

總結(jié)起來一句話:服務(wù)多了,配置很麻煩,問題一大堆

所以現(xiàn)在就選擇服務(wù)發(fā)現(xiàn)來解決這些問題。我們來看一下,服務(wù)發(fā)現(xiàn)如何解決這個問題,具體設(shè)計如下:

服務(wù)發(fā)現(xiàn)模式.png

與之前解決方法不同的是,加了個服務(wù)發(fā)現(xiàn)模塊。服務(wù)端把當(dāng)前自己的網(wǎng)絡(luò)位置注冊到服務(wù)發(fā)現(xiàn)模塊(這里注冊的意思就是告訴),服務(wù)發(fā)現(xiàn)就以K-V的方式記錄下,K一般是服務(wù)名,V就是IP:PORT。服務(wù)發(fā)現(xiàn)模塊定時的輪詢查看這些服務(wù)能不能訪問的了(這就是健康檢查)。客戶端在調(diào)用服務(wù)A-N的時候,就跑去服務(wù)發(fā)現(xiàn)模塊問下它們的網(wǎng)絡(luò)位置,然后再調(diào)用它們的服務(wù)。這樣的方式是不是就可以解決上面的問題了呢?客戶端完全不需要記錄這些服務(wù)的網(wǎng)絡(luò)位置,客戶端和服務(wù)端完全解耦!

常見的服務(wù)發(fā)現(xiàn)框架有:Etcd、Eureka、Consul、Zookeeper

這里我們選擇go-micro默認(rèn)的服務(wù)發(fā)現(xiàn)框架consul來做一個詳細(xì)介紹。

2. consul相關(guān)

consul架構(gòu)圖:

consul-arch.png

Consul集群是有N個SERVER,加上M個CLIENT組成的。 而不管是SERVER還是CLIENT,都是CONSUL的一個節(jié)點,所有的服務(wù)都可以注冊到這些節(jié)點上,正是通過這些節(jié)點實現(xiàn)服務(wù)注冊信息的共享。

CLIENT:client表示consul的client模式,就是客戶端模式。這種模式下,所有注冊到當(dāng)前節(jié)點的服務(wù)會被轉(zhuǎn)發(fā)到SERVER【通過HTTP和DNS接口請求SERVER】,本身是不持久化這些信息,

SERVER: server表示consul的server模式,表明此consul是個server,這種模式下,功能和client都一樣,唯一不同的是,它會把所有的信息持久化到本地。遇到故障,信息是可以被保留并溯源。

SERVER-LEADER: 表明這個server是leader,它和其他server不一樣的一點是,它需要負(fù)責(zé)同步注冊的信息給其他的server,同時也要負(fù)責(zé)各個節(jié)點的健康監(jiān)測

2.1 consul關(guān)鍵特性:

服務(wù)發(fā)現(xiàn):consul通過DNS或者HTTP接口使服務(wù)注冊和服務(wù)發(fā)現(xiàn)變的很容易,一些外部服務(wù),例如saas提供的也可以一樣注冊。

健康檢查:Cousul的client可以提供任意數(shù)量的健康檢查,既可以給定的服務(wù)相關(guān)聯(lián)(webserver是否返回200),也可以與本地節(jié)點相關(guān)聯(lián)(內(nèi)存利用率是都低于90%)。 操作員可以使用這些信息來檢測集群的健康狀況,服務(wù)發(fā)現(xiàn)組件可以使用這些信息將流量從不健康的主路由出去。健康檢測使consul可以快速的告警在集群中的操作。和服務(wù)發(fā)現(xiàn)的集成,可以防止服務(wù)轉(zhuǎn)發(fā)到故障的服務(wù)上面。(心跳機制)

鍵/值存儲:一個用來存儲動態(tài)配置的系統(tǒng)。提供簡單的HTTP接口,可以在任何地方操作。

安全服務(wù)通信:Consul可以為服務(wù)生成和分發(fā)TLS證書,以建立項目的TLS連接。 意圖可用于定義允許哪些服務(wù)通信。 服務(wù)分割可以很容易地進(jìn)行管理,其目的是可以實時更改的,而不是使用復(fù)雜的網(wǎng)絡(luò)拓?fù)浜挽o態(tài)防火墻規(guī)則。

多數(shù)據(jù)中心:無需復(fù)雜的配置,即可支持任意數(shù)量的區(qū)域。

官方建議:最好是三臺或者三臺以上的consul在運行,同名服務(wù)最好是三臺或三臺以上,默認(rèn)可以搭建集群

.2 下載:

https://www.consul.io/downloads.html

2.3 consul簡單使用

首先我們要運行consul,運行有兩種模式,分別是==server==和==client==,通過下面的命令開啟:

consul agent -server  # server運行模式

consul agent  # client 運行模式

每個數(shù)據(jù)中心至少必須擁有一個server。

一個client是一個非常輕量級的進(jìn)程,用于注冊服務(wù),運行健康檢查和轉(zhuǎn)發(fā)對server的查詢.

agent必須在集群中的每個主機上運行.

server模式啟動

$ consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -node=n1 -bind=192.168.6.108 -ui -rejoin -config-dir=/etc/consul.d/ -client 0.0.0.0

需要先在/etc/下面創(chuàng)建consul.d目錄

  • -server : 定義agent運行在server模式

  • -bootstrap-expect :在一個datacenter中期望提供的server節(jié)點數(shù)目,當(dāng)該值提供的時候,<u>consul一直等到達(dá)到指定sever數(shù)目的時候才會引導(dǎo)整個集群,</u>該標(biāo)記不能和bootstrap共用

  • -bind:該地址用來在集群內(nèi)部的通訊,集群內(nèi)的所有節(jié)點到地址都必須是可達(dá)的,默認(rèn)是0.0.0.0

  • -node:節(jié)點在集群中的名稱,在一個集群中必須是唯一的,默認(rèn)是該節(jié)點的主機名

  • -ui: 啟動web界面 :8500

  • -rejoin:使consul忽略先前的離開,在再次啟動后仍舊嘗試加入集群中。

  • -config-dir:配置文件目錄,里面所有以.json結(jié)尾的文件都會被加載(需要先創(chuàng)建/etc/consul.d這個目錄)

  • -client:consul服務(wù)監(jiān)聽地址,這個地址提供HTTP、DNS、RPC等服務(wù),默認(rèn)是127.0.0.1所以不對外提供服務(wù),如果你要對外提供服務(wù)改成0.0.0.0

  • --data-dir:提供一個目錄用來存放agent的狀態(tài),所有的agent允許都需要該目錄,該目錄必須是穩(wěn)定的,系統(tǒng)重啟后都繼續(xù)存在

client模式啟動

consul agent -data-dir /tmp/consul -node=n2 -bind=192.168.137.82 -config-dir /etc/consul.d -rejoin -join 192.168.137.81

運行cosnul agent以client模式,-join 加入到已有的集群中去。

停止Agent

可以使用 Ctrl-C來關(guān)閉Agent,中斷Agent之后你可以看到他離開了集群應(yīng)關(guān)閉。

在退出中,Consul提醒其他集群成員,這個節(jié)點離開了。如果強行殺掉進(jìn)程,集群的其他成員應(yīng)該能檢測到這個節(jié)點失效了。 當(dāng)一個成員離開,他的服務(wù)和檢測也會從目錄中移除。當(dāng)一個成員失效了,他的健康狀況被簡單的標(biāo)記為危險。但是不會從目錄中移除,Consul會自動對失效的節(jié)點進(jìn)行重連。允許他從某些網(wǎng)絡(luò)條件下恢復(fù)過來,離開的節(jié)點則不再繼續(xù)聯(lián)系。

此外,如果一個agent作為一個服務(wù)器,一個優(yōu)雅的離開是很重要的,可以避免潛在的可用性故障,影響達(dá)成一致性協(xié)議。

consul leave # consul優(yōu)雅退出命令

注冊服務(wù)

搭建好consul集群后,用戶或者程序就能到consul中查詢或注冊服務(wù)。可以通過提供服務(wù)定義文件或者調(diào)用HTTP API來注冊一個服務(wù)。

需要注意的是,要先為consul配置創(chuàng)建一個目錄 /etc/consul.d, consul會載入配置文件件里的所有以.json為后綴的配置文件。

mkdir /etc/consul.d

web.json

{
    "service": {
        "ID":"S1", 
        "name": "test",
        "tags": ["001", "002"], // 標(biāo)簽
        "port": 8989,
        "address":"127.0.0.1",
        "checks":[
            {
                "http":"http://127.0.0.1:8989/consul", // 健康檢查的請求
                "timeout":"3s",  //timeout設(shè)置
                "interval":"1s"  // 心跳時間
            }
        ]
    }
}

保存文件并重啟服務(wù)即可。

然后啟動一個簡易的go服務(wù):

package main

import (
    "fmt"
    "net/http"
)

func main(){
    http.HandleFunc("/consul", func(writer http.ResponseWriter, request *http.Request) {
        fmt.Println("來自consul的健康問候...")
    })

    http.ListenAndServe(":8989", nil)
}

會看到不停的打印 來自consul的健康問候... ,consul 進(jìn)行健康檢查,根據(jù)配置的心跳時間,不停的訪問http://127.0.0.1:8989/consul

查詢服務(wù)

  1. 此時可以到ui界面localhost:8500查看服務(wù)的狀態(tài)
  2. 使用命令:dig @127.0.0.1 -p 8600 test.service.consul srv

請思考一個問題: consul中的client模式把請求轉(zhuǎn)向server,那么client的作用是什么?


首先server端的網(wǎng)絡(luò)連接資源有限。對于一個分布式系統(tǒng),一般情況下的訪問量還是很大的。如果用用戶不能通過client直接訪問數(shù)據(jù)中心,那么數(shù)據(jù)中心必然要為每個用戶提供一個單獨的連接資源,那么server端的負(fù)擔(dān)會非常大。隨意很有必要用大量的client端來分散連接請求,在client端先統(tǒng)一整合用戶的服務(wù)請求,然后一次性的通過單一的鏈接發(fā)送大量的的請求給server端,能夠大量減少server端的網(wǎng)絡(luò)負(fù)擔(dān)。 

其次,在client端可以對用戶的請求進(jìn)行一些處理來提高服務(wù)的效率,比如將相同的請求合并成同一個查詢,再比如將之前的查詢通過cookie的形式緩存下來。但是這些功能都需要消耗不少的計算和存儲資源。 如果在server端提供這些功能,必然加重server端的負(fù)擔(dān),使得server端更加不穩(wěn)定。而通過client端來進(jìn)行這些服務(wù)就沒有這些問題了,移位client端不提供實際服務(wù),有充足的計算資源來處理這些工作。

最后一點,consul規(guī)定只要介入一個client就將自己注冊到一個網(wǎng)絡(luò)服務(wù)當(dāng)中。這些結(jié)構(gòu)使得系統(tǒng)的拓展性非常強,網(wǎng)絡(luò)的拓?fù)渥兓梢蕴貏e靈活。這也依賴于client-server結(jié)構(gòu)的。如果系統(tǒng)中只有幾個數(shù)據(jù)中心存在,那網(wǎng)絡(luò)的擴張也無從談起。

參考文檔:http://www.liangxiansen.cn/tags/Consul/

3. consul+grpc

參考文檔:https://www.consul.io/api/agent/check
健康檢查參考文檔:https://www.consul.io/docs/discovery/checks

1. 安裝包

go get -u -v github.com/hashicorp/consul
2. 代碼相關(guān)
2.1 proto文件:

health.pb.proto 參考文檔:https://github.com/grpc/grpc/blob/master/doc/health-checking.md

syntax = "proto3";

package grpc.health.v1;

option go_package="./grpc.health.v1/grpc.health.v1";

message HealthCheckRequest {
  string service = 1;
}

message HealthCheckResponse {
  enum ServingStatus {
    UNKNOWN = 0;
    SERVING = 1;
    NOT_SERVING = 2;
    SERVICE_UNKNOWN = 3;  // Used only by the Watch method.
  }
  ServingStatus status = 1;
}

service Health {
  rpc Check(HealthCheckRequest) returns (HealthCheckResponse);

  rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
}

people.proto

syntax = "proto3";
package pb;

option go_package="./pb;pb";

message people {
  string name = 1;
}

service SayHi {
  rpc sayHi (people) returns (people);
}

通過命令,生成對應(yīng)的.proto.go文件后,開始編寫client.gosever.go

server.go

package main

import (
    "ConsulDemo/grpc.health.v1"
    "ConsulDemo/pb"
    "context"
    "fmt"
    "github.com/hashicorp/consul/api"
    "google.golang.org/grpc"
    "net"
)


type people struct {}

func (c *people) SayHi(ctx context.Context, p *pb.People) (*pb.People, error){
    fmt.Printf("hi %v", p.Name)
    return p, nil
}

// HealthImpl 健康檢查實現(xiàn)
type HealthImpl struct{}

// Check 實現(xiàn)健康檢查接口,這里直接返回健康狀態(tài),這里也可以有更復(fù)雜的健康檢查策略,比如根據(jù)服務(wù)器負(fù)載來返回
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
}

func (*HealthImpl) Watch(*grpc_health_v1.HealthCheckRequest, grpc_health_v1.Health_WatchServer) error {
    return nil
}



func main(){
    //初始化consul配置
    consulConfig := api.DefaultConfig()
    //consulConfig.Address= "localhost:8900"

    //獲取consul操作對象
    registry,err := api.NewClient(consulConfig)
    if err != nil{
        panic(err)
    }

  
    //注冊服務(wù),服務(wù)的常規(guī)配置
    registerService := api.AgentServiceRegistration{
        ID:"1",
        Tags:[]string{"sayHi"},
        Name:"sayHi",
        Port:1234,
        Address:"localhost",
        Check:&api.AgentServiceCheck{
            GRPC:  fmt.Sprintf("%v:%v/%v", "localhost", "1234", "HealthImpl"),
            Timeout:"5s",
            Interval:"5s",
        },
    }


    //注冊服務(wù)到consul上
    err = registry.Agent().ServiceRegister(&registerService)

    if err != nil{
        panic(err)
    }

    //獲取grpc服務(wù)端對象
    grpcServer := grpc.NewServer()
    grpc_health_v1.RegisterHealthServer(grpcServer, &HealthImpl{})


    //注冊grpc服務(wù)
    pb.RegisterSayHiServer(grpcServer,new(people))

    //設(shè)置服務(wù)端監(jiān)聽
    lis,err := net.Listen("tcp",":1234")
    if err != nil {
        panic(err)
    }

    //在指定端口上提供grpc服務(wù)
    grpcServer.Serve(lis)
}

注意:此時需要開啟一個consul agent,consul agent -dev

client.go

package main

import (
    "fmt"
    "github.com/hashicorp/consul/api"
    "google.golang.org/grpc"
    "strconv"
    "ConsulDemo/pb"
    "context"

)

func main(){
    //初始化consul配置, 客戶端服務(wù)器需要一致
    consulConfig := api.DefaultConfig()
  
    //獲取consul操作對象
    registerClient,_ := api.NewClient(consulConfig)
  
    //獲取地址
    serviceEntry,_,_ :=registerClient.Health().Service("sayHi","sayHi",true,nil)

    fmt.Println(fmt.Sprintf("%+v",serviceEntry ))

    //和grpc服務(wù)建立連接
    conn,err := grpc.Dial(serviceEntry[0].Service.Address+":"+strconv.Itoa(serviceEntry[0].Service.Port),grpc.WithInsecure())
    if err != nil {
        panic(err)
    }

    defer conn.Close()

    client := pb.NewSayHiClient(conn)
    reply, err := client.SayHi(context.Background(),&pb.People{Name:"dongxiaojian"})
    if err != nil {
        panic(err)
    }

  fmt.Println("reply:",reply)
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容