├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── config └── lock.php └── src ├── Factory └── Factory.php ├── Lock.php ├── Lock └── Redis │ ├── Data.php │ └── Lock.php ├── LockContextInterface.php ├── LockException.php ├── LockInterface.php └── LockServiceProvider.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .git/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 zc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 环境要求 2 | 3 | 1.PHP >= 7 4 | 2.composer 5 | 3.redis(必须支持lua) 6 | 4.predis 7 | 8 | # composer 安装 9 | 10 | 移步 [composer中文网](https://www.phpcomposer.com/). 11 | # redis 安装 12 | 13 | [redis中文网](http://www.redis.net.cn/) 14 | 15 | # predis 安装 16 | composer require predis/predis 17 | # lock 安装 18 | 第一步, 安装alravel-lock 19 | composer require nabao/laravel-lock 20 | 第二步, 生成配置文件 21 | php artisan vendor:publish --provider="Lock\LockServiceProvider" 22 | # 抢占锁 23 | ## lock(callable $callback, string $lock_val) 24 | 多进程并发时, 其中某一个进程得到锁后, 其他进程将被拒绝 25 | 26 | 27 | $callback 28 | 回调函数, 可返回值 29 | $lock_val 30 | 锁定值 31 | # 多参数抢占锁 32 | ## lock(callable $callback, array $lock_vals) 33 | 多进程并发时, 其中某一个进程得到锁后, 其他进程将被拒绝 34 | 35 | 36 | $callback 37 | 回调函数, 可返回值 38 | $lock_vals 39 | 锁定值(数组) 40 | 41 | 42 | # 队列锁 43 | 44 | ## queueLock($closure, $lock_val, $max_queue_process = 100) 45 | 多进程并发时, 其中某一个进程得到锁后, 其他进程将等待解锁(配置最大等待进程后, 超过等待数量后进程将被拒绝) 46 | 47 | $callback 48 | 回调函数, 可返回值 49 | $lock_val 50 | 锁定值 51 | $max_queue_process 52 | 队列最大等待进程 53 | 54 | # 多参数队列锁 55 | 56 | ## queueLock($closure, $lock_vals, $max_queue_process = 100) 57 | 多进程并发时, 其中某一个进程得到锁后, 其他进程将等待解锁(配置最大等待进程后, 超过等待数量后进程将被拒绝) 58 | 59 | $callback 60 | 回调函数, 可返回值 61 | $lock_vals 62 | 锁定值(数组) 63 | $max_queue_process 64 | 队列最大等待进程 65 | 66 | # 使用 67 | 68 | //静态调用 69 | $lock_val = 'user:pay:1'; 70 | Lock::lock(function($redis){ 71 | echo 'hello world!'; 72 | }, $lock_val); 73 | 74 | //实例化调用 75 | $lock = new Lock(); 76 | $lock_val = 'user:pay:1'; 77 | $lock->lock(function($redis){ 78 | echo 'hello world!'; 79 | }, $lock_val); 80 | 81 | //多参数锁 82 | $lock = new Lock(); 83 | $lock_val[] = 'user:pay:1'; 84 | $lock_val[] = 'user:pay:2'; 85 | $lock->lock(function($redis){ 86 | echo 'hello world!'; 87 | }, $lock_val); 88 | 89 | 90 | # 限流 91 | 92 | ## isActionAllowed($key, $period, $max_count) 93 | 94 | $key 限制key 95 | $period 限制时间(秒) 96 | $max_count 限制时间内最大数量 97 | 98 | # config配置 99 | 100 | /* 101 | |-------------------------------------------------------------------------- 102 | | lock配置文件 103 | |-------------------------------------------------------------------------- 104 | | 105 | |drive 锁驱动(默认redis) 106 | | 107 | |redis redis驱动配置 108 | | host 地址 109 | | port 端口 110 | | 111 | |params 参数配置 112 | | max_queue_process 进程池最大进程 113 | | expiration 锁值过期时间 114 | | 115 | */ 116 | 'drive' => 'redis', 117 | 118 | 'redis' => [ 119 | 'host' => '127.0.0.1', 120 | 'port' => 6379, 121 | 'read_write_timeout' => 0, 122 | 'persistent' => true, 123 | ], 124 | 125 | 'params' => [ 126 | 'max_queue_process' => 100, 127 | 'expiration' => 5 128 | ] 129 | # laravel-lock 130 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nabao/laravel-lock", 3 | "description": "高性能, 分布式, 并发抢占锁, 队列锁", 4 | "keywords": ["lock", "并发锁"], 5 | "license": "MIT", 6 | "type": "library", 7 | "minimum-stability": "dev", 8 | "authors": [ 9 | { 10 | "name": "ZhaoCong", 11 | "qq":"1140253608", 12 | "email": "1140253608@qq.com" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=7.0", 17 | "predis/predis": "^1.1" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "Lock\\": "src/", 22 | "Lock\\Lock\\": "src/Lock", 23 | "Lock\\Factory\\": "src/Factory" 24 | } 25 | }, 26 | "extra": { 27 | "laravel": { 28 | "providers": [ 29 | "Lock\\LockServiceProvider" 30 | ] 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /config/lock.php: -------------------------------------------------------------------------------- 1 | 'redis', 6 | 7 | 'redis' => [ 8 | 'host' => env('REDIS_HOST', '127.0.0.1'), 9 | 'port' => env('REDIS_PORT', 6379), 10 | 'read_write_timeout' => 0, 11 | 'persistent' => true, 12 | 'parameters' =>[ 13 | 'password'=>env('REDIS_PASSWORD', null) 14 | ], 15 | ], 16 | 17 | 'params' => [ 18 | 'max_queue_process' => 100, 19 | 'expiration' => 5 20 | ] 21 | ]; 22 | -------------------------------------------------------------------------------- /src/Factory/Factory.php: -------------------------------------------------------------------------------- 1 | '127.0.0.1', 28 | 'port' => '6379', 29 | 'drive' => 'redis', 30 | ]; 31 | 32 | /** 33 | * Factory constructor. 34 | * @param array $config 35 | * @param array $params 36 | * @throws LockException 37 | */ 38 | public function __construct($config = [], $params = []) 39 | { 40 | $config = $this->getConfig($config); 41 | $params = $this->getParams($params); 42 | 43 | $this->getInstance($config, $params); 44 | } 45 | 46 | /** 47 | * @param $name 48 | * @param $arguments 49 | * @return mixed 50 | */ 51 | public function __call($name, $arguments) 52 | { 53 | // TODO: Implement __call() method. 54 | 55 | return call_user_func_array([$this->lock, $name], $arguments); 56 | } 57 | 58 | /** 59 | * 实例 60 | * @param $config 61 | * @param $params 62 | * @throws LockException 63 | */ 64 | private function getInstance($config = [], $params = []) 65 | { 66 | if ($this->lock) return $this->lock; 67 | 68 | switch ($this->drive){ 69 | case 'redis': 70 | $this->lock = new RedisLock($config, $params); 71 | break; 72 | default: 73 | throw new LockException('该驱动没有对应的类文件!'); 74 | } 75 | } 76 | 77 | /** 78 | * 获取配置文件 79 | * @param $config 80 | * @return array 81 | */ 82 | private function getConfig($config = []) 83 | { 84 | if ($this->config) return $this->config; 85 | 86 | if (!empty($config)) { 87 | $config = array_merge($this->default_config, $config); 88 | $this->drive = $config['drive']; 89 | 90 | return $this->config = $config[$this->drive]; 91 | } 92 | 93 | $this->drive = config('lock')['drive']; 94 | $this->config = config('lock')[$this->drive]; 95 | 96 | if (empty($this->config)){ 97 | $config = $this->default_config; 98 | $this->drive = $config['drive']; 99 | } 100 | 101 | return $this->config; 102 | } 103 | 104 | /** 105 | * @param array $params 106 | * @return array 107 | */ 108 | private function getParams($params = []) 109 | { 110 | if ($this->params) return $this->params; 111 | 112 | if (!empty($params)) { 113 | return $this->params = $params; 114 | } 115 | 116 | $this->params = config('lock')['params']; 117 | 118 | return $this->params; 119 | } 120 | } -------------------------------------------------------------------------------- /src/Lock.php: -------------------------------------------------------------------------------- 1 | manyInstance($config, $params); 27 | } 28 | 29 | /** 30 | * 单例 31 | * @param $name 32 | * @param $arguments 33 | * @throws LockException 34 | * @return mixed 35 | */ 36 | public static function __callStatic($name, $arguments) 37 | { 38 | $single = self::singleInstance(); 39 | 40 | return call_user_func_array([$single, $name], $arguments); 41 | } 42 | 43 | /** 44 | * 实例化调用 45 | * @param $name 46 | * @param $arguments 47 | * @return mixed 48 | */ 49 | public function __call($name, $arguments) 50 | { 51 | return call_user_func_array([$this->many, $name], $arguments); 52 | } 53 | 54 | /** 55 | * 单例工厂 56 | * @return Factory 57 | * @throws LockException 58 | */ 59 | private static function singleInstance() 60 | { 61 | if (empty(self::$single)){ 62 | self::$single = new Factory(); 63 | } 64 | 65 | return self::$single; 66 | } 67 | 68 | /** 69 | * 多例工厂 70 | * @param $config 71 | * @param $params 72 | * @return Factory 73 | * @throws LockException 74 | */ 75 | private function manyInstance($config, $params) 76 | { 77 | if (empty($this->many)){ 78 | $this->many = new Factory($config, $params); 79 | } 80 | 81 | return $this->many; 82 | } 83 | 84 | private function __clone(){} 85 | private function __wakeup(){} 86 | } 87 | -------------------------------------------------------------------------------- /src/Lock/Redis/Data.php: -------------------------------------------------------------------------------- 1 | $value){ 106 | $this->$key = $value; 107 | } 108 | } 109 | 110 | /** 111 | * 初始化lock数据 112 | * 113 | * @param $lock_val 114 | */ 115 | public function bootLock($lock_val) 116 | { 117 | $this->lock_val = $lock_val; 118 | 119 | $this->is_del_lock = true; 120 | $this->is_del_queue_lock_process = false; 121 | $this->is_del_queue_lock = false; 122 | 123 | $this->randNum(); 124 | 125 | $this->setLockName(); 126 | 127 | $this->setQueueLockListName(); 128 | } 129 | 130 | /** 131 | * 初始化queueLock数据 132 | * 133 | * @param $lock_val 134 | * @param null $max_queue_process 135 | * @param int $wait_timeout 136 | */ 137 | public function bootQueueLock($lock_val, $max_queue_process = null, $wait_timeout = 6) 138 | { 139 | $this->lock_val = $lock_val; 140 | $this->max_queue_process = $max_queue_process; 141 | $this->wait_timeout = $wait_timeout; 142 | 143 | $this->is_del_lock = false; 144 | $this->is_del_queue_lock_process = true; 145 | $this->is_del_queue_lock = true; 146 | 147 | $this->randNum(); 148 | 149 | $this->setQueueLockName(); 150 | 151 | $this->setQueueLockListName(); 152 | 153 | $this->setQueueLockProcessName(); 154 | } 155 | 156 | /** 157 | * 设置队列锁名称 158 | * 159 | * @return string 160 | */ 161 | private function setQueueLockName() 162 | { 163 | return $this->queue_lock_name = $this->queue_lock_prefix. ':' .$this->lock_val; 164 | } 165 | 166 | /** 167 | * 设置等待队列 168 | * @return string 169 | */ 170 | private function setQueueLockListName() 171 | { 172 | return $this->queue_lock_list_name = $this->queue_lock_prefix. ':list:' .$this->lock_val; 173 | } 174 | 175 | /** 176 | * 设置等待进程名称 177 | * 178 | * @return string 179 | */ 180 | private function setQueueLockProcessName() 181 | { 182 | return $this->queue_lock_process_name = $this->queue_lock_prefix. ':process:' .$this->lock_val; 183 | } 184 | 185 | /** 186 | * 设置lock名称 187 | * @return string 188 | */ 189 | private function setLockName() 190 | { 191 | return $this->lock_name = $this->lock_prefix. ':' .$this->lock_val; 192 | } 193 | 194 | /** 195 | * 生成随机函数 196 | * 197 | * @return string 198 | */ 199 | private function randNum() 200 | { 201 | return $this->rand_num = uniqid() . mt_rand(1, 1000000); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/Lock/Redis/Lock.php: -------------------------------------------------------------------------------- 1 | shutdown(); 65 | $this->bootSignals(); 66 | $this->initRedis($config); 67 | $this->initParams($params); 68 | } 69 | 70 | /** 71 | * 抢占锁 (适用于限制单个用户行为) 72 | * 此锁不会等待, 第一个锁用户没有处理完成, 第二个用户将被拒绝 73 | * @param $closure 74 | * @param $lock_val 75 | * @throws \Exception 76 | * @return mixed 77 | */ 78 | public function lock($closure, $lock_val) 79 | { 80 | $this->pushLockKey($lock_val); 81 | list($current_data, $current_index) = $this->bootLock($lock_val); 82 | 83 | if ($this->redis->set($current_data->lock_name, $current_data->rand_num, 'nx', 'ex', $current_data->expiration)) { 84 | try{ 85 | $closure_res = $closure($this->redis); 86 | $this->delLock($current_data); 87 | unset($this->locks[$current_index]); 88 | return $closure_res; 89 | }catch (\Error $exception){ 90 | $this->forcedShutdown(); 91 | throw $exception; 92 | }catch (\Exception $exception){ 93 | $this->forcedShutdown(); 94 | throw $exception; 95 | } 96 | }else{ 97 | throw new LockException(self::TOO_MANY_REQUESTS_ERROR_MSG, self::TOO_MANY_REQUESTS_ERROR_CODE); 98 | } 99 | } 100 | 101 | /** 102 | * 多参数抢占锁 103 | * @param $closure 104 | * @param $lock_vals 105 | * @return mixed 106 | */ 107 | public function locks($closure, $lock_vals) 108 | { 109 | $one_lock_val = array_pop($lock_vals); 110 | $one_closure = function ()use ($closure, $one_lock_val){ 111 | return $this->lock($closure, $one_lock_val); 112 | }; 113 | 114 | $go = empty($lock_vals) 115 | ? $one_closure 116 | : array_reduce($lock_vals, function ($next, $lock_val)use ($one_closure){ 117 | return function ()use ($next, $lock_val, $one_closure){ 118 | return is_null($next) 119 | ? $this->lock($one_closure, $lock_val) 120 | : $this->lock($next, $lock_val); 121 | }; 122 | }); 123 | 124 | return $go(); 125 | } 126 | 127 | /** 128 | * 队列锁 129 | * 此锁会等待, 第一个锁用户没有处理完成, 第二个用户将等待 130 | * @param $closure 131 | * @param $lock_val 132 | * @param int $max_queue_process 最大等待进程数 133 | * @param int $wait_timeout 队列等待过期时间 134 | * @throws \Exception 135 | * @return mixed 136 | */ 137 | public function queueLock($closure, $lock_val, $max_queue_process = null, $wait_timeout = 6) 138 | { 139 | $this->pushQueueLockKey($lock_val); 140 | list($current_data, $current_index) = $this->bootQueueLock($lock_val, $max_queue_process, $wait_timeout); 141 | $this->initQueueLockProcess($current_data); 142 | $queue_lock_list_name = $this->initQueueLockList($current_data); 143 | $this->addQueueLockProcess($current_data); 144 | 145 | loop: 146 | $wait = $this->redis->blpop($queue_lock_list_name, $wait_timeout); 147 | if (is_null($wait)) throw new LockException(self::TIME_OUT_ERROR_MSG, self::TIME_OUT_ERROR_CODE); 148 | 149 | if ($this->redis->set($current_data->queue_lock_name, $current_data->rand_num, 'nx', 'ex', $current_data->expiration)) { 150 | 151 | try{ 152 | $closure_res = $closure($this->redis); 153 | $this->delQueueLockProcess($current_data); 154 | $this->delQueueLock($current_data); 155 | $this->addQueueLockList($current_data); 156 | unset($this->queue_locks[$current_index]); 157 | return $closure_res; 158 | }catch (\Error $exception){ 159 | $this->forcedShutdown(); 160 | throw $exception; 161 | }catch (\Exception $exception){ 162 | $this->forcedShutdown(); 163 | throw $exception; 164 | } 165 | }else{ 166 | goto loop; 167 | } 168 | } 169 | 170 | /** 171 | * 多参数队列锁 172 | * @param $closure 173 | * @param $lock_vals 174 | * @param null $max_queue_process 175 | * @param int $wait_timeout 176 | * @return mixed 177 | */ 178 | public function queueLocks($closure, $lock_vals, $max_queue_process = null, $wait_timeout = 6) 179 | { 180 | $one_lock_val = array_pop($lock_vals); 181 | $one_closure = function ()use ($closure, $one_lock_val, $max_queue_process, $wait_timeout){ 182 | return $this->queueLock($closure, $one_lock_val, $max_queue_process, $wait_timeout); 183 | }; 184 | 185 | $go = empty($lock_vals) 186 | ? $one_closure 187 | : array_reduce($lock_vals, function ($next, $lock_val)use ($one_closure, $max_queue_process, $wait_timeout){ 188 | return function ()use ($next, $lock_val, $one_closure, $max_queue_process, $wait_timeout){ 189 | return is_null($next) 190 | ? $this->queueLock($one_closure, $lock_val, $max_queue_process, $wait_timeout) 191 | : $this->queueLock($next, $lock_val, $max_queue_process, $wait_timeout); 192 | }; 193 | }); 194 | return $go(); 195 | } 196 | 197 | 198 | /** 199 | * 简单限流 200 | * 作者地址: https://juejin.im/book/5afc2e5f6fb9a07a9b362527/section/5b4477416fb9a04fa259c496 201 | * @param $key 202 | * @param $period 203 | * @param $max_count 204 | * @return bool 205 | */ 206 | public function isActionAllowed($key, $period, $max_count) 207 | { 208 | $key = 'actionAllowed:'.$key; 209 | 210 | $msec_time = $this->getMsecTime(); 211 | 212 | list(,,$count) = $this->redis->pipeline(function ($pipe)use ($key, $msec_time, $period){ 213 | $pipe->zadd($key, $msec_time, $msec_time); 214 | $pipe->zremrangebyscore($key, 0, $msec_time - $period * 1000); 215 | $count = $pipe->zcard($key); 216 | $pipe->expire($key, $period + 1); 217 | return $count; 218 | }); 219 | 220 | return $count <= $max_count; 221 | } 222 | 223 | /** 224 | * 初始化等待锁的进程数量 225 | * 226 | * @param Data $data 227 | */ 228 | private function initQueueLockProcess(Data $data) 229 | { 230 | $lua = <<redis->eval($lua, 1, $data->queue_lock_process_name, $data->expiration); 243 | } 244 | 245 | /** 246 | * 初始化lock list 247 | * 248 | * @param Data $data 249 | * @return mixed 250 | */ 251 | private function initQueueLockList(Data $data) 252 | { 253 | $lua = <<redis->eval($lua, 2, $data->queue_lock_process_name, $data->queue_lock_list_name, $data->expiration); 268 | return $data->queue_lock_list_name; 269 | } 270 | 271 | /** 272 | * 新增等待进程 273 | * 274 | * @param Data $data 275 | * @throws LockException 276 | */ 277 | private function addQueueLockProcess(Data $data) 278 | { 279 | $lua = <<redis->eval($lua, 1, $data->queue_lock_process_name, $data->max_queue_process, $data->expiration)){ 296 | $data->is_del_queue_lock_process = false; 297 | throw new LockException(self::TOO_MANY_REQUESTS_ERROR_MSG, self::TOO_MANY_REQUESTS_ERROR_CODE); 298 | } 299 | } 300 | 301 | /** 302 | * 新增等待队列list 303 | * 304 | * @param Data $data 305 | */ 306 | private function addQueueLockList(Data $data) 307 | { 308 | $this->redis->lpush($data->queue_lock_list_name, true); 309 | $this->redis->expire($data->queue_lock_list_name, $data->expiration); 310 | } 311 | 312 | /** 313 | * 删除当前等待进程 314 | * 315 | * @param Data $data 316 | */ 317 | private function delQueueLockProcess(Data $data) 318 | { 319 | if ($data->is_del_queue_lock_process){ 320 | 321 | $lua = << 0) 326 | then 327 | redis.call('decr', queueLockProcessName) 328 | end 329 | LUA; 330 | $this->redis->eval($lua, 1, $data->queue_lock_process_name); 331 | 332 | $data->is_del_queue_lock_process = false; 333 | } 334 | } 335 | 336 | 337 | /** 338 | * 删除占用锁 339 | * 340 | * @param Data $data 341 | */ 342 | private function delLock(Data $data) 343 | { 344 | if ($data->is_del_lock && $data->lock_name){ 345 | $this->redis->eval($this->delLockLua(), 2, $data->lock_name, $data->rand_num); 346 | 347 | $data->is_del_lock = false; 348 | } 349 | } 350 | 351 | /** 352 | * 删除队列锁 353 | * 354 | * @param Data $data 355 | */ 356 | private function delQueueLock(Data $data) 357 | { 358 | if ($data->is_del_queue_lock && $data->queue_lock_name){ 359 | $this->redis->eval($this->delLockLua(), 2, $data->queue_lock_name, $data->rand_num); 360 | 361 | $data->is_del_queue_lock = false; 362 | } 363 | } 364 | 365 | /** 366 | * @return string 367 | */ 368 | private function delLockLua() 369 | { 370 | return <<redis = new Client($config); 390 | } 391 | 392 | /** 393 | * 初始化参数 394 | * @param $params 395 | */ 396 | private function initParams($params) 397 | { 398 | if (!empty($params)){ 399 | foreach ($params as $key => $item){ 400 | $this->$key = $item; 401 | } 402 | } 403 | } 404 | 405 | /** 406 | * 防止其他错误 407 | */ 408 | private function shutdown() 409 | { 410 | register_shutdown_function(function (){ 411 | $this->forcedShutdown(); 412 | }); 413 | } 414 | 415 | /** 416 | * 强制关闭 417 | * 418 | */ 419 | private function forcedShutdown() 420 | { 421 | if (isset($this->locks)){ 422 | 423 | array_walk($this->locks, function ($data){ 424 | $this->delLock($data); 425 | }); 426 | 427 | unset($this->locks); 428 | } 429 | 430 | if (isset($this->queue_locks)){ 431 | 432 | array_walk($this->queue_locks, function ($data){ 433 | $this->delQueueLock($data); 434 | $this->delQueueLockProcess($data); 435 | }); 436 | 437 | unset($this->queue_locks); 438 | } 439 | } 440 | 441 | /** 442 | * 初始化lock数据对象 443 | * 444 | * @param $lock_val 445 | * @return array 446 | */ 447 | private function bootLock($lock_val) 448 | { 449 | $data = new Data( 450 | [ 451 | 'expiration'=>$this->expiration 452 | ] 453 | ); 454 | 455 | $data->bootLock($lock_val); 456 | 457 | $this->locks[] = $data; 458 | 459 | $current_index = count($this->locks) - 1; 460 | 461 | return [$data, $current_index]; 462 | } 463 | 464 | /** 465 | * 初始化queueLock数据对象 466 | * 467 | * @param $lock_val 468 | * @param null $max_queue_process 469 | * @param int $wait_timeout 470 | * @return array 471 | */ 472 | private function bootQueueLock($lock_val, $max_queue_process = null, $wait_timeout = 6) 473 | { 474 | $data = new Data( 475 | [ 476 | 'expiration'=>$this->expiration 477 | ] 478 | ); 479 | 480 | $data->bootQueueLock($lock_val, $max_queue_process, $wait_timeout); 481 | 482 | $this->queue_locks[] = $data; 483 | 484 | $current_index = count($this->queue_locks) - 1; 485 | 486 | return [$data, $current_index]; 487 | } 488 | 489 | /** 490 | * 将key放入keys 491 | * 492 | * @param $lock_val 493 | * @throws LockException 494 | */ 495 | private function pushQueueLockKey($lock_val) 496 | { 497 | if (isset($this->queue_lock_keys[$lock_val])){ 498 | 499 | throw new LockException(self::EXISTED_ERROR_MSG, self::PARAMS_ERROR_CODE); 500 | } 501 | 502 | $this->queue_lock_keys[$lock_val] = true; 503 | } 504 | 505 | /** 506 | * 将key放入keys 507 | * 508 | * @param $lock_val 509 | * @throws LockException 510 | */ 511 | private function pushLockKey($lock_val) 512 | { 513 | if (isset($this->lock_keys[$lock_val])){ 514 | 515 | throw new LockException(self::EXISTED_ERROR_MSG, self::PARAMS_ERROR_CODE); 516 | } 517 | 518 | $this->lock_keys[$lock_val] = true; 519 | } 520 | 521 | /** 522 | * 获取毫秒时间 523 | * @return int 524 | */ 525 | private function getMsecTime() 526 | { 527 | list($msec, $sec) = explode(' ', microtime()); 528 | $msec_time = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000); 529 | return (int)explode('.', $msec_time)[0]; 530 | } 531 | 532 | /** 533 | * 注册信号 534 | */ 535 | private function bootSignals() 536 | { 537 | if (function_exists('pcntl_async_signals')){ 538 | \pcntl_async_signals(true); 539 | 540 | } 541 | 542 | if (function_exists('pcntl_signal')){ 543 | \pcntl_signal(SIGINT, function(){ 544 | $this->forcedShutdown(); 545 | }); 546 | } 547 | 548 | // pcntl_signal(SIGHUP, function(){ 549 | // $this->forcedShutdown(); 550 | // }); 551 | 552 | // pcntl_signal(SIGTERM, function(){ 553 | // $this->forcedShutdown(); 554 | // }); 555 | } 556 | 557 | /** 558 | * 删除锁 559 | * 防止程序中止后没解锁 560 | */ 561 | public function __destruct() 562 | { 563 | // TODO: Implement __destruct() method. 564 | $this->forcedShutdown(); 565 | } 566 | } 567 | -------------------------------------------------------------------------------- /src/LockContextInterface.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | interface LockContextInterface{} -------------------------------------------------------------------------------- /src/LockException.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 17 | $this->publishes([ 18 | __DIR__.'/../config/lock.php' => $this->app['path.config'].DIRECTORY_SEPARATOR.'lock.php', 19 | ]); 20 | } 21 | } 22 | } 23 | --------------------------------------------------------------------------------