分布式锁之redis解决方案

20 篇文章 0 订阅
5 篇文章 1 订阅

reids安装多端口实例

Redis是一种跨平台NoSql内存数据库,这几年各大公司系统应用中非常多见,支持多种数据类型,可以持久化保存数据,很多公司直接拿来作为数据库使用.

相比于Memcached,Redis支持持久化保存数据,重启服务,数据不丢失,支持分布式缓存,多个服务主从读写分离,大大提高效率。

网上可下载https://github.com/MicrosoftArchive/redis 文件,msi文件可视化安装,安装成功后Windows服务中有Redis服务,鼠标右键启动、停止

如何进行多实例端口运行,整体思路:

1)、更换新端口

2)、配置RDB数据文件路径

3)、配置logs日志文件路径

具体如下:

1、找到安装后的程序目录,复制 redis.windows-service.conf 命名为 redis.windows-service-6380.conf

2、修改此配置文件中的配置属性

# 本地数据库名称

dbfilename dump-6380.rdb 

# 日志文件

logfile "logs/server_log_6380.txt"

# 日志输出

syslog-enabled yes

3、启动cmd命令行,执行

redis-server.exe --service-install redis.windows-service-6380.conf --service-name redis6380 --port 6380 

4、创建后执行命令,启动服务

redis-server.exe --service-start --service-name redis6380

5、cmd执行命令测试登录

redis-cli.exe -h 127.0.0.1 -p 6380

6、若需要删除实例服务则执行

redis-server.exe --service-uninstall --service-name redis6380

 

具体实现实例

 

<?php
/**
 * RedLock.php.
 * User: shen
 * Date: 2020/1/7
 */

class RedLock
{
    private $retryDelay;
    private $retryCount;
    private $clockDriftFactor = 0.01;
    private $quorum;
    private $servers = array();
    private $instances = array();
    function __construct(array $servers, $retryDelay = 200, $retryCount = 3)
    {
        // 所有实例
        $this->servers = $servers;
        // 重试延迟时间  毫秒
        $this->retryDelay = $retryDelay;
        // 重试次数
        $this->retryCount = $retryCount;
        // 最少存活redis个数
        $this->quorum  = min(count($servers), (count($servers) / 2 + 1));
    }

    /**
     * 加锁
     *  逻辑:
     *
     * @param $resource
     * @param $ttl
     * @return array|bool
     */
    public function lock($resource, $ttl)
    {
        // 初始化实例
        $this->initInstances();
        // 获取唯一值
        $token = uniqid();
        // 重试次数
        $retry = $this->retryCount;
        do {
            $n = 0;
            $startTime = microtime(true) * 1000;
            foreach ($this->instances as $instance) {
                if ($this->lockInstance($instance, $resource, $token, $ttl)) {
                    $n++;
                }
            }
            # Add 2 milliseconds to the drift to account for Redis expires
            # precision, which is 1 millisecond, plus 1 millisecond min drift
            # for small TTLs.
            //偏移量 = (锁的过期时间*锁过期的一个基数) + 2ms
            $drift = ($ttl * $this->clockDriftFactor) + 2;
            // 此时间为: 锁的过期时间-(加锁过程的时间)- 偏移量
            $validityTime = $ttl - (microtime(true) * 1000 - $startTime) - $drift;
            if ($n >= $this->quorum && $validityTime > 0) {
                return [
                    'validity' => $validityTime,
                    'resource' => $resource,
                    'token'    => $token,
                ];
            } else {
                foreach ($this->instances as $instance) {
                    $this->unlockInstance($instance, $resource, $token);
                }
            }
            // Wait a random delay before to retry
            // 获取一个重试延迟时间  (在 重试延迟时间的一半 到 重试延迟时间 中间取一个值)
            $delay = mt_rand(floor($this->retryDelay / 2), $this->retryDelay);
            // 休息一会 重新再取
            usleep($delay * 1000);
            // 重试次数减一
            $retry--;
        } while ($retry > 0);
        return false;
    }

    /**
     * 解锁
     * @param array $lock
     */
    public function unlock(array $lock)
    {
        $this->initInstances();
        $resource = $lock['resource'];
        $token    = $lock['token'];
        foreach ($this->instances as $instance) {
            $this->unlockInstance($instance, $resource, $token);
        }
    }

    /**
     *  初始化所有redis实例
     */
    private function initInstances()
    {
        if (empty($this->instances)) {
            foreach ($this->servers as $server) {
                list($host, $port, $timeout) = $server;
                $redis = new \Redis();
                $redis->connect($host, $port, $timeout);
                $this->instances[] = $redis;
            }
        }
    }

    /**
     * 加锁
     * @param $instance
     * @param $resource
     * @param $token
     * @param $ttl
     * @return mixed
     */
    private function lockInstance($instance, $resource, $token, $ttl)
    {
        return $instance->set($resource, $token, ['NX', 'PX' => $ttl]);
    }

    /**
     * 解锁
     * @param $instance
     * @param $resource
     * @param $token
     * @return mixed
     */
    private function unlockInstance($instance, $resource, $token)
    {
        $script = '
            if redis.call("GET", KEYS[1]) == ARGV[1] then
                return redis.call("DEL", KEYS[1])
            else
                return 0
            end
        ';
        return $instance->eval($script, [$resource, $token], 1);
    }
}
<?php
/**
 * testLock.php.
 * User: shen
 * Date: 2020/1/7
 */

require_once  './RedLock.php';

$servers = [
    ['127.0.0.1', 6380, 0.01],
    ['127.0.0.1', 6381, 0.01],
    ['127.0.0.1', 6382, 0.01],
];

try{
    $redLock = new RedLock($servers);
    // 模拟多个用户一起占用锁
    while (true) {
        // ttl:此时间为处理业务时间+加锁时间+不可控因素的时间
        $lock = $redLock->lock('test', 10000);
        if ($lock) {
            //todo  处理业务数据   处理网完毕调用解锁
            print_r($lock);
        } else {
            print "Lock not acquired\n";
        }
    }
}catch (\Exception $e){
    var_dump($e);
}

 

参考资料:https://www.cnblogs.com/jaylongli/p/11170085.html
https://www.cnblogs.com/jaylongli/p/11170085.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yilukuangpao

你的鼓励是我创造最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值