【四】golang實戰之服務註冊
阿新 • • 發佈:2022-04-07
新增一個微服務例項的時候,微服務就會將自己的 ip 與 port 傳送到註冊中心,在註冊中心裡面記錄起來。當 API gateway 需要訪問某些微服務的時候,就會去註冊中心取到相應的 ip 與 port,從而實現自動化操作。
技術選型
名稱 | 優點 | 缺點 | 介面 | 一致性演算法 |
---|---|---|---|---|
zookeeper | 1.功能強大,不僅僅只是服務發現 2.提供 watcher 機制能實時獲取服務提供者的狀態 3.dubbo 等框架支援 | 1.沒有健康檢查 2.需在服務中整合 sdk,複雜度高 3.不支援多資料中心 | sdk | Paxos |
consul | 1.簡單易用,不需要整合 sdk 2.自帶健康檢查 3.支援多資料中心 4.提供 web 管理介面 | 1.不能實時獲取服務資訊的變化通知 | http/dns | Raft |
etcd | 1.簡單易用,不需要整合 sdk 2.可配置性強 | 1.沒有健康檢查 2.需配合第三方工具一起完成服務發現 3.不支援多資料中心 | http | Raft |
consul安裝
docker run -d -p 8500:8500 -p 8300:8300 -p 8301:8301 -p 8302:8302 -p 8600:8600/udp consul consul agent -dev -client=0.0.0.0
api文件
實現
user-service註冊
配置檔案增加consul配置
consul: host: xx.xx.xx.xx port: xxxx
定義consul配置結構體
type ConsulConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
}
新增全域性變數和初始化
ConsulConfig *models.ConsulConfig
ConsulConfig = &models.ConsulConfig{}
新增解析配置檔案程式碼
err = vp.UnmarshalKey("consul", &global.ConsulConfig) if err != nil { panic(any(fmt.Sprintf("Read consul config failed:%v", err))) }
實現註冊函式
func RegisterService(address, name, id string, port int, tags []string) error {
cfg := api.DefaultConfig()
cfg.Address = fmt.Sprintf("%s:%d", global.Config.Consul.Host, global.Config.Consul.Port)
client, err := api.NewClient(cfg)
if err != nil {
panic(any(err))
}
check := &api.AgentServiceCheck{
GRPC: fmt.Sprintf("%s:%d", global.Config.Service.Host, global.Config.Service.Port),
GRPCUseTLS: false,
Timeout: "5s",
Interval: "5s",
DeregisterCriticalServiceAfter: "10s",
}
registration := api.AgentServiceRegistration{}
registration.ID = id
registration.Name = name
registration.Port = port
registration.Address = address
registration.Tags = tags
registration.Check = check
err = client.Agent().ServiceRegister(®istration)
if err != nil {
panic(any(err))
}
return nil
}
主函式呼叫註冊函式
err = initializer.RegisterService(
global.Config.Service.Host, global.Config.Service.Name, global.Config.Service.Id,
global.Config.Service.Port, strings.Split(global.Config.Service.Tags, ","),
)
if err != nil {
zap.S().Fatal("RegisterService failed")
}
可以看到此時,健康檢查是失敗的,我們需要註冊健康檢查到grpc中。首先實現健康檢查的介面
type HealthImpl struct{}
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 (h *HealthImpl) Watch(*grpc_health_v1.HealthCheckRequest, grpc_health_v1.Health_WatchServer) error {
return nil
}
再在grpc中進行註冊
grpc_health_v1.RegisterHealthServer(grpcServer, &initializer.HealthImpl{})
可以看到,此時已經好了。
user-api服務發現
在配置檔案增加consul的配置塊
consul:
host: xx.xx.xx.xx
port: xxxx
新增consul配置結構體
type ConsulConfig struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
}
新增全域性變數
ConsulConfig *models.ConsulConfig
ConsulConfig = &models.ConsulConfig{}
增加解析配置檔案
err = vp.UnmarshalKey("consul", &global.ConsulConfig)
if err != nil {
panic(any(fmt.Sprintf("Read consul config faild: %v", err)))
}
實現根據服務名獲取地址資訊的方法
func FilterServiceByName(name string) (host string, port int, err error) {
cfg := api.DefaultConfig()
cfg.Address = fmt.Sprintf("%s:%d", global.ConsulConfig.Host, global.ConsulConfig.Port)
client, err := api.NewClient(cfg)
if err != nil {
panic(any(err))
}
data, err := client.Agent().ServicesWithFilter(fmt.Sprintf(`Service == "%s"`, name))
if err != nil {
panic(any(err))
}
for _, v := range data {
host = v.Address
port = v.Port
return
}
return
}
替換掉註冊user-service的地址資訊
host, port, err := utils.FilterServiceByName(global.UserService.Name)
if err != nil {
zap.S().Fatalw("FilterServiceByName", "name", global.UserService.Name, "err", err)
return
}
userConn := initialize.InitUserConnection(host, port)
為了方便測試,我把密碼登陸中的驗證碼註釋掉了。可以看到成功的連線到了使用者服務並獲取到了資料