需求背景:
入库单号:L-ASN202502270728
入库批次号:100025022800151
数量:500
理货后,生成了一个500的已理货数据,上架后,生成了两个500已上架数据,重复了
以php项目为例 php-laravel框架
1,通过url+请求参数进行加锁处理 可以组织90%以上的重复点击带来的重复数据的问题
PreventDupSubmit.php
<?php namespace App\Http\Middleware; use App\Exceptions\InvalidRequestException; use App\Http\ApiHelper\Response; use App\Http\Services\WmsLockService; use Closure; use Illuminate\Support\Facades\Redis; class PreventDupSubmit { /** * 简单的防止前端重复提交数据 导致的数据异常 * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { $requestParams = $request->all(); $requestParams["adminId"] = getAdminUserId(); $requestParams["uri"] = $request->getUri(); if(isset($requestParams["file"])){ unset($requestParams["file"]); } \Log::info(sprintf("并发请求url:%s,请求参数:%s",$request->getUri(),json_encode($requestParams))); ksort($requestParams); $key = md5(serialize($requestParams)); $getLock = WmsLockService::getLock($key,8); // $request->offsetSet("request_md5_key",$key); if(!$getLock){ return response()->json(json_decode(Response::setError("请勿重复请求,请5秒后再发起请求"), true)); } // $result = Redis::Connection("wms")->set('wms_' . $key, 1,"EX",5,"NX"); // if(!$result){ // throw new InvalidRequestException("请勿重复请求,请5秒后再发起请求"); // } $response = $next($request); WmsLockService::delLock($key); // Redis::Connection("wms")->del('wms_' . $key); return $response; } }
WmsLockService.php
<?php namespace App\Http\Services; use Illuminate\Support\Facades\Redis; class WmsLockService { const LOCK_KEY = "wms_lock_"; public static function getLock($Key="",$time=15){ $requestId = uniqid(); // 生成一个唯一的请求ID $expireTime = 1000 * intval($time); // 锁的过期时间,单位毫秒 // Lua脚本 $luaScript = <<<LUA local lock_key = KEYS[1] local request_id = ARGV[1] local expire_time = ARGV[2] local result = redis.call('SET', lock_key, request_id, 'NX', 'PX', expire_time) if result then return 1 else return 0 end LUA; $lockKey = self::LOCK_KEY.$Key; $result = Redis::Connection("wms")->eval($luaScript, 1, $lockKey, $requestId, $expireTime); return $result; } public static function delLock($lockKey=""){ Redis::Connection("wms")->del(self::LOCK_KEY.$lockKey); } }
Route::match(['get', 'post'], '/stockIn/stockShelf/oneKeyPutawayAction', 'StockShelfController@oneKeyPutawayAction')->middleware('prevent_dup_submit')
2,假如业务中出现非重复点击,或者由于事务中处理比较慢(请求上架或者理货 请求参数类似于[1,3,5] [1,3,8,9]),两次请求中都有请求参数id:1,3 会造成1和3的并发问题。
处理方法就是针对具体请求的函数对单个参数进行加锁 当上架id:[1,3,5]请求的时候可以让1,3,5分别进行加锁,下一个1,3,8,9来请求的时候就会因为拿不到锁而报错
RedisLock

<?php namespace App\Http\Utils; use Illuminate\Support\Facades\Redis; class RedisLock { protected $systemName; protected $functionName; const KEY_PREFIX = 'lock'; const TTL = 60; protected $randNumMap = [];//直接存储的,只有php-fpm才建议使用,否则会出现内存溢出问题 public function __construct($systemName, $functionName) { $this->systemName = $systemName; $this->functionName = $functionName; } public static function initInstance($systemName, $functionName) { return new self($systemName, $functionName); } public function lock($key, $ttl = null) { $redis = Redis::connection(); $randNum = mt_rand(10000000, 99999999); $ttl = $ttl ?? self::TTL; $redisKey = self::KEY_PREFIX . ":{$this->systemName}:{$this->functionName}:{$key}"; $result = $redis->setNx($redisKey, $randNum); if ($result) { $redis->expire($redisKey, $ttl); $this->randNumMap[$key] = $randNum; return $randNum; } else { return null; } } public function unlock($key, $randNum) { $redis = Redis::connection(); $redisKey = self::KEY_PREFIX . ":{$this->systemName}:{$this->functionName}:{$key}"; $lua = <<<LUA if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end LUA; $result = $redis->eval($lua, 1, $redisKey, $randNum); return $result; } public function lockFunction($key, callable $callback,callable $failCallback = null) { $random = $this->lock($key); if ($random !== null) { $result = call_user_func($callback); $delResult = $this->unlock($key, $random); if ($delResult==0){ if ($failCallback){ call_user_func($failCallback); } } return $result; } else { return false; } } public function lockBatchKey($keyArr, $ttl = null) { $lockResult = []; foreach ($keyArr as $key) { $result = $this->lock($key, $ttl); if ($result) { $lockResult[$key] = $result; } else { $lockResult[$key] = 0; } } return $lockResult; } public function unlockBatchKey($resultArr) { $unlockResult = []; foreach ($resultArr as $key => $random) { $random = $this->randNumMap[$key] ?? $random; $result = $this->unlock($key, $random); if ($result) { $unlockResult[$key] = true; } else { $unlockResult[$key] = false; } } return $unlockResult; } }
本文来自博客园,作者:孙龙-程序员,转载请注明原文链接:https://www.cnblogs.com/sunlong88/p/18750146