一文詳解Golang中consul的基本使用

consul

consul是一個開源服務註冊和服務發現的中心,可以用於微服務的註冊和服務之間的調用的發現,幫助上遊服務找到下遊服務的具體ip:port或者是domain,也可以使用dns的方式讓consul幫你去做轉發,具體介紹請看consul的官網,consul區分server-agent和client-agent,client-agent的作用一般來說就是用來轉發到server-agent的,所以本文隻啟動server-agent,他們的詳細差距可以在google上查到,本文基於golang實現一個服務註冊和服務發現的demo。demo地址

consul的安裝和部署

consul有兩種部署模式,一種是直接在cvm上安裝consul的bin包,然後以server-agent的模式進行啟動,一種是用docker直接啟動鏡像,本文直接使用docker啟動鏡像,將這個鏡像的啟動參數設置為server-agent,在這種模式下,如果要使用服務發現的功能需要區分主機ip和容器ip 不能使用127.0.0.1這種ip去讓server維持go服務的心跳

本文使用的cvm系統為centos8,其他Linux發行版可以自行用包管理工具去安裝一下的前置依賴

docker安裝

curl -fsSL https://get.docker.com | bash -s docker –mirror Aliyun
systemctl start docker

使用上文腳本一鍵安裝docker

consul鏡像的啟動

docker pull consul
docker run -d -p 8500:8500 -v ~/consul:/consul/data -e CONSUL_BIND_INTERFACE='eth0' –name=consul1 consul agent -server -bootstrap -ui -client='0.0.0.0'

兩步啟動一個consul的server-agent,然後就可以通過ip:8500訪問得到consul的一個web界面,如果ip訪問不通可以使用下文的vscode的代理模式去訪問或者是在自己廠商的cvm控制臺去開端口的訪問策略,web界面如下

啟動一個tcp_health_check的服務註冊

創建一個go項目

mkdir consul_demo
go mod init consul_demo
go get -u github.com/hashicorp/consul/api
touch main.go

// main.go
package main

import (
	"bufio"
	"fmt"
	"net"

	consulapi "github.com/hashicorp/consul/api"
)

type DiscoveryConfig struct {
	ID      string
	Name    string
	Tags    []string
	Port    int
	Address string
}

var consulAddress = "127.0.0.1:8500"

func RegisterService(dis DiscoveryConfig) error {
	config := consulapi.DefaultConfig()
	config.Address = consulAddress
	client, err := consulapi.NewClient(config)
	if err != nil {
		fmt.Printf("create consul client : %v\n", err.Error())
	}
	registration := &consulapi.AgentServiceRegistration{
		ID:      dis.ID,
		Name:    dis.Name,
		Port:    dis.Port,
		Tags:    dis.Tags,
		Address: dis.Address,
	}
	// 啟動tcp的健康檢測,註意address不能使用127.0.0.1或者localhost,因為consul-agent在docker容器裡,如果用這個的話,
	// consul會訪問容器裡的port就會出錯,一直檢查不到實例
	check := &consulapi.AgentServiceCheck{}
	check.TCP = fmt.Sprintf("%s:%d", registration.Address, registration.Port)
	check.Timeout = "5s"
	check.Interval = "5s"
	check.DeregisterCriticalServiceAfter = "60s"
	registration.Check = check

	if err := client.Agent().ServiceRegister(registration); err != nil {
		fmt.Printf("register to consul error: %v\n", err.Error())
		return err
	}
	return nil
}

func startTcp() {
	ls, err := net.Listen("tcp", ":10111")
	if err != nil {
		fmt.Printf("start tcp listener error: %v\n", err.Error())
		return
	}
	for {
		conn, err := ls.Accept()
		if err != nil {
			fmt.Printf("connect error: %v\n", err.Error())
		}
		go func(conn net.Conn) {
			_, err := bufio.NewWriter(conn).WriteString("hello consul")
			if err != nil {
				fmt.Printf("write conn error: %v\n", err)
			}
		}(conn)
	}
}
func main() {
	ch := make(chan error)
	dis := DiscoveryConfig{
		ID:      "9527",
		Name:    "main_service",
		Tags:    []string{"a", "b"},
		Port:    10111,
		Address: "192.168.0.124", //通過ifconfig查看本機的eth0的ipv4地址
	}
	go startTcp()
	RegisterService(dis)
	// 阻塞等待
	<-ch
}

然後我們運行這個代碼

go run main.go

就可以看到consul的web界面上多瞭一個服務實例

http版

如果不使用tcp作為健康檢查的方式,可以使用Http_server去實現,邏輯是一樣的,需要給consul返回一個消息,讓consul確認你的心跳即可

check := &consulapi.AgentServiceCheck{}
check.HTTP = fmt.Sprintf("http://%s:%d/", registration.Address, registration.Port)
func startHttp() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Printf("consul get uri: %s\n", r.RequestURI)
		w.Write([]byte("hello consul"))
	})
	if err := http.ListenAndServe(":10111", nil); err != nil {
		fmt.Printf("start http server error: %v\n", err)
	}
}
go startHttp()

服務發現

服務發現其實就是通過http請求向consul請求指定的service下的實例,獲取到他們對應的ip:port和一些其他的元信息,然後在客戶端根據需要篩選得出一個ip:port的實例進行通訊,由於向consul發起http請求的sdk已經在consul官方實現瞭,所以我們不需要自己建一個httpclient去調用這些api,而是直接構建一個struct交給sdk去查詢即可

package main

import (
	"fmt"
	"testing"

	consulapi "github.com/hashicorp/consul/api"
)

func Discovery(serviceName string) []*consulapi.ServiceEntry {
	config := consulapi.DefaultConfig()
	config.Address = "127.0.0.1:8500"
	client, err := consulapi.NewClient(config)
	if err != nil {
		fmt.Printf("consul client error: %v", err)
	}
	service, _, err := client.Health().Service(serviceName, "", false, nil)
	if err != nil {
		fmt.Printf("consul client get serviceIp error: %v", err)
	}
	return service
}

func TestDiscoeryFromConsul(t *testing.T) {
	t.Logf("client discovery start")
	se := Discovery("main_service")
	for i := 0; i < len(se); i++ {
		t.Logf("the instance Node is %+v\n", se[i].Node)
		t.Logf("the isntance Service is %+v\n", se[i].Service)
		t.Logf("\n")
	}
}

ut的結果如下,證明我們通過consul找到瞭下遊服務的ip:port即可發起通訊

[root@hecs-74066 consul_demo]# go test -v main_test.go 
=== RUN   TestDiscoeryFromConsul
    main_test.go:25: client discovery start
    main_test.go:28: the instance Node is &{ID:278ba4f1-0309-fc92-d641-a312b5797779 Node:241f8a20d7fb Address:172.17.0.2 Datacenter:dc1 TaggedAddresses:map[lan:172.17.0.2 lan_ipv4:172.17.0.2 wan:172.17.0.2 wan_ipv4:172.17.0.2] Meta:map[consul-network-segment:] CreateIndex:13 ModifyIndex:16 Partition: PeerName:}
    main_test.go:29: the isntance Service is &{Kind: ID:9527 Service:main_service Tags:[a b] Meta:map[] Port:10111 Address:192.168.0.124 SocketPath: TaggedAddresses:map[lan_ipv4:{Address:192.168.0.124 Port:10111} wan_ipv4:{Address:192.168.0.124 Port:10111}] Weights:{Passing:1 Warning:1} EnableTagOverride:false CreateIndex:43 ModifyIndex:43 ContentHash: Proxy:0xc0000c44d0 Connect:0xc000091a50 PeerName: Namespace: Partition: Datacenter:}
    main_test.go:30: 
— PASS: TestDiscoeryFromConsul (0.00s)

到此這篇關於一文詳解Golang中consul的基本使用的文章就介紹到這瞭,更多相關Golang consul內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: