Consul
DNS解析
- 输入URL网址。
- 通过DNS服务器(或hosts文件)找到对应网址的IP地址。
- 向对应的IP地址服务器发送请求。
- 服务器接收请求并处理。
- 服务器向客户端响应相关资源。
微服务中心
微服务中心也叫服务发现,是微服务架构中的一个核心组件或工具,用于解决在分布式系统中各个服务如何相互定位和通信的问题,可以管理、协调和监控分布式微服务的运行。
- 统一管理服务配置。
- 监控服务的监控状况。
- 提供服务的发现与接入。
- 服务的注册、注销、服务的信息。
- HTTP访问、DNS解析等。
- 存储数据。
Docker
VM
软件开发时经常会遇到软件运行的环境问题,如不兼容、支持配置等。
为了解决此问题,虚拟机技术到来。它是一个小型的操作系统,可在操作系统中以软件的形式安装,并基于此系统运行相关软件。但也产生了一些问题:
- 虚拟机占用系统资源过多。
- 系统有一些访问权限,配置比较复杂。
- 性能与速度无法保证。
Docker
Docker是一种开源的平台,用于开发、部署和运行应用程序。它通过使用容器化技术,使应用程序可以在不同环境下轻松地运行,而不必担心环境差异引起的问题。
Docker是基于Linux操作系统开发的一套虚拟化技术,是Linux容器技术功能性封装的产物,它不是操作系统,而是基于宿主系统的一个进程隔离。
- 由于其不是操作系统,因此启动比较快。
- 动态资源分配,资源占用容易优化。
- 体积小,不是一个系统级的体量。
- 使用灵活,拓展性强。
// 安装
brew install --cask docker
// 查看版本
docker -v
// 查询docker镜像
docker images
// 查询容器
docker ps -a
Consul
Consul是一个服务发现与可视化管理服务的工具。
安装与启动
使用Docker安装Consul:
docker pull hashicorp/consul:latest
通过docker run命令启动Consul:
docker run -d -p 8500:8500 -p 8600:8600/udp -p 8300:8300 -p 8301:8301 -p 8302:8302 hashicorp/consul consul agent -dev -client=0.0.0.0
8500:提供服务的列表、注册服务、注销服务、HTTP接口、可视化工具。8600:提供DNS服务发现。8300:同一个数据中心,服务之间使用该端口通信。8301:同一个数据中心,客户端之间使用该端口通信。8302:不同的数据中心,服务之间使用该端口通信。
// 重启consul
docker container update --restart=always containerId
API
Consul提供了API接口,可通过接口进行相关操作,如注册、注销等。
服务注册
Consul 可直接进行 HTTP 服务的注册:
type ServiceMap = map[string]*api.AgentService
func ServiceRegister(
hostInfo config.Host,
consulInfo config.Consul,
serviceInfo config.UserWebApi,
) error {
// 创建 consul 客户端, 健康检查和服务注册需要通过客户端完成
client, err := createClient(hostInfo.IP, consulInfo.PORT)
if err != nil {
return err
}
// 健康检查配置
healthCheck := &api.AgentServiceCheck{
HTTP: fmt.Sprintf("%s://%s:%d%s", serviceInfo.Scheme, hostInfo.IP, serviceInfo.PORT, serviceInfo.Path),
Timeout: consulInfo.Timeout,
Interval: consulInfo.Interval,
DeregisterCriticalServiceAfter: consulInfo.Deregister,
}
// 注册服务的配置,一般通过配置文件提供
reg := new(api.AgentServiceRegistration)
reg.Address = hostInfo.IP
reg.Port = serviceInfo.PORT
reg.Name = serviceInfo.Name
reg.ID = serviceInfo.Name
// 关联健康检查
reg.Check = healthCheck
reg.Tags = serviceInfo.Tags
// 通过客户端注册服务
e := client.Agent().ServiceRegister(reg)
if e != nil {
return e
}
return nil
}
func createClient(addr string, port int) (*api.Client, error) {
cfg := api.DefaultConfig()
cfg.Address = fmt.Sprintf("%s:%d", addr, port)
client, err := api.NewClient(cfg)
if err != nil {
return nil, err
}
return client, nil
}
服务查询
Consul 也可通过 API 进行服务查询与过滤
func ServiceFilter(addr string, port int, filter string) (ServiceMap, error) {
client, err := createClient(addr, port)
if err != nil {
return nil, err
}
services, e := client.Agent().ServicesWithFilter(filter)
if e != nil {
return nil, e
}
return services, nil
}
// services, e := lib.ServiceList(cfg.Host.IP, cfg.Consul.PORT,"")
// services, e := lib.ServiceFilter(cfg.Host.IP, cfg.Consul.PORT, `Service == "user_web_api"`)
// services, e := lib.ServiceFilter(cfg.Host.IP, cfg.Consul.PORT, `api in Service`)
gRPC服务
由于 gRPC 不是普通的 HTTP服务,需要借助官方工具包进行注册:
import (
// Google官方健康检查包
"google.golang.org/grpc/health"
"google.golang.org/grpc/health/grpc_health_v1"
)
func main() {
// ...
// 注册服务健康检查
grpc_health_v1.RegisterHealthServer(gServer, health.NewServer())
// 注册 consul 服务
err := utils.ServiceRegister(*cfg.Consul, *cfg.GRPC)
// ...
}
func ServiceRegister(consulInfo config.Consul, serviceInfo config.GRPC) {
// ...
healthCheck := &api.AgentServiceCheck{
GRPC: fmt.Sprintf("%s:%d", serviceInfo.Host, serviceInfo.Port),
Timeout: consulInfo.Timeout,
Interval: consulInfo.Interval,
DeregisterCriticalServiceAfter: consulInfo.Deregister,
}
// ...
}
type Consul struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Timeout string `mapstructure:"timeout"`
Interval string `mapstructure:"interval"`
Deregister string `mapstructure:"deregister"`
}
type GRPC struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Name string `mapstructure:"name"`
Tags []string `mapstructure:"tags"`
}
开启多微服务
- IDE 启动两次,会重启服务。
- 终端启动两次服务,由于 Consul 的 ID 是同一个,Consul 认为是同一个实例,因此会覆盖。
- 每次启动都创建不同的 ID 到 Consul 进行注册。
import (
uuid "github.com/satori/go.uuid"
)
func main() {
// ...
serviceId := fmt.Sprintf("%s-%s", cfg.GRPC.Name, uuid.NewV4())
// ServiceRegister(serviceId)
}
负载均衡
实际业务中,微服务通常也需要进行负载均衡,而 GRPC 内置了对外的接口,方便进行配置:
import (
_ "github.com/mbobakov/grpc-consul-resolver"
)
func GrpcDial(cfg config.Consul) (*grpc.ClientConn, error) {
conn, err := grpc.NewClient(
// 服务集合
fmt.Sprintf("consul://%s:%d/%s?wait=14s", cfg.IP, cfg.PORT, cfg.Name),
grpc.WithTransportCredentials(insecure.NewCredentials()),
// 负载均衡轮询
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),
)
return conn, err
}