转载

Golang 通过 Consul 实现分布式锁

Consul 是什么

Golang 通过 Consul 实现分布式锁

Consul 是一个支持多数据中心分布式高可用的服务发现和配置共享的服务软件,由 HashiCorp 公司用 Go 语言开发, 基于 Mozilla Public License 2.0 的协议进行开源. Consul 支持健康检查,并允许 HTTP 和 DNS 协议调用 API 存储键值对.

命令行超级好用的虚拟机管理软件 vgrant 也是 HashiCorp 公司开发的产品.

一致性协议采用 Raft 算法,用来保证服务的高可用. 使用 GOSSIP 协议管理成员和广播消息, 并且支持 ACL 访问控制.

Consul 的使用场景

  • docker 实例的注册与配置共享
  • coreos 实例的注册与配置共享
  • vitess 集群
  • SaaS 应用的配置共享
  • 与 confd 服务集成,动态生成 nginx 和 haproxy 配置文件

Consul 的优势

  • 使用 Raft 算法来保证一致性, 比复杂的 Paxos 算法更直接. 相比较而言, zookeeper 采用的是 Paxos, 而 etcd 使用的则是 Raft.
  • 支持多数据中心,内外网的服务采用不同的端口进行监听。 多数据中心集群可以避免单数据中心的单点故障,而其部署则需要考虑网络延迟, 分片等情况等. zookeeper 和 etcd 均不提供多数据中心功能的支持.
  • 支持健康检查. etcd 不提供此功能.
  • 支持 http 和 dns 协议接口. zookeeper 的集成较为复杂, etcd 只支持 http 协议.
  • 官方提供web管理界面, etcd 无此功能.

综合比较, Consul 作为服务注册和配置管理的新星, 比较值得关注和研究.

Consul 的角色

client: 客户端, 无状态, 将 HTTP 和 DNS 接口请求转发给局域网内的服务端集群.

server: 服务端, 保存配置信息, 高可用集群, 在局域网内与本地客户端通讯, 通过广域网与其他数据中心通讯. 每个数据中心的 server 数量推荐为 3 个或是 5 个.

Golang 通过 Consul 实现分布式锁

什么是分布式锁

Golang 通过 Consul 实现分布式锁

分布式锁 ,是控制 分布式系统 之间同步访问共享 资源 的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要 互斥 来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。

目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。

在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。

在这里我们使用Consul来管理分布式锁。Consul内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(比如 ZooKeeper 等)。

Sessions

Golang 通过 Consul 实现分布式锁

session是 一个 远程 进程 和consul 节点 之间 的 链接, 它 由 一个 远程 进程 和 可以 显式 无效 或 由于 健康 检查 机制 。 根据 会话 配置 , 创建 与 已 失效 会话 锁 摧毁 或 释放 。

Health checks

Consul支持多种 检查 (如 HTTP 、 TCP 等)。 在session 创建 过程中 可以 定义 的 健康 检查 列表。 这些 检查 将 用于 确定 是否sessio 需要 使 之 失效 。

TTL

除了 健康 检查 , 会话 也 具有 内置 支持 的 TTL 。 当 TTL 过期session 被视为 无效 。 远程 进程 负责 更新session 之前 TTL 过期 。

Golang API

Consul API client 提供 一个 方便 的 抽象 ,session 和 K/V  存储 。 有 是 一个 锁 结构 与 锁定 、 解锁 和 破坏 的 方法 。 也 有 用于帮助 创建 锁 实例 方法 。 API 客户端 还 负责 更新 会话 。

Creating the Consul client

client, err := api.NewClient(&api.Config{Address: "127.0.0.1:8500"})

Creating Lock instance

type LockOptions struct {
    Key              string        // Must be set and have write permissions
    Value            []byte        // Optional, value to associate with the lock
    Session          string        // Optional, created if not specified
    SessionOpts      *SessionEntry // Optional, options to use when creating a session
    SessionName      string        // Optional, defaults to DefaultLockSessionName (ignored if SessionOpts is given)
    SessionTTL      string        // Optional, defaults to DefaultLockSessionTTL (ignored if SessionOpts is given)
    MonitorRetries  int          // Optional, defaults to 0 which means no retries
    MonitorRetryTimetime.Duration // Optional, defaults to DefaultMonitorRetryTime
    LockWaitTime    time.Duration // Optional, defaults to DefaultLockWaitTime
    LockTryOnce      bool          // Optional, defaults to false which means try forever
}

LockOptions 是 所有 可能 的 选项 的 容器 , 可以 用于 设置 键 和 值 、 定制 会话 或 设置 TTL 。

opts := &api.LockOptions{
  Key:        "webhook_receiver/1",
  Value:      []byte("set by sender 1"),
  SessionTTL: "10s",
  SessionOpts: &api.SessionEntry{
    Checks:  []string{"check1", "check2"},
    Behavior: "release",
  },
}
lock, err := client.LockOpts(opts)

另一种常用的 方法 是 LockKey, 它 创建 一个 锁 与 所有 选项 设置 为 默认 条目 名称 除外 。

lock, err := client.LockKey("webhook_receiver/1")

Acquiring lock

stopCh := make(chan struct{})
lockCh, err := lock.Lock(stopCh)
if err != nil {
  panic(err)
}
cancelCtx, cancelRequest := context.WithCancel(context.Background())
req, _ := http.NewRequest("GET", "https://example.com/webhook", nil)
req = req.WithContext(cancelCtx)
gofunc() {
  http.DefaultClient.Do(req)
  select {
  case <-cancelCtx.Done():
    log.Println("request cancelled")
  default:
    log.Println("request done")
    err = lock.Unlock()
    if err != nil {
      log.Println("lock already unlocked")
    }
  }
}()
gofunc() {
  <-lockCh
  cancelRequest()
}()

Golang 通过 Consul 实现分布式锁

原文  https://xiequan.info/golang-通过-consul-实现分布式锁/
正文到此结束
Loading...