什么是consul?
Consul是一个由HashiCorp公司推出的开源工具,主要用于实现分布式系统的服务发现与配置。它基于CP(一致性和分区容错性)模型,是一个轻量级且高度可用的系统,提供了丰富的功能。
docker 安装
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
docker container update --restart=always 容器名字
访问
浏览器访问 127.0.0.1:8500
// BaseConsul 函数用于创建并返回一个配置好的Consul客户端。
// 它首先获取全局配置中的Consul配置信息,然后设置Consul客户端的默认配置,
// 包括Consul服务器的地址(由主机名和端口号组成),最后创建并返回Consul客户端实例。
// 如果在创建客户端过程中出现错误,则使用zap日志库记录并触发panic。
func BaseConsul() *api.Client {
c := global.ServerConfig.ConsulConfig // 从全局配置中获取Consul配置
config := api.DefaultConfig() // 获取Consul客户端的默认配置
config.Address = fmt.Sprintf("%s:%d", c.Host, c.Port) // 设置Consul服务器的地址
client, err := api.NewClient(config) // 根据配置创建Consul客户端
if err != nil {
zap.S().Panic() // 如果创建失败,则记录panic级别的日志
}
return client // 返回创建好的Consul客户端实例
}
// InitConsul 函数用于初始化Consul服务注册。
// 它首先获取全局配置中的用户配置和Consul配置,然后调用BaseConsul函数获取Consul客户端实例。
// 接着,它构建一个服务注册请求,包括服务的唯一ID、名称、标签、端口、地址以及健康检查配置。
// 最后,它尝试将服务注册到Consul中,并在注册成功时记录日志,如果注册失败则记录panic级别的日志。
func InitConsul() {
u := global.ServerConfig.UserConfig // 获取用户配置
c := global.ServerConfig.ConsulConfig // 获取Consul配置
client := BaseConsul() // 调用BaseConsul函数获取Consul客户端实例
// 创建一个服务健康检查配置
// 这里GRPC字段实际上可能是误用,因为Consul的AgentServiceCheck通常不包含GRPC字段,
// 除非这是通过某种方式扩展的。这里可能是想表达某种健康检查的方式或URL,但标准Consul API不支持GRPC字段。
check := &api.AgentServiceCheck{
Interval: "2s", // 健康检查间隔时间
Timeout: "2s", // 健康检查超时时间
GRPC: fmt.Sprintf("%s:%d", u.Host, u.Port), // 在srv中写入
DeregisterCriticalServiceAfter: "1s", // 服务在连续失败多少次健康检查后自动注销
HTTP: fmt.Sprintf("http://%s:%d/health", u.Host, u.Port), //api中写入 grpc和http不能一块写入
}
// 构建服务注册请求
registration := api.AgentServiceRegistration{
ID: global.ServerId, // 服务的唯一ID
Name: u.Name, // 服务的名称
Tags: c.Tags, // 服务的标签列表
Port: u.Port, // 服务的端口号
Address: u.Host, // 服务的地址(通常是IP地址或主机名)
Check: check, // 服务的健康检查配置
}
// 尝试将服务注册到Consul中
err := client.Agent().ServiceRegister(®istration)
if err != nil {
zap.S().Panic(err) // 如果注册失败,则记录panic级别的日志
}
zap.S().Info("consul连接成功") // 如果注册成功,则记录info级别的日志,但这里的信息可能有些误导,应为"服务注册成功"
}
自研框架grpc 优雅的退出 释放端口号
import (
"fmt"
"github.com/hashicorp/consul/api"
DbInv "github.com/jiao-zixuan/shop_order/inventory"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/health"
"google.golang.org/grpc/health/grpc_health_v1"
"inventory_srv/global"
"inventory_srv/handler"
"net"
"os"
"os/signal"
"syscall"
)
// InitServer 初始化gRPC服务器,注册服务处理程序,并设置优雅退出逻辑
func InitServer() {
// 创建一个新的gRPC服务器实例
g := grpc.NewServer()
// 创建一个Server类型的实例(注意这里假设controller.Server已经定义并实现了相应的gRPC服务接口)
var s handler.Server
// 将Server实例注册到gRPC服务器中,以便处理对应的RPC调用
DbInv.RegisterInventoryServer(g, &s)
// 监听TCP端口,准备接受gRPC连接
listen, err := net.Listen("tcp", fmt.Sprintf("%s:%d", global.ServerConfig.InventorySrvConfig.Host, global.ServerConfig.InventorySrvConfig.Port))
if err != nil {
// 如果监听失败,则记录错误并触发panic
zap.S().Panic(err.Error())
}
// 注册服务健康状态检查到gRPC服务器中
// grpc_health_v1.RegisterHealthServer是gRPC健康检查服务的标准注册方式
grpc_health_v1.RegisterHealthServer(g, health.NewServer()) // 假设health.NewServer()返回了一个实现了grpc_health_v1.HealthServer接口的对象
// 在一个新的goroutine中启动gRPC服务器,以非阻塞方式运行
go func() {
err = g.Serve(listen)
if err != nil {
// 如果服务器启动或运行过程中发生错误,则记录错误并触发panic
zap.S().Panic(err.Error())
}
}()
// 设置优雅退出逻辑
// 创建一个通道,用于接收操作系统信号(如SIGINT, SIGTERM)
quit := make(chan os.Signal)
// 通知quit通道接收SIGINT和SIGTERM信号
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
// 阻塞等待接收到退出信号
<-quit
// 接收到退出信号后,开始执行注销服务的逻辑
// 配置Consul客户端的默认设置,并设置地址
config := api.DefaultConfig()
config.Address = fmt.Sprintf("%s:%d", global.ServerConfig.ConsulConfig.Host, global.ServerConfig.ConsulConfig.Port)
// 实例化Consul客户端
client, err := api.NewClient(config)
if err != nil {
// 如果实例化Consul客户端失败,则记录错误并触发panic
zap.S().Panic(err.Error())
}
// 从Consul中注销服务
err = client.Agent().ServiceDeregister(global.ServerId)
if err != nil {
// 如果注销服务失败,则记录错误并触发panic
zap.S().Info("服务注销失败")
}
zap.S().Info("服务注销成功")
// 注意:这里的"服务注册成功"日志可能有些误导,因为实际上是在执行注销操作。如果要在服务启动时打印注册成功的信息,应该在注册服务的代码之后添加。
// zap.S().Info("服务注册成功") // 这行日志应该放在服务注册代码之后,并且可能需要改为"服务即将注销"或类似的表述
// 实际上,由于此函数的主要目的是启动服务并设置退出逻辑,通常不会在退出逻辑执行完毕后打印"服务注册成功"的日志。
// 如果确实需要记录服务退出的信息,可以在退出逻辑执行完毕后添加相应的日志。
}