├── .gitignore ├── composer.json ├── phpunit.xml ├── readme.md └── src ├── Latrell └── Lock │ ├── Console │ └── ClearCommand.php │ ├── Facades │ └── Lock.php │ ├── FileStore.php │ ├── GranuleStore.php │ ├── LockInterface.php │ ├── LockManager.php │ ├── LockServiceProvider.php │ ├── MemcachedStore.php │ ├── Middleware │ └── SynchronizationRequests.php │ ├── NullStore.php │ └── RedisStore.php └── config └── config.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | /.settings 6 | /.buildpath 7 | /.project 8 | .idea -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "latrell/lock", 3 | "description" : "支持Redis与Memcached的并发锁。", 4 | "keywords" : [ 5 | "Lock", 6 | "L5", 7 | "Laravel 5", 8 | "Laravel" 9 | ], 10 | "license" : "MIT", 11 | "authors" : [{ 12 | "name" : "Latrell Chan", 13 | "email" : "i@latrell.me" 14 | } 15 | ], 16 | "require" : { 17 | "php" : ">=5.5.9" 18 | }, 19 | "require-dev" : { 20 | "illuminate/support" : "5.*" 21 | }, 22 | "extra" : { 23 | "laravel" : { 24 | "providers" : [ 25 | "Latrell\\Lock\\LockServiceProvider" 26 | ], 27 | "aliases" : { 28 | "Lock" : "Latrell\\Lock\\Facades\\Lock" 29 | } 30 | } 31 | }, 32 | "autoload" : { 33 | "psr-0" : { 34 | "Latrell\\Lock\\" : "src/" 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Lock 2 | ====== 3 | 4 | 这是一个支持 Laravel 5 的并发锁拓展包。 5 | 6 | 该模块在 Redis 与 Memcache 上实现了锁机制。 7 | 8 | 注意:集群环境下,必须使用 Redis 驱动,否则由于 Memcache 的特性,锁可能出现上锁不准确的情况。 9 | 10 | ## 安装 11 | 12 | ``` 13 | composer require latrell/lock dev-master 14 | ``` 15 | 16 | 使用 ```composer update``` 更新包列表,或使用 ```composer install``` 安装。 17 | 18 | 找到 `config/app.php` 配置文件中的 `providers` 键,注册服务提供者。 19 | 20 | (Laravel 5.5 以上版本可跳过该步骤) 21 | 22 | ```php 23 | 'providers' => [ 24 | // ... 25 | Latrell\Lock\LockServiceProvider::class, 26 | ] 27 | ``` 28 | 29 | 找到 `config/app.php` 配置文件中的 `aliases` 键,注册别名。 30 | 31 | ```php 32 | 'aliases' => [ 33 | // ... 34 | 'Lock' => Latrell\Lock\Facades\Lock::class, 35 | ] 36 | ``` 37 | 38 | 运行 `php artisan vendor:publish` 命令,发布配置文件到你的项目中。 39 | 40 | ## 使用 41 | 42 | ### 闭包方式 43 | 44 | 使用闭包的方式,可由方法内自动处理异常,并自动解锁,防止死锁。 45 | 46 | 但需注意,外部变量需要使用 `use` 引入才可在闭包中使用。 47 | 48 | ```php 49 | // 防止商品超卖。 50 | $key = 'Goods:' . $goods_id; 51 | Lock::granule($key, function() use($goods_id) { 52 | $goods = Goods::find($goods_id); 53 | if ( $goods->stock > 0 ) { 54 | // ... 55 | } 56 | }); 57 | ``` 58 | 59 | ### 普通方式 60 | 61 | 提供手动上锁与解锁方式,方便应用在复杂环境。 62 | 63 | ```php 64 | 65 | // 锁名称。 66 | $key = 'Goods:' . $goods_id; 67 | 68 | // **注意:除非特别自信,否则一定要记得捕获异常,保证解锁动作。** 69 | try { 70 | 71 | // 上锁。 72 | Lock::acquire($key); 73 | 74 | // 逻辑单元。 75 | $goods = Goods::find($goods_id); 76 | if ( $goods->stock > 0 ) { 77 | // ... 78 | } 79 | } finally { 80 | // 解锁。 81 | Lock::release($key); 82 | } 83 | ``` 84 | 85 | ### 中间件 86 | 87 | 使用中间件的方式,让两个相同指纹的请求同步执行。 88 | 89 | 找到 `app/Http/Kernel.php` 中的 `$routeMiddleware` 配置,添加中间件配置。 90 | 91 | ``` 92 | protected $routeMiddleware = [ 93 | // ... 94 | 'synchronized' => \Latrell\Lock\Middleware\SynchronizationRequests::class, 95 | ]; 96 | ``` 97 | -------------------------------------------------------------------------------- /src/Latrell/Lock/Console/ClearCommand.php: -------------------------------------------------------------------------------- 1 | lock = $lock; 42 | } 43 | 44 | /** 45 | * Execute the console command. 46 | * 47 | * @return void 48 | */ 49 | public function handle() 50 | { 51 | $timeout = $this->option('timeout'); 52 | $store_name = $this->option('store'); 53 | 54 | $this->laravel['events']->fire('lock:clearing', [ 55 | $store_name 56 | ]); 57 | 58 | $num = $this->lock->store($store_name)->clear($timeout); 59 | 60 | $this->laravel['events']->fire('lock:cleared', [ 61 | $store_name 62 | ]); 63 | 64 | $this->info("cleared {$num} lock!"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Latrell/Lock/Facades/Lock.php: -------------------------------------------------------------------------------- 1 | files = $files; 68 | $this->directory = $directory; 69 | $this->timeout = $timeout; 70 | $this->max_timeout = $max_timeout; 71 | $this->retry_wait_usec = $retry_wait_usec; 72 | $this->identifier = md5(uniqid(gethostname(), true)); 73 | } 74 | 75 | /** 76 | * 上锁 77 | * 78 | * @param string $key 79 | * @return boolean 80 | */ 81 | public function acquire($name) 82 | { 83 | $key = $this->getKey($name); 84 | 85 | // 取得锁文件路径。 86 | $file = $this->directory . '/' . $key; 87 | 88 | $time = time(); 89 | while (time() - $time < $this->max_timeout) { 90 | 91 | // 删除超时的锁文件。 92 | try { 93 | $current_value = $this->files->get($key); 94 | if (! is_null($current_value) && $this->hasLockValueExpired($current_value)) { 95 | $this->files->delete($file); 96 | } 97 | } catch (FileNotFoundException $e) {} 98 | 99 | // 检查锁文件是否存在。。 100 | if (! $this->files->exists($file)) { 101 | 102 | // 创建文件锁目录。 103 | if (! $this->files->exists($this->directory)) { 104 | $this->files->makeDirectory($this->directory, 0777, true, true); 105 | } 106 | 107 | // 创建锁文件。 108 | $value = $this->getLockExpirationValue(); 109 | if ($this->files->put($file, $value, true)) { 110 | // 加锁成功。 111 | $this->acquired($name); 112 | return true; 113 | } 114 | } 115 | 116 | usleep($this->retry_wait_usec); 117 | } 118 | return false; 119 | } 120 | 121 | /** 122 | * 记录该锁的到期时间 123 | */ 124 | protected function acquired($name) 125 | { 126 | $this->expires_at[$name] = Carbon::now()->addSeconds($this->timeout); 127 | } 128 | 129 | /** 130 | * 解锁 131 | * @param unknown $key 132 | */ 133 | public function release($name) 134 | { 135 | $key = $this->getKey($name); 136 | 137 | if (! $this->isLocked($name)) { 138 | throw new RuntimeException('Attempting to release a lock that is not held'); 139 | } 140 | 141 | // 取得锁文件路径。 142 | $file = $this->directory . '/' . $key; 143 | 144 | try { 145 | $value = $this->files->get($file); 146 | 147 | unset($this->expires_at[$name]); // 释放内存占用。 148 | if (! $this->hasLockValueExpired($value)) { 149 | $this->files->delete($file); // 释放锁。 150 | } else { 151 | trigger_error(sprintf('A FileLock was not released before the timeout. Class: %s Lock Name: %s', get_class($this), $name), E_USER_WARNING); 152 | } 153 | } catch (FileNotFoundException $e) { 154 | trigger_error(sprintf('Attempting to release a lock that is not held. Class: %s Lock Name: %s', get_class($this), $name), E_USER_WARNING); 155 | } 156 | } 157 | 158 | /** 159 | * 我们有一个锁? 160 | */ 161 | protected function isLocked($name) 162 | { 163 | return key_exists($name, $this->expires_at); 164 | } 165 | 166 | /** 167 | * 取得用于该锁的Key。 168 | */ 169 | protected function getKey($name) 170 | { 171 | return $this->prefix . md5($name); 172 | } 173 | 174 | /** 175 | * 取得锁的值。 176 | * 添加到期时间与识别码。 177 | * 178 | * @return string 179 | */ 180 | protected function getLockExpirationValue() 181 | { 182 | return serialize([ 183 | 'identifier' => $this->identifier, 184 | 'expires_at' => Carbon::now()->addSeconds($this->timeout) 185 | ]); 186 | } 187 | 188 | /** 189 | * 确定一个锁已过期。 190 | * 191 | * @param string 锁的值 192 | * @return boolean 193 | */ 194 | protected function hasLockValueExpired($value) 195 | { 196 | $data = @unserialize($value); 197 | if (! $data) { 198 | return true; 199 | } 200 | return Carbon::now() > $data['expires_at']; 201 | } 202 | 203 | /** 204 | * 清理过期的死锁 205 | * 206 | * @return integer 清理的死锁数量 207 | */ 208 | public function clear() 209 | { 210 | $files = $this->files->files($this->directory); 211 | $num = 0; 212 | foreach ($files as $file) { 213 | if (! starts_with($file, $this->getPrefix())) { 214 | continue; 215 | } 216 | $value = $this->files->get($file); 217 | if ($this->hasLockValueExpired($value)) { 218 | $this->files->delete($file); 219 | $num ++; 220 | } 221 | } 222 | return $num; 223 | } 224 | 225 | /** 226 | * Get the Filesystem instance. 227 | * 228 | * @return \Illuminate\Filesystem\Filesystem 229 | */ 230 | public function getFilesystem() 231 | { 232 | return $this->files; 233 | } 234 | 235 | /** 236 | * Get the working directory of the lock. 237 | * 238 | * @return string 239 | */ 240 | public function getDirectory() 241 | { 242 | return $this->directory; 243 | } 244 | 245 | /** 246 | * Get the lock key prefix. 247 | * 248 | * @return string 249 | */ 250 | public function getPrefix() 251 | { 252 | return ''; 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/Latrell/Lock/GranuleStore.php: -------------------------------------------------------------------------------- 1 | acquire($key)) { 14 | return $callback(); 15 | } else { 16 | throw new RuntimeException("Acquire lock key {$key} timeout!"); 17 | } 18 | } finally { 19 | $this->release($key); 20 | } 21 | } 22 | 23 | /** 24 | * [synchronized alias of function granule] 25 | */ 26 | public function synchronized($key, Closure $callback) 27 | { 28 | return $this->granule($key, $callback); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Latrell/Lock/LockInterface.php: -------------------------------------------------------------------------------- 1 | app = $app; 41 | } 42 | 43 | /** 44 | * Get a lock store instance by name. 45 | * 46 | * @param string|null $name 47 | * @return mixed 48 | */ 49 | public function store($name = null) 50 | { 51 | $name = $name ? : $this->getDefaultDriver(); 52 | 53 | return $this->stores[$name] = $this->get($name); 54 | } 55 | 56 | /** 57 | * Get a lock driver instance. 58 | * 59 | * @param string $driver 60 | * @return mixed 61 | */ 62 | public function driver($driver = null) 63 | { 64 | return $this->store($driver); 65 | } 66 | 67 | /** 68 | * Attempt to get the store from the local lock. 69 | * 70 | * @param string $name 71 | * @return \Latrell\Lock\LockInterface 72 | */ 73 | protected function get($name) 74 | { 75 | return isset($this->stores[$name]) ? $this->stores[$name] : $this->resolve($name); 76 | } 77 | 78 | /** 79 | * Resolve the given store. 80 | * 81 | * @param string $name 82 | * @return \Latrell\Lock\LockInterface 83 | * 84 | * @throws \InvalidArgumentException 85 | */ 86 | protected function resolve($name) 87 | { 88 | $config = $this->getConfig($name); 89 | 90 | if (is_null($config)) { 91 | throw new InvalidArgumentException("Lock store [{$name}] is not defined."); 92 | } 93 | 94 | if (isset($this->customCreators[$config['driver']])) { 95 | return $this->callCustomCreator($config); 96 | } else { 97 | $driverMethod = 'create' . ucfirst($config['driver']) . 'Driver'; 98 | 99 | if (method_exists($this, $driverMethod)) { 100 | return $this->{$driverMethod}($config); 101 | } else { 102 | throw new InvalidArgumentException("Driver [{$config['driver']}] is not supported."); 103 | } 104 | } 105 | } 106 | 107 | /** 108 | * Call a custom driver creator. 109 | * 110 | * @param array $config 111 | * @return mixed 112 | */ 113 | protected function callCustomCreator(array $config) 114 | { 115 | return $this->customCreators[$config['driver']]($this->app, $config); 116 | } 117 | 118 | /** 119 | * Create an instance of the file lock driver. 120 | * 121 | * @param array $config 122 | * @return \Latrell\Lock\FileStore 123 | */ 124 | protected function createFileDriver(array $config) 125 | { 126 | $timeout = $this->getTimeout($config); 127 | $max_timeout = $this->getMaxTimeout($config); 128 | $retry_wait_usec = $this->getRetryWaitUsec($config); 129 | 130 | return new FileStore($this->app['files'], $config['path'], $timeout, $max_timeout, $retry_wait_usec); 131 | } 132 | 133 | /** 134 | * Create an instance of the Memcached lock driver. 135 | * 136 | * @param array $config 137 | * @return \Latrell\Lock\MemcachedStore 138 | */ 139 | protected function createMemcachedDriver(array $config) 140 | { 141 | $prefix = $this->getPrefix($config); 142 | $timeout = $this->getTimeout($config); 143 | $max_timeout = $this->getMaxTimeout($config); 144 | $retry_wait_usec = $this->getRetryWaitUsec($config); 145 | 146 | $memcached = $this->app['memcached.connector']->connect($config['servers']); 147 | 148 | return new MemcachedStore($memcached, $prefix, $timeout, $max_timeout, $retry_wait_usec); 149 | } 150 | 151 | /** 152 | * Create an instance of the Null lock driver. 153 | * 154 | * @return \Latrell\Lock\NullStore 155 | */ 156 | protected function createNullDriver() 157 | { 158 | return new NullStore(); 159 | } 160 | 161 | /** 162 | * Create an instance of the Redis lock driver. 163 | * 164 | * @param array $config 165 | * @return \Latrell\Lock\RedisStore 166 | */ 167 | protected function createRedisDriver(array $config) 168 | { 169 | $redis = $this->app['redis']; 170 | 171 | $connection = Arr::get($config, 'connection', 'default'); 172 | 173 | $timeout = $this->getTimeout($config); 174 | $max_timeout = $this->getMaxTimeout($config); 175 | $retry_wait_usec = $this->getRetryWaitUsec($config); 176 | 177 | return new RedisStore($redis, $this->getPrefix($config), $connection, $timeout, $max_timeout, $retry_wait_usec); 178 | } 179 | 180 | /** 181 | * Get the lock prefix. 182 | * 183 | * @param array $config 184 | * @return string 185 | */ 186 | protected function getPrefix(array $config) 187 | { 188 | return Arr::get($config, 'prefix') ? : $this->app['config']['lock.prefix']; 189 | } 190 | 191 | /** 192 | * Get the lock timeout. 193 | * 194 | * @param array $config 195 | * @return string 196 | */ 197 | protected function getTimeout(array $config) 198 | { 199 | return Arr::get($config, 'timeout') ? : $this->app['config']['lock.timeout']; 200 | } 201 | 202 | /** 203 | * Get the lock max_timeout. 204 | * 205 | * @param array $config 206 | * @return string 207 | */ 208 | protected function getMaxTimeout(array $config) 209 | { 210 | return Arr::get($config, 'max_timeout') ? : $this->app['config']['lock.max_timeout']; 211 | } 212 | 213 | /** 214 | * Get the lock retry_wait_usec. 215 | * 216 | * @param array $config 217 | * @return string 218 | */ 219 | protected function getRetryWaitUsec(array $config) 220 | { 221 | return Arr::get($config, 'retry_wait_usec') ? : $this->app['config']['lock.retry_wait_usec']; 222 | } 223 | 224 | /** 225 | * Get the lock connection configuration. 226 | * 227 | * @param string $name 228 | * @return array 229 | */ 230 | protected function getConfig($name) 231 | { 232 | return $this->app['config']["lock.stores.{$name}"]; 233 | } 234 | 235 | /** 236 | * Get the default lock driver name. 237 | * 238 | * @return string 239 | */ 240 | public function getDefaultDriver() 241 | { 242 | return $this->app['config']['lock.default']; 243 | } 244 | 245 | /** 246 | * Set the default lock driver name. 247 | * 248 | * @param string $name 249 | * @return void 250 | */ 251 | public function setDefaultDriver($name) 252 | { 253 | $this->app['config']['lock.default'] = $name; 254 | } 255 | 256 | /** 257 | * Register a custom driver creator Closure. 258 | * 259 | * @param string $driver 260 | * @param \Closure $callback 261 | * @return $this 262 | */ 263 | public function extend($driver, Closure $callback) 264 | { 265 | $this->customCreators[$driver] = $callback; 266 | 267 | return $this; 268 | } 269 | 270 | /** 271 | * Dynamically call the default driver instance. 272 | * 273 | * @param string $method 274 | * @param array $parameters 275 | * @return mixed 276 | */ 277 | public function __call($method, $parameters) 278 | { 279 | return call_user_func_array([ 280 | $this->store(), 281 | $method 282 | ], $parameters); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/Latrell/Lock/LockServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 25 | __DIR__ . '/../../config/config.php' => config_path('lock.php') 26 | ]); 27 | } 28 | 29 | /** 30 | * Register the service provider. 31 | * 32 | * @return void 33 | */ 34 | public function register() 35 | { 36 | $this->mergeConfigFrom(__DIR__ . '/../../config/config.php', 'lock'); 37 | 38 | $this->app->singleton('lock', function ($app) { 39 | return new LockManager($app); 40 | }); 41 | 42 | $this->app->singleton('lock.store', function ($app) { 43 | return $app['lock']->driver(); 44 | }); 45 | 46 | $this->registerCommands(); 47 | } 48 | 49 | /** 50 | * Register the lock related console commands. 51 | * 52 | * @return void 53 | */ 54 | public function registerCommands() 55 | { 56 | $this->app->singleton('command.lock.clear', function ($app) { 57 | return new ClearCommand($app['lock']); 58 | }); 59 | 60 | $this->commands('command.lock.clear'); 61 | } 62 | 63 | /** 64 | * Get the services provided by the provider. 65 | * 66 | * @return array 67 | */ 68 | public function provides() 69 | { 70 | return [ 71 | 'lock', 72 | 'lock.store', 73 | 'command.lock.clear' 74 | ]; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Latrell/Lock/MemcachedStore.php: -------------------------------------------------------------------------------- 1 | setPrefix($prefix); 53 | $this->memcached = $memcached; 54 | $this->timeout = $timeout; 55 | $this->max_timeout = $max_timeout; 56 | $this->retry_wait_usec = $retry_wait_usec; 57 | $this->Identifier = md5(uniqid(gethostname(), true)); 58 | } 59 | 60 | /** 61 | * 上锁 62 | * 63 | * @param string $key 64 | * @return boolean 65 | */ 66 | public function acquire($key) 67 | { 68 | $key = $this->prefix . $key; 69 | $time = time(); 70 | while (time() - $time < $this->max_timeout) { 71 | if ($this->memcached->add($key, $this->Identifier, $this->timeout)) { 72 | return true; 73 | } 74 | usleep($this->retry_wait_usec); 75 | } 76 | return false; 77 | } 78 | 79 | /** 80 | * 解锁 81 | * @param unknown $key 82 | */ 83 | public function release($key) 84 | { 85 | $key = $this->prefix . $key; 86 | if ($this->memcached->get($key) === $this->Identifier) { 87 | $this->memcached->delete($key); 88 | } 89 | } 90 | 91 | /** 92 | * 清理过期的死锁 93 | * 94 | * @return integer 清理的死锁数量 95 | */ 96 | public function clear() 97 | { 98 | // 由Memcache自动管理。 99 | return 0; 100 | } 101 | 102 | /** 103 | * Get the underlying Memcached connection. 104 | * 105 | * @return \Memcached 106 | */ 107 | public function getMemcached() 108 | { 109 | return $this->memcached; 110 | } 111 | 112 | /** 113 | * Get the lock key prefix. 114 | * 115 | * @return string 116 | */ 117 | public function getPrefix() 118 | { 119 | return $this->prefix; 120 | } 121 | 122 | /** 123 | * Set the lock key prefix. 124 | * 125 | * @param string $prefix 126 | * @return void 127 | */ 128 | public function setPrefix($prefix) 129 | { 130 | $this->prefix = ! empty($prefix) ? $prefix . ':' : ''; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/Latrell/Lock/Middleware/SynchronizationRequests.php: -------------------------------------------------------------------------------- 1 | resolveRequestSignature($request); 21 | $lock = app('lock'); 22 | try { 23 | $lock->acquire($key); 24 | $response = $next($request); 25 | } finally { 26 | $lock->release($key); 27 | } 28 | return $response; 29 | } 30 | 31 | /** 32 | * Resolve request signature. 33 | * 34 | * @param \Illuminate\Http\Request $request 35 | * @return string 36 | */ 37 | protected function resolveRequestSignature($request) 38 | { 39 | return $request->fingerprint(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Latrell/Lock/NullStore.php: -------------------------------------------------------------------------------- 1 | redis = $redis; 56 | $this->setPrefix($prefix); 57 | $this->connection = $connection; 58 | $this->timeout = $timeout; 59 | $this->max_timeout = $max_timeout; 60 | $this->retry_wait_usec = $retry_wait_usec; 61 | } 62 | 63 | /** 64 | * 上锁 65 | * 66 | * @param string $key 67 | * @return boolean 68 | */ 69 | public function acquire($name) 70 | { 71 | $key = $this->getKey($name); 72 | 73 | $time = time(); 74 | while (time() - $time < $this->max_timeout) { 75 | $lockValue = time() + $this->timeout; 76 | if ($this->connection()->set($key, $lockValue, "EX", $this->timeout, "NX")) { 77 | // 加锁成功。 78 | return true; 79 | } 80 | 81 | // 未能加锁成功。 82 | // 检查当前锁是否已过期,并重新锁定。 83 | if ($this->connection()->get($key) < time() && $this->connection()->getset($key, $lockValue) < time()) { 84 | $this->connection()->expire($key, $this->timeout); 85 | return true; 86 | } 87 | usleep($this->retry_wait_usec); 88 | } 89 | return false; 90 | } 91 | 92 | /** 93 | * 解锁 94 | * 95 | * @param string $key 96 | */ 97 | public function release($name) 98 | { 99 | $key = $this->getKey($name); 100 | 101 | if($this->connection()->ttl($key)) { 102 | $this->connection()->del($key); 103 | } 104 | } 105 | 106 | /** 107 | * 取得用于该锁的Key。 108 | */ 109 | protected function getKey($name) 110 | { 111 | return $this->prefix . $name; 112 | } 113 | 114 | /** 115 | * 清理过期的死锁 116 | * 117 | * @return integer 清理的死锁数量 118 | */ 119 | public function clear() 120 | { 121 | return 0; 122 | } 123 | 124 | /** 125 | * Get the Redis connection instance. 126 | * 127 | * @return \Predis\ClientInterface 128 | */ 129 | public function connection() 130 | { 131 | return $this->redis->connection($this->connection); 132 | } 133 | 134 | /** 135 | * Set the connection name to be used. 136 | * 137 | * @param string $connection 138 | * @return void 139 | */ 140 | public function setConnection($connection) 141 | { 142 | $this->connection = $connection; 143 | } 144 | 145 | /** 146 | * Get the Redis database instance. 147 | * 148 | * @return \Illuminate\Redis\Database 149 | */ 150 | public function getRedis() 151 | { 152 | return $this->redis; 153 | } 154 | 155 | /** 156 | * Get the lock key prefix. 157 | * 158 | * @return string 159 | */ 160 | public function getPrefix() 161 | { 162 | return $this->prefix; 163 | } 164 | 165 | /** 166 | * Set the lock key prefix. 167 | * 168 | * @param string $prefix 169 | * @return void 170 | */ 171 | public function setPrefix($prefix) 172 | { 173 | $this->prefix = ! empty($prefix) ? $prefix . ':' : ''; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/config/config.php: -------------------------------------------------------------------------------- 1 | env('LOCK_DRIVER', 'redis'), 6 | 7 | 'stores' => [ 8 | 9 | 'file' => [ 10 | 'driver' => 'file', 11 | 'path' => storage_path('framework/lock') 12 | ], 13 | 14 | 'memcached' => [ 15 | 'driver' => 'memcached', 16 | 'servers' => [ 17 | [ 18 | 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 19 | 'port' => env('MEMCACHED_PORT', 11211), 20 | 'weight' => 100 21 | ] 22 | ] 23 | ], 24 | 25 | 'redis' => [ 26 | 'driver' => 'redis', 27 | 'connection' => 'default' 28 | ] 29 | ], 30 | 31 | 'prefix' => 'lock', 32 | 33 | // 锁超时时间(秒) 34 | 'timeout' => 30, 35 | 36 | // 上锁最大超时时间(秒) 37 | 'max_timeout' => 300, 38 | 39 | // 重试等待时间(微秒) 40 | 'retry_wait_usec' => 100000 41 | ]; 42 | --------------------------------------------------------------------------------