锁(Lock),在并发处理,防止冲突的场景非常常用。

在 imi 中,你可以使用注解,或者自己实例化Lock类来实现加锁处理。

除了内置的锁驱动外,你可以实现Imi\Lock\Handler\ILockHandler接口,来实现其他方式的锁。

配置用法

需要在配置中预定义

配置说明

  1. // 锁
  2. 'lock' =>[
  3. 'list' => [
  4. // 锁 ID => 配置
  5. 'redis' => [
  6. 'class' => 'RedisLock', // Handler 类 Bean 名或完整类名
  7. 'options' => [
  8. // Handler 类所需配置
  9. 'poolName' => 'redis_test',
  10. ],
  11. ],
  12. ],
  13. ],

使用说明

顺序用法:

  1. use Imi\Lock\Lock;
  2. $lockId = ''; // 你定义的ID
  3. if(Lock::lock($lockId))
  4. {
  5. try {
  6. // 干一些事情
  7. } catch(\Throwable $th) {
  8. throw $th;
  9. } finally {
  10. Lock::unlock($lockId);
  11. }
  12. }

回调用法(无需手动释放锁):

  1. $result = Lock::lock($lockId, function(){
  2. // 执行任务
  3. }, function(){
  4. // return 非null则不执行任务
  5. // 一般用于防止缓存击穿
  6. // 这个回调可以不传
  7. });
  8. if($result)
  9. {
  10. // 加锁并执行成功
  11. }
  12. else
  13. {
  14. // 加锁失败
  15. }

获取 Handler 对象:

  1. $lock = Lock::getInstance($lockId);
  2. $lock->lock(); // 使用方法参考下面的“实例化使用方法”

注解使用

@Lockable

无需在配置中预定义

支持参数:

  1. /**
  2. * 锁ID
  3. * 支持{id}、{data.name}形式,代入参数
  4. * 如果为null,则使用类名+方法名+全部参数,序列化后hash
  5. *
  6. * @var string|null
  7. */
  8. public $id;
  9. /**
  10. * 锁类型,如:RedisLock
  11. * 为null则使用默认锁类型(@currentServer.lock.defaultType)
  12. *
  13. * @var string|null
  14. */
  15. public $type;
  16. /**
  17. * 等待锁超时时间,单位:毫秒,0为不限制
  18. *
  19. * @var int
  20. */
  21. public $waitTimeout = 3000;
  22. /**
  23. * 锁超时时间,单位:毫秒
  24. *
  25. * @var int
  26. */
  27. public $lockExpire = 3000;
  28. /**
  29. * 锁初始化参数
  30. *
  31. * @var array
  32. */
  33. public $options = [];
  34. /**
  35. * 当获得锁后执行的回调。该回调返回非 null 则不执行加锁后的方法,本回调的返回值将作为返回值
  36. * 一般用于防止缓存击穿,获得锁后再做一次检测
  37. * 如果为{"$this", "methodName"}格式,$this将会被替换为当前类,方法必须为 public 或 protected
  38. *
  39. * @var callable
  40. */
  41. public $afterLock;

用法示例

最简单的锁:

只指定id,其它全部默认值

  1. @Lockable(id="锁ID")

定义执行任务前的操作:

  1. class Test
  2. {
  3. /**
  4. * @Lockable(id="锁ID", afterLock={"$this", "check"})
  5. */
  6. public function index()
  7. {
  8. return 1;
  9. }
  10. protected function check()
  11. {
  12. return 2;
  13. }
  14. /**
  15. * @Lockable(id="锁ID", afterLock={"$this", "check2"})
  16. */
  17. public function index2()
  18. {
  19. return 3;
  20. }
  21. protected function check2()
  22. {
  23. }
  24. }
  25. $result = App::getBean('Test')->index();
  26. echo $result, PHP_EOL; // 2
  27. $result = App::getBean('Test')->index2();
  28. echo $result, PHP_EOL; // 3

实例化使用方法

无需在配置中预定义

顺序用法

  1. $redisLock = new \Imi\Lock\Handler\Redis('锁ID', [
  2. 'poolName' => 'redis', // Redis 连接池名称,默认取redis.default配置
  3. 'db' => 1, // Redis 几号库,默认0
  4. 'waitSleepTime' => 20, // 获得锁每次尝试间隔,单位:毫秒
  5. 'keyPrefix' => 'imi:lock:', // Redis key 前置
  6. ]);
  7. if($redisLock->lock())
  8. {
  9. // 加锁后的处理
  10. // 解锁
  11. $redisLock->unlock();
  12. }
  13. else
  14. {
  15. // 加锁失败的处理
  16. }

回调用法

  1. $redisLock = new \Imi\Lock\Handler\Redis('锁ID', [
  2. 'poolName' => 'redis', // Redis 连接池名称,默认取redis.default配置
  3. 'db' => 1, // Redis 几号库,默认0
  4. 'waitSleepTime' => 20, // 获得锁每次尝试间隔,单位:毫秒
  5. 'keyPrefix' => 'imi:lock:', // Redis key 前置
  6. ]);
  7. // 执行后自动解锁
  8. $result = $redisLock->lock(function(){
  9. // 执行任务
  10. }, function(){
  11. // return 非null则不执行任务
  12. // 一般用于防止缓存击穿
  13. });
  14. if($result)
  15. {
  16. // 加锁并执行成功
  17. }
  18. else
  19. {
  20. // 加锁失败
  21. }