82 lines
1.5 KiB
Go
82 lines
1.5 KiB
Go
package redisLock
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/go-redis/redis/v8"
|
|
)
|
|
|
|
type RedLock struct {
|
|
locks []*RedisLockNew
|
|
RedLockOptions
|
|
}
|
|
|
|
func (r *RedLock) UnLock() (err error) {
|
|
for _, lock := range r.locks {
|
|
if _error := lock.UnLock(); _error != nil {
|
|
if err == nil {
|
|
err = _error
|
|
}
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (r *RedLock) Lock() error {
|
|
successLock := 0
|
|
for _, lock := range r.locks {
|
|
now := time.Now()
|
|
err := lock.Lock()
|
|
since := time.Since(now)
|
|
if err == nil && since < time.Duration(r.perHostTimeout) {
|
|
successLock++
|
|
}
|
|
}
|
|
|
|
if successLock < len(r.locks)>>1+1 {
|
|
return errors.New("redis lock timeout")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type RedisHost struct {
|
|
NetWork string
|
|
Host string
|
|
Pass string
|
|
}
|
|
|
|
func NewRedLock(key string, hosts []*RedisHost, opts ...RedLockOption) (*RedLock, error) {
|
|
if len(hosts) < 3 {
|
|
return nil, fmt.Errorf("invalid redis host length %d", len(hosts))
|
|
}
|
|
|
|
l := &RedLock{}
|
|
|
|
for _, opt := range opts {
|
|
opt(&l.RedLockOptions)
|
|
}
|
|
|
|
l.repairOption()
|
|
if len(hosts)*l.perHostTimeout*10 > l.expireHostTime {
|
|
return nil, fmt.Errorf("invalid redis host length %d", len(hosts)*l.perHostTimeout*10)
|
|
}
|
|
|
|
l.locks = make([]*RedisLockNew, 0, len(hosts))
|
|
|
|
for _, host := range hosts {
|
|
client := redis.NewClient(&redis.Options{
|
|
Network: host.NetWork,
|
|
Addr: host.Host,
|
|
Password: host.Pass,
|
|
})
|
|
l.locks = append(l.locks, NewRedisLock(client, context.Background(), key, WithExpireTime(5)))
|
|
}
|
|
|
|
return l, nil
|
|
}
|