Docker安装consul + go使用consul + consul知识

发布于:2024-10-10 ⋅ 阅读:(135) ⋅ 点赞:(0)

1. 什么是服务注册和发现

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

1. 技术选型

Consul 与其他常见服务发现框架对比

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

2.consul的安装和配置

2.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.2. 访问

浏览器访问 127.0.0.1:8500

2.3. 访问dns

consul提供dns功能,可以让我们通过, 可以通过dig命令行来测试,consul默认的dns端口是8600, 命令行:
linux下的dig命令安装:
yum install bind-utils

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

windows下载dig命令 : BIND9.17.2.x64.zip

3.consul的api接口

3.1. 添加服务

https://www.consul.io/api-docs/agent/service#register-service

3.2. 删除服务

https://www.consul.io/api-docs/agent/service#deregister-service

3.3. 设置健康检查

https://www.consul.io/api-docs/agent/check

3.4. 同一个服务注册多个实例

3.5. 获取服务

https://www.consul.io/api-docs/agent/service#list-services

4.go操作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.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
    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()
}

5.grpc下的健康检查

5.1. grpc的健康检查规范

官方文档
grpc健康检查重要点:

  1. check = {
    “GRPC”: "ip:port",
    “GRPCUseTLS”: False,
    “Timeout”: “5s”,
    “Interval”: “5s”,
    “DeregisterCriticalServiceAfter”: “5s”,
    }
  2. 一定要确保网络是通的

  3. 一定要确保srv服务监听端口是对外可访问的(公网ip地址不是本地的127.0.0.1)
  4. GRPC一定要自己填写

5.2. go配置grpc的健康检查

//注册服务健康状态检查
grpc_health_v1.RegisterHealthServer(g, health.NewServer())

6.动态获取可用端口号

package utils
import (
    "net"
)
func GetFreePort() (int, error) {
    addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
    if err != nil {
        return 0, err
    }
    l, err := net.ListenTCP("tcp", addr)
    if err != nil {
        return 0, err
    }
    defer l.Close()
    return l.Addr().(*net.TCPAddr).Port,  nil
}

7.负载均衡策略

1. 什么是负载均衡

7.负载均衡策略 - 图1

2. 负载均衡策略

1. 集中式load balance

集中式LB方案,如下图。首先,服务的消费方和提供方不直接耦合,而是在服务消费者和服务提供者之间有一个独立的LB(LB通常是专门的硬件设备如F5,或者基于软件如LVS,HAproxy等实现)。

7.负载均衡策略 - 图2

LB上有所有服务的地址映射表,通常由运维配置注册,当服务消费方调用某个目标服务时,它向LB发起请求,由LB以某种策略(比如Round-Robin)做负载均衡后将请求转发到目标服务。
LB一般具备健康检查能力,能自动摘除不健康的服务实例。
服务消费方如何发现LB呢?通常的做法是通过DNS,运维人员为服务配置一个DNS域名,这个域名指向LB。
这种方案基本可以否决,因为它有致命的缺点:所有服务调用流量都经过load balance服务器,所以load balance服务器成了系统的单点,一旦LB发生故障对整个系统的影响是灾难性的。为了解决这个问题,必然需要对这个load balance部件做分布式处理(部署多个实例,冗余,然后解决一致性问题等全家桶解决方案),但这样做会徒增非常多的复杂度。

2. 进程内load balance

进程内load balance。将load balance的功能和算法以sdk的方式实现在客户端进程内。先看架构图:

7.负载均衡策略 - 图3

可看到引入了第三方:服务注册中心。它做两件事:

  1. 维护服务提供方的节点列表,并检测这些节点的健康度。检测的方式是:每个节点部署成功,都通知服务注册中心;然后一直和注册中心保持心跳。
  2. 允许服务调用方注册感兴趣的事件,把服务提供方的变化情况推送到服务调用方。

这种方案下,整个load balance的过程是这样的:

  1. 服务注册中心维护所有节点的情况。
  2. 任何一个节点想要订阅其他服务提供方的节点列表,向服务注册中心注册。
  3. 服务注册中心将服务提供方的列表(以长连接的方式)推送到消费方。
  4. 消费方接收到消息后,在本地维护一份这个列表,并自己做load balance。

可见,服务注册中心充当什么角色?它是唯一一个知道整个集群内部所有的节点情况的中心。所以对它的可用性要求会非常高,这个组件可以用Zookeeper实现。
这种方案的缺点是:每个语言都要研究一套sdk,如果公司内的服务使用的语言五花八门的话,这方案的成本会很高。第二点是:后续如果要对客户库进行升级,势必要求服务调用方修改代码并重新发布,所以该方案的升级推广有不小的阻力。

3. 独立进程load balance

该方案是针对第二种方案的不足而提出的一种折中方案,原理和第二种方案基本类似,不同之处是,他将LB和服务发现功能从进程内移出来,变成主机上的一个独立进程,主机上的一个或者多个服务要访问目标服务时,他们都通过同一主机上的独立LB进程做服务发现和负载均衡。如图

7.负载均衡策略 - 图4

这个方案解决了上一种方案的问题,不需要为不同语言开发客户库,LB的升级不需要服务调用方改代码。
但新引入的问题是:这个组件本身的可用性谁来维护?还要再写一个watchdog去监控这个组件?另外,多了一个环节,就多了一个出错的可能,线上出问题了,也多了一个需要排查的环节。

8.常见的负载均衡算法

在分布式系统中,多台服务器同时提供一个服务,并统一到服务配置中心进行管理,消费者通过查询服务配置中心,获取到服务到地址列表,需要选取其中一台来发起RPC远程调用。如何选择,则取决于具体的负载均衡算法,对应于不同的场景,选择的负载均衡算法也不尽相同。负载均衡算法的种类有很多种,常见的负载均衡算法包括轮询法、随机法、源地址哈希法、加权轮询法、加权随机法、最小连接法等,应根据具体的使用场景选取对应的算法。

1. 轮询(Round Robin)法

轮询很容易实现,将请求按顺序轮流分配到后台服务器上,均衡的对待每一台服务器,而不关心服务器实际的连接数和当前的系统负载。

2. 随机法

通过系统随机函数,根据后台服务器列表的大小值来随机选取其中一台进行访问。由概率概率统计理论可以得知,随着调用量的增大,其实际效果越来越接近于平均分配流量到后台的每一台服务器,也就是轮询法的效果。

3. 源地址哈希法

源地址哈希法的思想是根据服务消费者请求客户端的IP地址,通过哈希函数计算得到一个哈希值,将此哈希值和服务器列表的大小进行取模运算,得到的结果便是要访问的服务器地址的序号。采用源地址哈希法进行负载均衡,相同的IP客户端,如果服务器列表不变,将映射到同一个后台服务器进行访问。

4. 加权轮询(Weight Round Robin)法

不同的后台服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不一样。跟配置高、负载低的机器分配更高的权重,使其能处理更多的请求,而配置低、负载高的机器,则给其分配较低的权重,降低其系统负载,加权轮询很好的处理了这一问题,并将请求按照顺序且根据权重分配给后端。

5. 加权随机(Weight Random)法

加权随机法跟加权轮询法类似,根据后台服务器不同的配置和负载情况,配置不同的权重。不同的是,它是按照权重来随机选取服务器的,而非顺序。

6. 最小连接数法

前面我们费尽心思来实现服务消费者请求次数分配的均衡,我们知道这样做是没错的,可以为后端的多台服务器平均分配工作量,最大程度地提高服务器的利用率,但是,实际上,请求次数的均衡并不代表负载的均衡。因此我们需要介绍最小连接数法,最小连接数法比较灵活和智能,由于后台服务器的配置不尽相同,对请求的处理有快有慢,它正是根据后端服务器当前的连接情况,动态的选取其中当前积压连接数最少的一台服务器来处理当前请求,尽可能的提高后台服务器利用率,将负载合理的分流到每一台服务器。

9.grpc的负载均衡策略

 9.1. grpc的负载均衡策略

 文档

 9.2. go使用grpc负载均衡

 grpc-consul-resolver地址

 9.3. 关于serverconfig

 官方文档

 9.4. go的grpc测试

  

package main
import (
    "OldPackageTest/grpclb_test/proto"
    "context"
    "fmt"
    "log"
    _ "github.com/mbobakov/grpc-consul-resolver" // It's important
    "google.golang.org/grpc"
)
func main() {
    conn, err := grpc.Dial(
        "consul://192.168.1.103:8500/user-srv?wait=14s&tag=srv",
        grpc.WithInsecure(),
        grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()
    for i := 0; i<10; i++{
        userSrvClient := proto.NewUserClient(conn)
        rsp, err := userSrvClient.GetUserList(context.Background(), &proto.PageInfo{
            Pn:    1,
            PSize: 2,
        })
        if err != nil {
            panic(err)
        }
        for index, data := range rsp.Data{
            fmt.Println(index, data)
        }
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到