服务注册和发现——consul

服务注册和发现

什么是服务注册和发现

假如这个产品已经在线上运行,有一天运营想搞一场促销活动,那么我们相对应的【用户服务】可能就要新开启三个微服务实例来支撑这场促销活动。而与此同时,作为苦逼程序员的你就只有手动去API gateway 中添加新增的这三个微服务实例的ipport,一个真正在线的微服务系统可能有成百上千微服务,难道也要一个一个去手动添加吗?有没有让系统自动去实现这些操作的方法呢?答案当然是有的。当我们新添加一个微服务实例的时候,微服务就会将自己的ipport发送到注册中心,在注册中心里面记录起来。当API gateway 需要访问某些微服务的时候,就会去注册中心取到相应的ipport。从而实现自动化操作。

常见的服务发现框架

名称优点缺点接口一致性算法
zookeeper1. 功能强大,不仅仅只是服务发现
2. 提供 watcher 机制能实时获取服务提供者的状态
3. dubbo 等框架支持
1. 没有健康检查
2. 需在服务中集成 sdk,复杂度高
sdkPaxos
consul1. 简单易用,不需要集成 sdk
2. 自带健康检查
3. 支持多数据中心
4. 提供 web 管理界面
1. 不能实时获取服务信息的变化通知http/dnsRaft
etcd1. 简单易用,不需要集成 sdk
2. 可配置性强
1. 没有健康检查
2. 需配合第三方工具一起完成服务发现
3. 不支持多数据中心
httpRaft

consul的安装和配置

consul源码:github-consul

1. 安装

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 容器名字

2. 访问

浏览器访问 127.0.0.1:8500

3. 访问Dns3

linux下的dig命令安装:yum install bind-utils

consul提供dns功能,可以让我们通过dig命令行来测试,consul默认的dns端口是8600, 命令行:

dig @127.0.0.1 -p 8600 consul.service.consul SRV

consul的API接口

1. 添加服务接口

官方文档

此端点向本地代理添加了一项带有可选运行状况检查的新服务。

代理负责管理其本地服务的状态,并将有关其本地服务的更新发送到服务器以保持全局编录同步。

对于“connect-proxy”类型的服务,还需要 Proxy.DestinationServiceName 值的 service:write ACL 来注册服务。

方法小路生产
PUT/agent/service/registerapplication/json

下表显示了此端点对阻止查询、一致性模式、代理缓存和所需 ACL 的支持。

阻止查询一致性模式代理缓存需要 ACL
NOnonenoneservice:write

相应的 CLI 命令是 consul services register

查询参数
  • replace-existing-checks - 请求中缺少的运行状况检查将从代理中删除。使用此参数允许幂等注册服务及其检查,而无需手动取消注册检查。
  • ns (string: "") - 指定您注册的服务的名称空间。您还可以通过其他方法指定命名空间。
JSON 请求主体架构

/agent/service/register 端点支持服务定义键的驼峰式命名法和蛇形命名法。蛇形命名法是一种约定,其中包含两个或多个单词的键之间有下划线。蛇形格式确保与代理配置文件格式的兼容性。

  • Name (string: <required>) - 指定服务的逻辑名称。许多服务实例可能共享相同的逻辑服务名称。我们建议对服务定义名称使用有效的 DNS 标签。有关更多信息,请参阅互联网工程任务组的 RFC 1123。符合标准用法的服务名称可确保与外部 DNS 的兼容性。有关其他信息,请参阅服务配置参考。

  • ID (string: "") - 指定此服务的唯一 ID。每个代理必须是唯一的。如果未提供,则默认为 Name 参数。

  • Tags (array<string>: nil) - 指定分配给服务的标签列表。标签使您能够在查询服务时进行过滤,并在 Consul API 中公开。我们建议对标签使用有效的 DNS 标签。有关更多信息,请参阅互联网工程任务组的 RFC 1123。符合标准用法的标签可确保与外部 DNS 的兼容性。有关其他信息,请参阅服务配置参考。

  • Address (string: "") - 指定服务的地址。如果未提供,则代理的地址将在 DNS 查询期间用作服务的地址。

  • TaggedAddresses (map<string|object>: nil) - 指定服务实例的显式 LAN 和 WAN 地址的映射。地址和端口都可以在映射值中指定。

  • Meta (map<string|string>: nil) - 指定链接到服务实例的任意 KV 元数据。

  • Namespace (string: "") - 指定您注册的服务的名称空间。该字段优先于 ns 查询参数,后者是指定命名空间的其他几种方法之一。

  • Port (int: 0) - 指定服务的端口。

  • Kind (string: "") - 服务类型。默认为 "" ,这是典型的 Consul 服务。您可以指定以下值:

    • "connect-proxy" 用于代表另一个服务的服务网格代理
    • "mesh-gateway" 用于网状网关的实例
    • "terminating-gateway" 用于终止网关的实例
    • "ingress-gateway" 用于入口网关的实例
  • Proxy (Proxy: nil) - 从 1.2.3 开始,指定服务网格代理实例的配置。仅当 Kind 定义代理或网关时这才有效。有关完整详细信息,请参阅服务网格代理配置参考。

  • Connect (Connect: nil) - 指定服务网格的配置。连接子系统提供 Consul 的服务网格功能。有关支持的字段,请参阅下面的“连接结构”部分。

  • Check (Check: nil) - 指定检查。有关接受字段的更多信息,请参阅检查文档。如果您没有提供支票的名称或 ID,则会生成它们。要提供自定义 ID 和/或名称,请设置 CheckID 和/或 Name 字段。

  • Checks (array<Check>: nil) - 指定检查列表。有关接受字段的更多信息,请参阅检查文档。如果您没有提供支票的名称或 ID,则会生成它们。要提供自定义 ID 和/或名称,请设置 CheckID 和/或 Name 字段。自动生成的 NameCheckID 取决于数组中检查的位置,因此即使行为是确定性的,建议所有检查要么让 consul 设置 < b6> 通过将该字段留空/省略或提供唯一值。

  • EnableTagOverride (bool: false) - 指定禁用该服务标签的反熵功能。如果 EnableTagOverride 设置为 true ,则外部代理可以更新目录中的此服务并修改标签。此代理的后续本地同步操作将忽略更新的标签。例如,如果外部代理修改了该服务的标签和端口,并且 EnableTagOverride 设置为 true ,则在下一个同步周期后,该服务的端口将恢复为原始值但标签将保持更新后的值。作为反例,如果外部代理修改了该服务的标签和端口,并且 EnableTagOverride 设置为 false ,则在下一个同步周期后,服务的端口和标签将恢复恢复为原始值,所有修改都将丢失。

  • Weights (Weights: nil) - 指定服务的权重。有关其他信息,请参阅服务配置参考。默认为 {"Passing": 1, "Warning": 1}

    权重仅适用于本地注册的服务。如果多个节点注册同一个服务,则每个节点独立实现 EnableTagOverride 等服务配置项。更新在一个节点上注册的服务的标签不一定会更新在另一节点上注册的同名服务的相同标签。如果未指定 EnableTagOverride ,则默认值为 false 。有关更多信息,请参阅反熵同步。

连接结构

对于 Connect 字段,参数为:

  • Native (bool: false) - 指定此服务是否原生支持 Consul 服务网格协议。如果这是真的,那么服务网格代理、DNS 查询等将能够服务发现该服务。
  • Proxy (Proxy: nil) - 已弃用 指定应为此服务实例启动托管服务网格代理,并可选择提供代理的配置。托管代理(自 Consul v1.3.0 起已弃用)自 v1.6.0 起已被删除。
  • SidecarService (ServiceDefinition: nil) - 指定要注册的可选嵌套服务定义。有关更多信息,请参阅部署 sidecar 服务。
有效负载示例
{
  "ID": "redis1",
  "Name": "redis",
  "Tags": ["primary", "v1"],
  "Address": "127.0.0.1",
  "Port": 8000,
  "Meta": {
    "redis_version": "4.0"
  },
  "EnableTagOverride": false,
  "Check": {
    "DeregisterCriticalServiceAfter": "90m",
    "Args": ["/usr/local/bin/check_redis.py"],
    "Interval": "10s",
    "Timeout": "5s"
  },
  "Weights": {
    "Passing": 10,
    "Warning": 1
  }
}
样品请求
$ curl \
    --request PUT \
    --data @payload.json \
    http://127.0.0.1:8500/v1/agent/service/register?replace-existing-checks=true

2. 删除服务接口

官方文档

该端点从本地代理中删除服务。如果该服务不存在,则不采取任何操作。

代理将负责在目录中注销服务。如果存在关联支票,该支票也会被注销。

方法小路生产
PUT/agent/service/deregister/:service_idapplication/json

下表显示了此端点对阻止查询、一致性模式、代理缓存和所需 ACL 的支持。

阻止查询一致性模式代理缓存需要 ACL
NOnonenoneservice:write

相应的 CLI 命令是 consul services deregister

路径参数
  • service_id (string: <required>) - 指定要取消注册的服务的 ID。
查询参数
  • ns (string: "")- 指定您取消注册的服务的命名空间。您还可以通过其他方法指定命名空间。
样品请求
$ curl \
    --request PUT \
    http://127.0.0.1:8500/v1/agent/service/deregister/my-service-id

3. 设置健康检查

官方文档

此端点向本地代理添加新的检查。检查可以是脚本、HTTP、TCP、UDP 或 TTL 类型。代理负责管理检查状态并保持目录同步。

方法小路生产
PUT/agent/check/registerapplication/json

下表显示了此端点对阻止查询、一致性模式、代理缓存和所需 ACL 的支持。

阻止查询一致性模式代理缓存需要 ACL
NOnonenonenode:write,service:write
查询参数
  • ns (string: "") Enterprise 企业 - 指定您注册的支票的命名空间。您还可以通过其他方法指定命名空间。
JSON 请求主体架构
  • Name (string: <required>) - 指定检查的名称。

  • ID (string: "") - 指定节点上此检查的唯一 ID。默认为 "Name" 参数,但可能需要提供 ID 以确保唯一性。该值将在响应中返回为 "CheckId"

  • Namespace (string: "") Enterprise 企业 - 指定您注册的支票的命名空间。该字段优先于 ns 查询参数,后者是指定命名空间的其他几种方法之一。

  • Interval (string: "") - 指定运行此检查的频率。这是 HTTP、TCP 和 UDP 检查所必需的。

  • Notes (string: "") - 为人类指定任意信息。 Consul 内部不使用它。

  • DeregisterCriticalServiceAfter (string: "") - 指定与服务关联的检查应在此时间后取消注册。这被指定为带有“10m”等后缀的持续时间。如果检查处于关键状态的时间超过此配置值,则其关联服务(及其所有关联检查)将自动取消注册。最短超时时间为 1 分钟,获取关键服务的进程每 30 秒运行一次,因此可能需要比配置的超时时间稍长的时间来触发注销。通常应配置一个比给定服务的任何预期可恢复中断长得多的超时。

  • Args (array<string>) - 指定要运行以更新检查状态的命令参数。在 Consul 1.0 之前,检查使用单个 Script 字段来定义要运行的命令,并且始终在 shell 中运行。在 Consul 1.0 中,添加了 Args 数组,以便可以在没有 shell 的情况下运行检查。 Script 字段已弃用,您应该将 shell 包含在 Args 中以在 shell 下运行,例如。 "args": ["sh", "-c", "..."]

    注意:Consul 1.0 附带了一个问题,即在此 API 中 Args 被错误地命名为 ScriptArgs 。请在 Consul 1.0 中使用 ScriptArgs (Consul 的未来版本将继续接受),并在 Consul 1.0.1 及更高版本中使用 Args

  • AliasNode (string: "") - 指定用于别名检查的节点 ID。如果未指定服务,则检查将别名化节点的运行状况。如果指定了服务,则检查将在该特定节点上为指定服务设置别名。

  • AliasService (string: "") - 指定用于别名检查的服务的 ID。如果服务未向同一代理注册,则还必须指定 AliasNode 。请注意,这是服务 ID,而不是服务名称(尽管它们通常是相同的)。

  • DockerContainerID (string: "") - 指定检查是 Docker 检查,Consul 将使用指定的 Shell

  • GRPC (string: "") - 指定支持标准 gRPC 运行状况检查协议的 gRPC 检查端点。检查的状态将通过探测配置的端点在给定的 Interval 处更新。按以下格式在 gRPC 检查端点后添加服务标识符,以检查特定服务而不是整个 gRPC 服务器 /:service_identifier

  • GRPCUseTLS (bool: false) - 指定是否使用 TLS 进行此 gRPC 运行状况检查。如果启用 TLS,则默认情况下需要有效的 TLS 证书。可以通过将 TLSSkipVerify 设置为 true 来关闭证书验证。

  • H2PING (string "") - 指定使用 http2 运行 ping 检查的地址。在指定的 Interval 处,与该地址建立连接,并发送 ping。如果 ping 成功,则检查将被分类为 passing ,否则将被标记为 critical 。默认情况下使用 TLS。要禁用 TLS 并使用 h2c,请将 H2PingUseTLS 设置为 false 。如果启用 TLS,则默认需要有效的 SSL 证书,但可以使用 TLSSkipVerify 删除验证。

  • H2PingUseTLS (bool: true) - 指定是否应使用 TLS 进行 H2PING 检查。如果启用 TLS,则默认需要有效的 SSL 证书,但可以使用 TLSSkipVerify 删除验证。

  • HTTP (string: "") - 指定 HTTP 检查以针对 HTTP 的值执行 GET 请求(预计是一个 URL)每个 Interval 。如果响应是任何 2xx 代码,则检查为 passing 。如果响应为 429 Too Many Requests ,则检查为 warning 。否则,检查为 critical 。 HTTP 检查还支持 SSL。默认情况下,需要有效的 SSL 证书。可以使用 TLSSkipVerify 控制证书验证。

  • Method (string: "") - 指定用于 HTTP 检查的不同 HTTP 方法。如果未指定值,则使用 GET

  • Body (string: "") - 指定应与 HTTP 检查一起发送的正文。

  • DisableRedirects (bool: false) - 指定执行 HTTP 检查时是否禁用以下 HTTP 重定向。

  • Header (map[string][]string: {}) - 指定应为 HTTP 检查设置的一组标头。每个标头可以有多个值。

  • Timeout (duration: 10s) - 指定在脚本、HTTP、TCP、UDP 或 gRPC 检查情况下传出连接的超时。可以以“10s”或“5m”的形式指定(分别为10秒或5分钟)。

  • OutputMaxSize (positive int: 4096) - 允许为给定检查放置最大文本大小。该值必须大于0,默认为4k。可以使用代理中的 check_output_max_size 标志进一步限制给定代理的所有检查的值。

  • TLSServerName (string: "") - 指定一个可选字符串,用于在通过 TLS 连接时设置 SNI 主机。对于 HTTP 检查,如果 URL 使用主机名(而不是 IP 地址),则会自动设置此值。

  • TLSSkipVerify (bool: false) - 指定是否不应验证 HTTPS 检查的证书。

  • TCP (string: "") - 指定一个 TCP 来根据 TCP 的值(预计是 IP 或主机名加端口组合)进行连接 Interval 。如果连接尝试成功,则检查为 passing 。如果连接尝试不成功,则检查 critical 。如果主机名同时解析为 IPv4 和 IPv6 地址,则会尝试同时访问这两个地址,并且第一次成功的连接尝试将导致检查成功。

  • TCPUseTLS (bool: false) - 指定是否使用 TLS 进行此 TCP 运行状况检查。如果启用 TLS,则默认情况下需要有效的 TLS 证书。可以通过将 TLSSkipVerify 设置为 true 来关闭证书验证。

  • UDP (string: "") - 指定 UDP IP 地址/主机名和端口。该检查按照 Interval 配置中指定的时间间隔将数据报发送到指定的值。如果数据报发送成功或返回超时,则检查设置为 passing 状态。如果数据报发送失败,则检查记录为 critical

  • OSService (string: "") - 指定要检查的操作系统级服务的标识符。您可以在 Windows 上指定 Windows Services 或在 Unix 上指定 SystemD 服务。

  • TTL (duration: 10s) - 指定这是 TTL 检查,并且必须定期使用 TTL 端点来更新检查的状态。如果在指定的时间内检查未设置为通过,则检查将设置为失败状态。

  • ServiceID (string: "") - 指定将已注册支票与代理提供的现有服务关联起来的服务 ID。

  • Status (string: "") - 指定运行状况检查的初始状态。

  • SuccessBeforePassing (int: 0) - 指定检查状态转换为通过之前所需的连续成功结果数。可用于 HTTP、TCP、gRPC、Docker 和 Monitor 检查。 Consul 1.7.0 中添加。

  • FailuresBeforeWarning (int: 0) - 指定检查状态转换为警告之前所需的连续不成功结果的数量。默认值与 FailuresBeforeCritical 相同。高于 FailuresBeforeCritical 的值无效。可用于 HTTP、TCP、gRPC、Docker 和 Monitor 检查。 Consul 1.11.0 中添加。

  • FailuresBeforeCritical (int: 0) - 指定检查状态转换为严重之前所需的连续不成功结果的数量。可用于 HTTP、TCP、gRPC、Docker 和 Monitor 检查。 Consul 1.7.0 中添加。

有效负载示例
{
  "ID": "mem",
  "Name": "Memory utilization",
  "Namespace": "default",
  "Notes": "Ensure we don't oversubscribe memory",
  "DeregisterCriticalServiceAfter": "90m",
  "Args": ["/usr/local/bin/check_mem.py"],
  "DockerContainerID": "f972c95ebf0e",
  "Shell": "/bin/bash",
  "HTTP": "https://example.com",
  "Method": "POST",
  "Header": { "Content-Type": ["application/json"] },
  "Body": "{\"check\":\"mem\"}",
  "DisableRedirects": true,
  "TCP": "example.com:22",
  "Interval": "10s",
  "Timeout": "5s",
  "TLSSkipVerify": true
}
样品请求
$ curl \
   --request PUT \
   --data @payload.json \
   http://127.0.0.1:8500/v1/agent/check/register

4. 删除健康检查

该端点从本地代理中删除检查。代理人将负责从目录中注销支票。如果所提供的 ID 的检查不存在,则不采取任何操作。

方法小路生产
PUT/agent/check/deregister/:check_idapplication/json

下表显示了此端点对阻止查询、一致性模式、代理缓存和所需 ACL 的支持。

阻止查询一致性模式代理缓存需要 ACL
NOnonenonenode:write,service:write
路径参数
  • check_id (string: "") - 指定要取消注册的检查的唯一 ID。
查询参数
  • ns (string: "") Enterprise 企业 - 指定您取消注册的支票的命名空间。您还可以通过其他方法指定命名空间。
样品请求
$ curl \
    --request PUT \
    http://127.0.0.1:8500/v1/agent/check/deregister/my-check-id

5. go实现添加服务,设置健康检查,显示全部服务和过滤服务

0.目录树如下
.
├── gin_server
│   └── main.go
└── main.go
1. 编写gin服务
package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
	r.GET("/health", getIndex)
	r.Run("0.0.0.0:8200")
}

func getIndex(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{"msg": "success"})
}
2.编写consul服务
package main

import (
	"fmt"
	"github.com/hashicorp/consul/api"
)

func Register(address string, port int, name string, tags []string, id string) error {
	cfg := api.DefaultConfig()
	cfg.Address = "192.168.1.4:8500"

	client, err := api.NewClient(cfg)
	if err != nil {
		panic(err)
	}

	// 生成新的检查对象(第1种赋值)
	check := &api.AgentServiceCheck{
		HTTP:                           "http://192.168.1.4:8200/health",
		Timeout:                        "5s",
		Interval:                       "5s",
		DeregisterCriticalServiceAfter: "10s",
	}

	// 生成注册对象(第2种赋值)
	registration := new(api.AgentServiceRegistration)
	registration.Name = name
	registration.ID = id
	registration.Port = port
	registration.Tags = tags
	registration.Address = address
	registration.Check = check

	if err = client.Agent().ServiceRegister(registration); err != nil {
		panic(err)
	}

	return nil

}

// 全部服务
func AllServices() {
	cfg := api.DefaultConfig()
	cfg.Address = "192.168.1.4:8500"

	client, err := api.NewClient(cfg)
	if err != nil {
		panic(err)
	}

	data, err := client.Agent().Services()
	if err != nil {
		panic(err)
	}
	for k, _ := range data {
		fmt.Println(k)
	}
}

// 筛选服务
func FilterServices() {
	cfg := api.DefaultConfig()
	cfg.Address = "192.168.1.4:8500"

	client, err := api.NewClient(cfg)
	if err != nil {
		panic(err)
	}
	//
	data, err := client.Agent().ServicesWithFilter(`Service == "try-consul"`)
	if err != nil {
		panic(err)
	}
	for k, _ := range data {
		fmt.Println(k)
	}
}

func main() {
	go Register("192.168.1.4", 8200, "try-consul", []string{"try", "consul"}, "try-consul")

	go Register("192.168.1.4", 8200, "try", []string{"try", "consul"}, "try")

	go Register("192.168.1.4", 8200, "consul-try", []string{"try", "consul"}, "consul-try")

	AllServices()
	FilterServices()
}

3. consul服务效果显示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

6. grpc实现健康检查

官方文档

健康检查用于探测服务器是否能够处理 RPC。客户端到服务器的健康检查可以点对点或通过某些控制系统进行。服务器可能会选择回复“不健康”,因为它尚未准备好接受请求、正在关闭或其他原因。如果在某个时间窗口内没有收到响应或者响应表明响应不健康,客户端可以采取相应的行动。

GRPC 服务用作简单客户端到服务器场景和其他控制系统(例如负载平衡)的健康检查机制。作为高水平的服务提供了一些好处。首先,由于它本身是一个GRPC服务,因此进行健康检查的格式与普通rpc相同。其次,它具有丰富的语义,例如每个服务的健康状态。第三,作为GRPC服务,它能够重用所有现有的计费、配额基础设施等,因此服务器可以完全控制健康检查服务的访问。

Service Definition 服务定义

服务器应导出以下原型中定义的服务:

syntax = "proto3";

package grpc.health.v1;

message HealthCheckRequest {
  string service = 1;
}

message HealthCheckResponse {
  enum ServingStatus {
    UNKNOWN = 0;
    SERVING = 1;
    NOT_SERVING = 2;
    SERVICE_UNKNOWN = 3;  // Used only by the Watch method.
  }
  ServingStatus status = 1;
}

service Health {
  rpc Check(HealthCheckRequest) returns (HealthCheckResponse);

  rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
}

客户端可以通过调用 Check 方法查询服务器的健康状态,并且需要在rpc上设置一个截止时间。客户端可以选择设置想要查询健康状态的服务名称。

服务器应手动注册所有服务并设置各个状态,包括空服务名称及其状态。对于收到的每个请求,如果可以在注册表中找到服务名称,则必须发送回带有 OK 状态的响应,并且状态字段应设置为 SERVINGNOT_SERVING 相应地。如果服务名称未注册,服务器将返回 NOT_FOUND GRPC 状态。

服务器应该使用一个空字符串作为服务器整体健康状态的键,以便对特定服务不感兴趣的客户端可以使用空请求查询服务器的状态。服务器只能对服务名称进行精确匹配,而不支持任何类型的通配符匹配。然而,服务所有者可以自由地实现客户端和服务器都同意的更复杂的匹配语义。

如果 rpc 在一段时间后没有完成,客户端可以声明服务器不健康。客户端应该能够处理服务器没有 Health 服务的情况。

客户端可以调用 Watch 方法来执行流健康检查。服务器将立即发回一条消息,指示当前的服务状态。每当服务的服务状态发生变化时,它都会发送一条新消息。

0. 目录树如下
.
├── main.go
└── proto
    ├── user_grpc.pb.go
    ├── user.pb.go
    └── user.proto

1. 编写proto代码
syntax="proto3";
option go_package=".;proto";

service Users {
  rpc AddUser(AddUserReq) returns (AddUserRes);
  rpc GetUser(GetUserReq) returns (GetUserRes);
}

message UsersModel {
  string name = 1;
  int32 age = 2;
}

message AddUserReq {
  UsersModel user = 1;
}

message AddUserRes {
  string message = 1;
  bool success = 2;
}

message GetUserReq {
}

message GetUserRes {
  repeated UsersModel userList =1 ;
}

利用命令生成go文件

protoc user.proto --go_out=. --go-grpc_out=.
2. 编写main代码
package main

import (
	"context"
	"fmt"
	"net"
	"strconv"

	"github.com/hashicorp/consul/api"
	users "go-try/consul/grpc_consul/proto"
	"google.golang.org/grpc"
	"google.golang.org/grpc/health"
	"google.golang.org/grpc/health/grpc_health_v1"
)

// 定义通用度变量, 这里以后可以做成配置,仅做简单举例
var (
	host    string = "192.168.1.4"
	port    int    = 8081
	portStr string = strconv.Itoa(port)
	address        = host + ":" + portStr
)

type Users struct {
	users.UnimplementedUsersServer
}

func (g Users) AddUser(c context.Context, req *users.AddUserReq) (*users.AddUserRes, error) {
	fmt.Println(req)
	return &users.AddUserRes{
		Success: true,
		Message: "增加用户成功",
	}, nil
}

func (g Users) GetUser(c context.Context, req *users.GetUserReq) (*users.GetUserRes, error) {
	var tempList []*users.UsersModel
	for i := 0; i < 10; i++ {
		tempList = append(tempList, &users.UsersModel{
			Name: "商品" + strconv.Itoa(i),
			Age:  int32(i),
		})
	}
	return &users.GetUserRes{
		UserList: tempList,
	}, nil
}

func main() {
	// ----------------------- 1. 注册consul服务 -----------------------
	// 1、初始化consul配置
	consulConfig := api.DefaultConfig()
	consulConfig.Address = "127.0.0.1:8500" // consul服务的默认地址可省略,如果不是本机,需要补充

	// 2、获取consul操作对象
	consulClient, _ := api.NewClient(consulConfig)

	// 3、配置注册服务的参数
	agentService := api.AgentServiceRegistration{
		ID:      "uuu-1", // 不要重复
		Tags:    []string{"test"},
		Name:    "Users-Service",
		Port:    port, // 和下面 grpc server 的配置相同, 否则就连接不上微服务
		Address: host, // 同上
		Check: &api.AgentServiceCheck{
			GRPC:                           address,
			Timeout:                        "3s",
			Interval:                       "1s",
			DeregisterCriticalServiceAfter: "5s",
		},
	}
	//  4、注册服务到consul上
	consulClient.Agent().ServiceRegister(&agentService)
	// ----------------------- 2. 注册GRPC -----------------------
	// 1、获取Grpc示例
	grpcServer := grpc.NewServer()
	// 2、注册服务
	users.RegisterUsersServer(grpcServer, &Users{})
	// 3. 健康检查
	grpc_health_v1.RegisterHealthServer(grpcServer, health.NewServer())
	// 4、监听端口
	listener, err := net.Listen("tcp", address)
	if err != nil {
		fmt.Println(err)
	}
	// 5、退出服务的时候关闭监听
	// 6、启动服务
	grpcServer.Serve(listener)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值