功能說明
應用場景
資料庫高負荷時,在員工賬號層級進行限流,目的是讓資料庫儘早緩衝過來
特色功能
- 支援臨時 追加/減少 白名單名額
- 支援重置白名單
- 直接配置化,不需要運算元據庫
原始碼
// config/white_list.php
<?php
// 搶白名單配置,白名單內的員工允許訪問 xxx 系統,其他員工則跳轉到登陸介面
// 變更配置後,記得執行 php artisan optimize
return [
// 是否啟用
'is_enable' => env('WHITE_LIST_IS_ENABLE', 0),
// 白名單名額,支援臨時追加白名單名額,但不支援減少操作
'number' => env('WHITE_LIST_NUMBER', 100),
// 快取 key 字首,變更後會重置白名單
'cache_key_prefix' => env('WHITE_LIST_CACHE_KEY_PREFIX', 'first'),
// 快取 key
'cache_key_list_init' => '_xxx_white_list_init',
'cache_key_list' => '_xxx_white_list',
'cache_key_hash' => '_xxx_white_hash'
'cache_ttl' => 43200,
// 員工沒在白名單內的提示文案,並跳轉到登陸介面
'error_msg' => 'xxx 系統暫不可用,請稍晚一些再登陸使用,謝謝配合!',
];
// Services/RedisHelper.php
<?php
namespace App\Services;
use Illuminate\Support\Facades\Redis;
class RedisHelper
{
/**
* redis排他鎖:拒絕併發相同的請求
* @param string $key
* @param $value
* @param int $ttl
* @return bool
*/
public static function setNxWithTTL(string $key, $value, int $ttl = 300): bool
{
if ((Redis::connection('default'))->set($key, $value, 'ex', $ttl, 'nx')) {
return true;
}
return false;
}
}
/**
* 核心程式碼~判斷該員工是否在白名單內:只允許部分員工可以正常使用 xxx 系統,其他人則跳轉到登陸介面
* @param int $staffId
* @return bool 返回 true 表示該員工在白名單內,允許正常使用 xxx 系統
*/
public function isInWhiteList(int $staffId): bool
{
// 是否啟用白名單功能
if (!config('white_list.is_enable')) {
return true;
}
// 白名單名額
$whiteListNumber = (int)config('white_list.number');
if ($whiteListNumber <= 0) {
return false;
}
$redis = Redis::connection('default');
$cacheKeyPrefix = config('white_list.cache_key_prefix');
$cacheKeyListInit = $cacheKeyPrefix . config('white_list.cache_key_list_init');
$cacheKeyList = $cacheKeyPrefix . config('white_list.cache_key_list');
$cacheKeyHash = $cacheKeyPrefix . config('white_list.cache_key_hash');
$cacheTTL = config('white_list.cache_ttl');
// 判斷該員工是否在白名單中
if ($redis->exists($cacheKeyHash) && $redis->hexists($cacheKeyHash, $staffId)) {
return true;
}
// 初始化令牌佇列
if (!$redis->exists($cacheKeyListInit)) {
if (!RedisHelper::setNxWithTTL("{$cacheKeyListInit}_nx", 1, config('cache.one_hour'))) {
return true;
}
$redis->rpush($cacheKeyListInit, array_fill(0, $whiteListNumber, 1));
$redis->expire($cacheKeyListInit, $cacheTTL);
}
// 支援臨時追加白名單名額
$length = $redis->llen($cacheKeyListInit);
if ($length < $whiteListNumber) {
if (!RedisHelper::setNxWithTTL("{$cacheKeyListInit}_nx_{$whiteListNumber}", 1, config('cache.one_hour'))) {
return false;
}
$redis->rpush($cacheKeyListInit, array_fill($length - 1, $whiteListNumber - $length, 1));
$redis->expire($cacheKeyListInit, $cacheTTL);
}
// 避免併發情況下,同一員工佔用多個白名單名額
if (!$redis->hsetnx($cacheKeyHash, $staffId, 1)) {
return true;
}
$index = $redis->rpush($cacheKeyList, [$staffId]);
$ret = $redis->lindex($cacheKeyListInit, $index - 1);
// 手慢了,白名單名額已被搶完
if (empty($ret)) {
$redis->rpop($cacheKeyList);
$redis->hdel($cacheKeyHash, [$staffId]);
return false;
}
$redis->expire($cacheKeyList, $cacheTTL);
$redis->expire($cacheKeyHash, $cacheTTL);
return true;
}
使用案例
啟用白名單功能,在 .env 檔案配置以下引數:
WHITE_LIST_IS_ENABLE=1 WHITE_LIST_NUMBER=10
追加白名單名額,在 .env 檔案調整以下引數:
WHITE_LIST_NUMBER=20
減少白名單名額,在 .env 檔案調整以下引數:
WHITE_LIST_NUMBER=10 WHITE_LIST_CACHE_KEY_PREFIX="second"
覺得該換另一批人使用 xxx 系統了,則重置白名單,在 .env 檔案調整以下引數:
WHITE_LIST_CACHE_KEY_PREFIX="third"
關閉白名單功能,在 .env 檔案調整以下引數:
WHITE_LIST_IS_ENABLE=0
本作品採用《CC 協議》,轉載必須註明作者和本文連結