1. 程式人生 > 其它 >【四】golang實戰之服務註冊

【四】golang實戰之服務註冊

新增一個微服務例項的時候,微服務就會將自己的 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(&registration)
	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)

為了方便測試,我把密碼登陸中的驗證碼註釋掉了。可以看到成功的連線到了使用者服務並獲取到了資料