golang实现基于consul的分布式锁

我们在分布式应用中,经常都会用到分布式锁,分布式锁的实现可以有很多种,除了我们常用的redis之外,这里主要是介绍一下consul的锁的实现与原理和自己写的一个基于consul的demo

consul介绍

Consul 是 Go 实现的一个轻量级,支持服务发现 、KV存储 的工具,它通过强一致性的KV存储实现了简易的 分布式锁 ,下面我们根据源码看下 Consul 是怎么解决以上分布式锁的难点的。

// api/lock.go

// Lock 分布式锁数据结构
type Lock struct {
    c    *Client   // 提供访问consul的API客户端
    opts *LockOptions // 分布式锁的可选项
    
    isHeld       bool          // 该锁当前是否已经被持有
    sessionRenew chan struct{} // 通知锁持有者需要更新session
    locksession  string        // 锁持有者的session
    l            sync.Mutex    // 锁变量的互斥锁
}

// LockOptions 提供分布式锁的可选项参数
type LockOptions struct {
    Key              string        // 锁的 Key,必填项,且必须有 KV 的写权限
    Value            []byte        // 锁的内容,以下皆为选填项
    Session          string        // 锁的session,用于判断锁是否被创建
    SessionOpt       *SessionEntry // 自定义创建session条目,用于创建session,避免惊群
    SessionName      string        // 自定义锁的session名称,默认为 "Consul API Lock"
    SessionTTL       string        // 自定义锁的TTL时间,默认为 "15s"
    MonitorRetries   int           // 自定义监控的重试次数,避免脑裂问题
    MonitorRetryTime time.Duration // 自定义监控的重试时长,避免脑裂问题
    LockWaitTime     time.Duration // 自定义锁的等待市场,避免死锁问题
    LockTryOnce      bool          // 是否只重试一次,默认为false,则为无限重试
}


// 提供的接口:
// LockOpts 通过传入锁的参数,返回一个可用的锁
// 必须注意的是 opts.Key 必须在 KV 中有写权限
func (c *Client) LockOpts(opts *LockOptions) (*Lock, error)

// Lock尝试获取一个可用的锁,可以通过一个非空的 stopCh 来提前终止获取
// 如果返回的锁发生异常,则返回一个被关闭的 chan struct ,应用程序必须要处理该情况
func (l *Lock) Lock(stopCh <-chan struct) (<-chan struct{}, error)

// Unlock 尝试释放 consul 分布式锁,如果发生异常则返回 error
func (l *Lock) Unlock() error 

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

demo:

package dao

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

const (
	host = "127.0.0.1:8500"
)

var (
	Client  *api.Client
	Lockers map[string]*api.Lock
)

//初始化consul
func initConsul() {
	client, err := api.NewClient(&api.Config{Address: host})
	if err == nil {
		Client = client
	}
}

//Lock 上锁
func Lock(key, sessionTTL string) error {
	opts := &api.LockOptions{
		Key:        key,
		Value:      []byte("set by sender 1"),
		SessionTTL: sessionTTL,
		//SessionTTL: "10s",
		SessionOpts: &api.SessionEntry{
			Checks:   []string{"check1", "check2"},
			Behavior: "release",
		},
	}

	lock, err := Client.LockOpts(opts)
	if err != nil {
		return errors.New("failed to created lock")
	}
	Lockers[key] = lock

	_, err = lock.Lock(nil)
	if err != nil {
		return errors.New("failed to accquired lock")
	}
	return nil
}

//Unlock 解锁
func Unlock(key string) error {
	if l, ok := Lockers[key]; ok && l != nil {
		err := l.Unlock()
		if err != nil {
			return errors.New("failed to unlock")
		}
		return nil
	}
	return errors.New("not exist locker")
}

猜你喜欢

转载自blog.csdn.net/shalaoq/article/details/107475399