GO微服务开发中consul注册中心使用详解

介绍

Consul 是一种用于服务发现、配置管理和健康检查的分布式系统

当谈论分布式系统时,我们可以通过一个简单的例子来更好地理解。假设你正在构建一个在线购物网站,其中涉及多个服务和组件协同工作来提供用户购物的功能。这个网站可以被视为一个分布式系统。

在这个例子中,我们可以看到以下几个方面:

  1. 多个节点: 你的网站由多个独立的节点组成,比如 Web 服务器、数据库服务器、支付服务等。每个节点可能运行在不同的计算机上,彼此之间通过网络通信。

  2. 协调与通信: 不同的节点需要协同工作来实现整个购物过程。当用户访问网站时,Web 服务器可能需要与数据库服务器进行通信,检索商品信息。然后,当用户进行支付时,支付服务需要与银行进行通信。这些节点之间的通信和协调是分布式系统的核心。

  3. 可靠性和容错性: 假设某个节点出现故障,比如数据库服务器崩溃了。在一个分布式系统中,其他节点仍然可以继续工作,确保用户购物功能不会完全中断。系统可以使用备份数据库或分布式数据库来提高可靠性。

  4. 负载均衡: 假设你有多个 Web 服务器来处理用户请求。通过负载均衡,用户的请求会被均匀地分发到不同的服务器上,以避免某个服务器过载。这可以提高系统的性能和可扩展性。

  5. 数据一致性: 在购物过程中,用户可能在不同的节点间切换,比如从商品浏览切换到支付页面。这时,不同节点之间的数据要保持一致,以确保用户看到正确的商品信息和订单状态。分布式系统需要实现数据同步和一致性,以避免出现错误。

回到 Consul 的例子,假设你在上述购物网站中使用 Consul 来进行服务发现和健康检查。每个服务(Web 服务器、支付服务等)都会注册到 Consul 中,表示它们可用。客户端可以通过 Consul 查询可用的服务,确保用户请求被正确路由到健康的服务。Consul 的不同节点之间通过协议保持数据的一致性,以确保服务信息的准确性。

总之,分布式系统是由多个节点组成,通过协同工作来实现特定功能。在现实世界中,像在线购物网站这样的应用程序,以及用于服务发现和协调的工具如 Consul,都涉及分布式系统的概念和原则。

安装部署

执行下面的命令,从官方 github 仓库中获取的配置和资源。

git clone https://github.com/hashicorp/learn-consul-docker.git

这个仓库中有很多定义好的各式 consul 环境 docker-compose 配置,我们这里使用的是服务发现的环境,进入到 datacenter-deploy-service-discovery 目录。

cd datacenter-deploy-service-discovery

执行以下命令,启动容器。

❯ docker-compose up -d
Creating network "datacenter-deploy-service-discovery_consul" with driver "bridge"
Creating consul-server ... done
Creating consul-client ... done

这样一个用于服务发现的 consul 环境就搭建好了。

datacenter-deploy-secure 目录中提供了一个由3个server和1个client组成的集群环境。

使用浏览器打开 http:127.0.0.1:8500 就可以看到Consul的管理界面了。


代码部分

实现consul的初始化,面向接口开发,这样当你在中途打算更改注册中心(例如改用etcd)可以避免修改大量代码,只要定义的结构体实现接口定义的方法就可以直接用。

// Register 自定义一个注册中心的抽象(此示例不够严谨,仍然使用了consul/api库,仅做教学使用)
type Register interface {
	// 注册
	RegisterService(serviceName string, ip string, port int, tags []string) error
	// 服务发现
	ListService(serviceName string) (map[string]*api.AgentService, error)
	// 注销
	Deregister(serviceID string) error
}
type consul struct {
	client *api.Client
}

var Reg Register

// 确保某个结构体实现了对应的接口
var _ Register = (*consul)(nil)

// Init 连接至consul服务,初始化全局的consul对象,这段代码的目的是在 Go 语言中连接到 Consul 服务并初始化一个全局的 Consul 对象,以便后续可以使用该对象来进行服务发现、健康检查等操作。整体来说,它涵盖了配置对象的创建、客户端的初始化以及错误处理等方面的内容。
func Init(addr string) (err error) {
	//api.DefaultConfig返回一个默认的 Consul 配置对象
	cfg := api.DefaultConfig()
	//这一行代码将之前创建的配置对象的 Address 字段设置为传入的 addr 参数的值。Address 字段表示要连接的 Consul 服务的地址,通过这个步骤,我们告诉配置对象要连接的具体地址。
	cfg.Address = addr
	//在这一行代码中,使用之前配置好的配置对象 cfg 来创建一个新的 Consul 客户端对象 c。api.NewClient() 函数接受一个配置对象作为参数,并返回一个连接到 Consul 的客户端对象。
	//一个连接到 Consul 的客户端对象在应用程序中扮演着与 Consul 交互的角色。Consul 是一种用于服务发现、配置管理和健康检查的分布式系统,连接到 Consul 的客户端对象允许你的应用程序与 Consul 服务进行通信
	c, err := api.NewClient(cfg)
	if err != nil {
		return err
	}
	Reg = &consul{c}
	return
}

实现服务注册

// RegisterService 将gRPC服务注册到consul
func (c *consul) RegisterService(serviceName string, ip string, port int, tags []string) error {
	// 健康检查,Timeout 表示检查超时时间,Interval 表示检查的间隔时间,DeregisterCriticalServiceAfter 表示如果服务在指定时间内没有通过健康检查,将会从 Consul 注销。
	check := &api.AgentServiceCheck{
		GRPC:                           fmt.Sprintf("%s:%d", ip, port), // 这里一定是外部可以访问的地址
		Timeout:                        "10s",
		Interval:                       "10s",
		DeregisterCriticalServiceAfter: "20s",
	}
	/*
		在这一段代码中,一个服务注册对象 srv 被创建。这个对象包含了要注册到 Consul 的服务的详细信息。
		ID 字段用于标识唯一的服务,根据服务名、IP 地址和端口号生成。Name 字段设置为服务的名称,
		Tags 字段是一个字符串切片,可以为服务添加标签,例如标识服务的用途或特性。Address 和 Port 字段分别设置为
		服务的 IP 地址和端口号。Check 字段使用之前创建的健康检查对象。
	*/
	srv := &api.AgentServiceRegistration{
		ID:      fmt.Sprintf("%s-%s-%d", serviceName, ip, port), // 服务唯一ID
		Name:    serviceName,                                    // 服务名称
		Tags:    tags,                                           // 为服务打标签
		Address: ip,
		Port:    port,
		Check:   check,
	}
	//调用 Consul 客户端的 Agent().ServiceRegister() 方法来将之前创建的服务注册对象 srv 注册到 Consul。
	return c.client.Agent().ServiceRegister(srv)
}

实现服务发现

// ListService 服务发现
func (c *consul) ListService(serviceName string) (map[string]*api.AgentService, error) {
	return c.client.Agent().ServicesWithFilter(fmt.Sprintf("Service==`%s`", serviceName))
}

它接收一个参数 serviceName,表示要发现的服务的名称。这个方法的返回值是一个映射(map),其中键是服务的ID,值是指向 api.AgentService 对象的指针。这个对象包含了有关服务的详细信息,比如服务名称、标签、地址、端口等。在方法内部,首先使用 fmt.Sprintf 格式化字符串,将传入的服务名称嵌入到字符串中,以生成一个用于过滤的表达式。然后,通过调用 Consul 客户端的 Agent().ServicesWithFilter() 方法,以过滤的方式获取服务信息。这样做可以只返回指定服务名称的相关信息。

实现注销服务

// Deregister 注销服务
func (c *consul) Deregister(serviceID string) error {
	return c.client.Agent().ServiceDeregister(serviceID)
}

完成一个服务要注册时,在main函数调用init方法初始化consul,再调用RegisterService方法传入所需参数即可 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

idMiFeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值