业务场景中可能遇到连点导致数据库记录有问题无法保证幂等性,这里简单写了一个中间件来防止连点或者并发请求导致数据库记录数据异常;
思路:利用redis锁机制,当前用户访问此url只能在访问结束后再进行访问
首先创建一个配置文件,在 config/ 文件下创建lock.php
<?php
return [
"lock" => [
"/url", //防止连点的url
],
"default_set" => [
"max_time" => 60,
"request" => [
],
"headers" => [
'Authorization' //区分不同用户,如果是oauth2 令牌登录,已token信息为key值
],
"message" => '正在处理中,请勿频繁操作',
"callback" => [],
],
];
创建中间件:配合config可读取
$app->configure('lock');
创建全局中间件:LockRouterMiddleware
$app->middleware([
\App\Http\Middleware\LockRouterMiddleware::class,
]);
在middleware文件夹下创建LockRouterMiddleware.php
<?php
namespace App\Http\Middleware;
use Illuminate\Support\Facades\Redis;
use Closure;
use Illuminate\Http\Request;
/**
* 防止并发(频繁)访问 配置在 config/lock.php
* Class LockRouterMiddleware
* @package App\Http\Middleware
*/
class LockRouterMiddleware
{
/**
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$judge = false;
$lock = config('lock');
$routeName = $request->path();
if (in_array($routeName, $lock['lock'] ?? [], false)) {
$judge = true;
}
if ($judge === true) {
$set = $lock[$routeName] ?? $lock['default_set'];
$message = $lock[$routeName]["message"] ?? $lock['default_set']["message"];
$headerString = "headers:";
if ($set["headers"]) {
foreach ($set["headers"] as $value) {
$headerString .= $value . "-" . $request->header($value, "") . ";";
}
} else {
$headerString .= ";";
}
$requestString = "request:";
if ($set["request"]) {
foreach ($set["request"] as $value) {
$requestString .= $value . "-" . $request->input($value, "") . ";";
}
} else {
$requestString .= ";";
}
$key = "lock:" . $routeName . ":" . $headerString . ":" . $requestString . ":";
if (Redis::setnx($key, 1)) {
Redis::expire($key, $set["max_time"] ?? 60);
} else {
return response()->json([
"code" => -1,
"data" => [],
"msg" => $message
]);
}
$response = $next($request);
Redis::del($key);
return $response;
}
return $next($request);
}
}