Redis作为缓存使用以及加锁的实现

作为缓存使用

<?php
declare(strict_types=1);
ini_set('display_errors', 'On');
error_reporting(-1);

require_once __DIR__ . '/predis/vendor/autoload.php';

// Redis服务器集群
$servers = [
    'tcp://localhost:6379',      // 服务器0
    // 'tcp://192.168.1.1:6379', // 服务器1
    // 'tcp://192.168.1.2:6379', // 服务器2
    // 'tcp://192.168.1.3:6379', // 服务器3
    // 'tcp://192.168.1.4:6379', // 服务器4
    // 'tcp://192.168.1.5:6379', // 服务器5
];

$options = [
    'cluster' => 'redis',
    'parameters' => [
        'password' => 'Redis密码'
    ]
];

$redis = new Predis\Client($servers, $options);

// 设置缓存
$redis->set('name', '张三');
$redis->set('gender', '男');
$redis->set('birth', 2003);

// 获取缓存
$name = $redis->get('name');
$gender = $redis->get('gender');
$birth = $redis->get('birth');

echo "俺叫{$name}{$gender}),出生于{$birth}年。" . PHP_EOL; // 俺叫张三(男),出生于2003年。



加锁的实现

<?php
declare(strict_types=1);
ini_set('display_errors', 'On');
error_reporting(-1);

require_once __DIR__ . '/predis/vendor/autoload.php';

// Redis服务器集群
$servers = [
    'tcp://localhost:6379',      // 服务器0
    // 'tcp://192.168.1.1:6379', // 服务器1
    // 'tcp://192.168.1.2:6379', // 服务器2
    // 'tcp://192.168.1.3:6379', // 服务器3
    // 'tcp://192.168.1.4:6379', // 服务器4
    // 'tcp://192.168.1.5:6379', // 服务器5
];

$options = [
    'cluster' => 'redis',
    'parameters' => [
        'password' => 'Redis密码'
    ]
];

$redis = new Predis\Client($servers, $options);

/**
 * 加锁
 *
 * @param string $key 锁标识
 * @param int $seconds 锁有效时间(单位:秒)
 * @return bool true=加锁成功|false=加锁失败(重复加锁)
 */
function lock(string $key, int $seconds): bool
{
    global $redis;

    // 当$key不存在才set,若set成功返回int(1),否则返回int(0)
    $ok = $redis->setnx($key, '1') === 1;

    // 设置锁有效时间
    $ok && $ok = $redis->expire($key, $seconds) === 1;

    return $ok;
}

/**
 * 获取锁的剩余有效时间
 * 重要说明①:当$key不存在时返回-2,当$key存在但没有为其设置有效时间时返回-1,
 *             且剩余有效时间可能为0,使用时请务必注意判断。
 *
 * @param string $key 锁标识
 * @return int 锁有效时间(单位:秒)
 */
function lock_expire(string $key): int
{
    global $redis;

    return $redis->ttl($key);
}

/**
 * 解锁
 *
 * @param string $key 锁标识
 * @return bool true=解锁成功(锁不存在也归为成功)|false=解锁失败
 */
function unlock(string $key): bool
{
    global $redis;

    $count = $redis->del($key);

    return $count === 0 || $count === 1;
}

// 对用户9527进行登录锁定,防止重复登录
$key = 'LOGIN_LOCK_UID_9527';
$locked = lock($key, 10);

//========== 加锁成功,可以开始执行登录逻辑 ==========//
if ($locked) {
    exit('用户正在登录……');

    // 验证账号密码,若登录失败在向前端返回错误信息前记得要先解锁
    // TODO...

    // 账号密码正确,更新用户最后登录时间、最后登录IP等信息
    // TODO...

    // 设置session数据
    // TODO...

    // 登录逻辑执行完毕,可以解锁
    // unlock($key);
}

//========== 加锁失败,原因:重复加锁(即用户正在登录) ==========//
// 这里可以直接返回“请勿重复操作”或“您操作太频繁”等错误信息
// TODO...
$expire = lock_expire($key);
$expire === 0 && $expire = 1; // 如果剩余时间为0了就改为1,不然剩余0秒会造成用户疑惑
exit("该账号被锁定,还有{$expire}秒后才可登录");

Copyright © 2024 码农人生. All Rights Reserved