这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天
1. 什么是服务注册和发现
假如有个产品已经在线上运行,有一天运营想搞一场促销活动,那么我们相对应的用户服务可能就要新开启三个微服务实例来支撑这场促销活动。而与此同时,作为苦逼程序员的你就只有手动去 API gateway 中添加新增的这三个微服务实例的 ip 与port ,一个真正在线的微服务系统可能有成百上千微服务,难道也要一个一个去手动添加吗?有没有让系统自动去实现这些操作的方法呢?答案当然是有的。
当我们新添加一个微服务实例的时候,微服务就会将自己的 ip 与 port 发送到注册中心,在注册中心里面记录起来。当 API gateway 需要访问某些微服务的时候,就会去注册中心取到相应的 ip 与 port。从而实现自动化操作。
2. consul的安装和配置
docker run -d -p 8500:8500 -p 8300:8300 -p 8302:8302 -p 8600:8600/udp consul consul agent -dev -client=0.0.0.0
浏览器访问
8500:http端口
8600:dns端口
访问dns
consul提供dns功能,可以让我们通过, 可以通过dig命令行来测试,consul默认的dns端口是8600, 命令行:
linux下的dig命令安装:
yum install bind-utils
yum install bind-utils
consul也可以作为DNS服务器。也就是通过8600端口,来返回IP和端口号。
dig @192.168.1.103 -p 8600 consul.service.consul SRV
4. consul的api接口
- 添加服务developer.hashicorp.com/consul/api-…
- 设置健康检查
developer.hashicorp.com/consul/api-…
1. 添加服务
developer.hashicorp.com/consul/api-…
2. 删除服务
3. 设置健康检查
developer.hashicorp.com/consul/api-…
4. 获取服务
5. go操作consul
go通过api操作consul
go中的服务注册与发现的代码:
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()
// consul的远程地址
cfg.Address = "192.168.1.103:8500"
client, err := api.NewClient(cfg)
if err != nil {
panic(err)
}
// 生成对应的检查对象
check := &api.AgentServiceCheck{
HTTP: "http://192.168.1.102:8021/health",
Timeout: "5s",
Interval: "5s",
DeregisterCriticalServiceAfter: "10s",
}
//生成注册对象
registration := new(api.AgentServiceRegistration)
registration.Name = name
// 如果要启动多个同样name的服务,ID不可以一样
registration.ID = id
registration.Port = port
registration.Tags = tags
registration.Address = address
registration.Check = check
err = client.Agent().ServiceRegister(registration)
if err != nil {
panic(err)
}
return nil
}
// 服务发现
func AllServices(){
cfg := api.DefaultConfig()
cfg.Address = "192.168.1.103:8500"
client, err := api.NewClient(cfg)
if err != nil {
panic(err)
}
data, err := client.Agent().Services()
if err != nil {
panic(err)
}
for key, _ := range data{
fmt.Println(key)
}
}
// 通过服务名来发现服务
// 这种方式如果客户端是全局变量的话则如果底层微服务变更端口了不会自动更新
func FilterSerivice(){
cfg := api.DefaultConfig()
cfg.Address = "192.168.1.103:8500"
client, err := api.NewClient(cfg)
if err != nil {
panic(err)
}
data, err := client.Agent().ServicesWithFilter(`Service == "user-web"`)
if err != nil {
panic(err)
}
for key, _ := range data{
fmt.Println(key)
}
}
func main(){
//_ = Register("192.168.1.102", 8021, "user-web", []string{"mxshop", "bobby"}, "user-web")
//AllServices()
FilterSerivice()
}
grpc健康检查规范:
grpc已经帮我们生成了健康检查的服务。
客户端可以使用check()方法来检查服务状态
grpc_health_v1.RegisterHealthServer(server, health.NewServer())
Go的优雅操作-同步信息并负载均衡
上面这种实现方式不推荐,因为初始化客户端的时候把当前consul中的微服务已经写死在全局变量中了,如果调用的微服务变更端口,增加机器等无法自动更新导致无法调用成功微服务
从服务中心获取服务
import _ "github.com/mbobakov/grpc-consul-resolver" // 必须要导入
func InitSrvConn(){
consulInfo := global.ServerConfig.ConsulInfo
userConn, err := grpc.Dial(
fmt.Sprintf("consul://%s:%d/%s?wait=14s", consulInfo.Host, consulInfo.Port, global.ServerConfig.UserSrvInfo.Name),
grpc.WithInsecure(),
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),
)
if err != nil {
zap.S().Fatal("[InitSrvConn] 连接 【用户服务失败】")
}
userSrvClient := proto.NewUserClient(userConn)
global.UserSrvClient = userSrvClient
}
关于grpc.WithDefaultServiceConfig的配置可选:
我们可以看出目前所持支的负载均衡的策略,其他策略正在试验阶段。一般我们选择的就是轮询。
实际项目中如何使用
- 注册:注册是一个公用的类,一般会将方法封装成一个工具类,放在
utils包下来供使用。 - 发现:api层需要多次使用服务,不能每次使用就重新建立一个连接。在实际项目中,需要有一个服务的初始化程序,初始化服务作为全局变量来提供使用。初始化程序应该在主程序中运行,consul服务发现就需要在初始化程序中使用。
6. 总结
本文简单介绍Consul的安装使用和注意事项。举例说明如何使用go来操作Consul。简单介绍在实际项目中如何使用Consul。