mirror of
https://github.com/kuaifan/dootask.git
synced 2026-01-05 20:10:26 +00:00
perf: 优化设备登录
This commit is contained in:
parent
4710479b46
commit
bbe071545d
@ -25,7 +25,6 @@ use App\Tasks\ZincSearchSyncTask;
|
|||||||
use App\Tasks\UnclaimedTaskRemindTask;
|
use App\Tasks\UnclaimedTaskRemindTask;
|
||||||
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
use Hhxsv5\LaravelS\Swoole\Task\Task;
|
||||||
use Laravolt\Avatar\Avatar;
|
use Laravolt\Avatar\Avatar;
|
||||||
use Swoole\Coroutine;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -4,6 +4,7 @@ namespace App\Models;
|
|||||||
|
|
||||||
use App\Module\Base;
|
use App\Module\Base;
|
||||||
use App\Module\Doo;
|
use App\Module\Doo;
|
||||||
|
use App\Module\Lock;
|
||||||
use Cache;
|
use Cache;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use DeviceDetector\DeviceDetector;
|
use DeviceDetector\DeviceDetector;
|
||||||
@ -218,13 +219,8 @@ class UserDevice extends AbstractModel
|
|||||||
self::record();
|
self::record();
|
||||||
return $hash;
|
return $hash;
|
||||||
}
|
}
|
||||||
// 没有记录,尝试创建一个(防止升级后所有登录都失效,保证留一个可以保持登录) // todo 后期删除
|
// 没有记录
|
||||||
return AbstractModel::transaction(function () use ($hash, $userid) {
|
return null;
|
||||||
if (self::whereUserid($userid)->withoutTrashed()->lockForUpdate()->exists()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return self::record() ? $hash : null;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -234,46 +230,48 @@ class UserDevice extends AbstractModel
|
|||||||
*/
|
*/
|
||||||
public static function record(string $token = null): ?self
|
public static function record(string $token = null): ?self
|
||||||
{
|
{
|
||||||
return AbstractModel::transaction(function () use ($token) {
|
if (empty($token)) {
|
||||||
if (empty($token)) {
|
$token = Doo::userToken();
|
||||||
$token = Doo::userToken();
|
$userid = Doo::userId();
|
||||||
$userid = Doo::userId();
|
$expiredAt = Doo::userExpiredAt();
|
||||||
$expiredAt = Doo::userExpiredAt();
|
} else {
|
||||||
} else {
|
$info = Doo::tokenDecode($token);
|
||||||
$info = Doo::tokenDecode($token);
|
$userid = $info['userid'] ?? 0;
|
||||||
$userid = $info['userid'] ?? 0;
|
$expiredAt = $info['expired_at'];
|
||||||
$expiredAt = $info['expired_at'];
|
}
|
||||||
}
|
$hash = md5($token);
|
||||||
|
//
|
||||||
$hash = md5($token);
|
return Lock::withLock("userDeviceRecord:{$hash}", function () use ($expiredAt, $userid, $hash, $token) {
|
||||||
$row = self::whereHash($hash)->lockForUpdate()->first();
|
return AbstractModel::transaction(function () use ($expiredAt, $userid, $hash, $token) {
|
||||||
if (empty($row)) {
|
$row = self::whereHash($hash)->first();
|
||||||
// 生成一个新的设备记录
|
if (empty($row)) {
|
||||||
$row = self::createInstance([
|
// 生成一个新的设备记录
|
||||||
'userid' => $userid,
|
$row = self::createInstance([
|
||||||
'hash' => $hash,
|
'userid' => $userid,
|
||||||
]);
|
'hash' => $hash,
|
||||||
if (!$row->save()) {
|
]);
|
||||||
return null;
|
if (!$row->save()) {
|
||||||
}
|
return null;
|
||||||
// 删除多余的设备记录
|
}
|
||||||
$currentDeviceCount = self::whereUserid($userid)->count();
|
// 删除多余的设备记录
|
||||||
if ($currentDeviceCount > self::$deviceLimit) {
|
$currentDeviceCount = self::whereUserid($userid)->count();
|
||||||
$rows = self::whereUserid($userid)->orderBy('id')->take($currentDeviceCount - self::$deviceLimit)->get();
|
if ($currentDeviceCount > self::$deviceLimit) {
|
||||||
foreach ($rows as $row) {
|
$rows = self::whereUserid($userid)->orderBy('id')->take($currentDeviceCount - self::$deviceLimit)->get();
|
||||||
UserDevice::forget($row);
|
foreach ($rows as $row) {
|
||||||
|
UserDevice::forget($row);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
$row->expired_at = $expiredAt;
|
||||||
$row->expired_at = $expiredAt;
|
if (Request::hasHeader('version')) {
|
||||||
if (Request::hasHeader('version')) {
|
$deviceInfo = array_merge(Base::json2array($row->detail), self::getDeviceInfo($_SERVER['HTTP_USER_AGENT'] ?? ''));
|
||||||
$deviceInfo = array_merge(Base::json2array($row->detail), self::getDeviceInfo($_SERVER['HTTP_USER_AGENT'] ?? ''));
|
$row->detail = Base::array2json($deviceInfo);
|
||||||
$row->detail = Base::array2json($deviceInfo);
|
}
|
||||||
}
|
$row->save();
|
||||||
$row->save();
|
|
||||||
|
|
||||||
Cache::put(self::ck($hash), $row->userid, now()->addHour());
|
Cache::put(self::ck($hash), $row->userid, now()->addHour());
|
||||||
return $row;
|
return $row;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
52
app/Module/Lock.php
Normal file
52
app/Module/Lock.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Module;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Support\Facades\Redis;
|
||||||
|
|
||||||
|
class Lock
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 使用Redis分布式锁执行闭包
|
||||||
|
* @param string $key 锁的key
|
||||||
|
* @param Closure $closure 要执行的闭包函数
|
||||||
|
* @param int $ttl 锁的过期时间(毫秒),默认10000(10秒)
|
||||||
|
* @param int $waitTimeout 等待锁的超时时间(毫秒),0表示不等待,默认10000(10秒)
|
||||||
|
* @return mixed 闭包函数的返回值
|
||||||
|
* @throws Exception 如果获取锁失败或闭包执行异常
|
||||||
|
*/
|
||||||
|
public static function withLock(string $key, Closure $closure, int $ttl = 10000, int $waitTimeout = 10000)
|
||||||
|
{
|
||||||
|
$lockKey = "lock:{$key}";
|
||||||
|
$lockValue = uniqid('', true); // 生成唯一值,用于安全释放锁
|
||||||
|
|
||||||
|
// 尝试获取锁,如果waitTimeout为0则直接返回false,否则等待指定时间
|
||||||
|
$acquired = false;
|
||||||
|
if ($waitTimeout > 0) {
|
||||||
|
$end = microtime(true) + ($waitTimeout / 1000);
|
||||||
|
while (microtime(true) < $end) {
|
||||||
|
if (Redis::set($lockKey, $lockValue, 'PX', $ttl, 'NX')) {
|
||||||
|
$acquired = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
usleep(100000); // 休眠100ms后重试
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$acquired = Redis::set($lockKey, $lockValue, 'PX', $ttl, 'NX');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$acquired) {
|
||||||
|
throw new Exception("Failed to acquire lock for key: {$key}");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 执行闭包
|
||||||
|
return $closure();
|
||||||
|
} finally {
|
||||||
|
// 安全释放锁(仅当锁值未变时删除)
|
||||||
|
Redis::eval("if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end", 1, $lockKey, $lockValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user