锁组件

编辑本页

警告:您正在浏览的文档欧宝官网下载appob娱乐下载Symfony 5.0,现已不再维护。

本页的更新版本用于Syob娱乐下载mfony 6.2(当前稳定版本)。

锁组件

锁组件创建和管理,一种提供对共享资源的独占访问的机制。

安装

1
作曲家需要symfony/lockob娱乐下载

请注意

如果在Symfony应用程序外部安装此组件,则必须要求ob娱乐下载供应商/ autoload.php文件,以启用Composer提供的类自动加载机制。读这篇文章欲知详情。

使用

锁用于保证对某些共享资源的独占访问。例如,ob娱乐下载在Symfony应用程序中,您可以使用锁来确保一个命令不会在同一时间(在相同或不同的服务器上)执行多次。

类创建锁LockFactory类,这又需要另一个类来管理锁的存储:

1 2 3 4 5
使用ob娱乐下载组件LockFactory使用ob娱乐下载组件商店SemaphoreStore商店SemaphoreStore ();工厂LockFactory (商店);

方法创建锁createLock ()方法。它的第一个参数是表示锁定资源的任意字符串。然后,调用获得()方法将尝试获取锁:

1 2 3 4 5 6 7 8 9
/ /……工厂->createLock (“pdf-invoice-generation”);如果->获得()){//资源“pdf-invoice-generation”被锁定。//您可以在这里安全地计算和生成发票。->release ();}

如果无法获取锁,则该方法返回.的获得()方法可以安全地重复调用,即使已经获得了锁。

请注意

与其他实现不同的是,即使是为同一资源创建的锁实例,Lock Component也能区分它们。如果一个锁必须被多个服务使用,那么它们应该共享同一个锁实例返回的LockFactory: createLock方法。

提示

如果您没有显式地释放锁,它将在实例销毁时自动释放。在某些情况下,跨多个请求锁定一个资源可能很有用。属性的第三个参数,可禁用自动释放行为createLock ()方法

阻塞锁

缺省情况下,当无法获取锁时,将使用收购方法返回立即。要等待(无限期地)直到可以创建锁,请通过真正的作为论证获得()方法。这叫做阻塞锁因为在获得锁之前,应用程序的执行将停止。

一些内置的商店类支持此特性。如果没有,可以用RetryTillSaveStore类:

1 2 3 4 5 6 7 8 9 10
使用ob娱乐下载组件LockFactory使用ob娱乐下载组件商店RedisStore使用ob娱乐下载组件商店RetryTillSaveStore商店RedisStore (\ Predis \客户端(“tcp: / / localhost: 6379”));商店RetryTillSaveStore (商店);工厂LockFactory (商店);工厂->createLock (“notification-flush”);->获得(真正的);

到期的锁

远程创建的锁很难管理,因为没有远程的方法商店以了解储物柜进程是否仍然存在。由于bug,致命错误或分割错误,不能保证release ()方法,这将导致资源被无限锁定。

在这种情况下,最好的解决办法就是创造到期的锁,在经过一段时间后自动释放(对于活着的时间).属性的第二个参数,单位为秒createLock ()方法。方法也可以提前释放这些锁release ()方法。

处理过期锁时最棘手的部分是选择正确的TTL。如果它太短,其他进程可能会在完成任务之前获得锁;如果时间过长,进程在调用release ()方法,资源将保持锁定直到超时:

1 2 3 4 5 6 7 8 9 10
/ /……//创建一个持续30秒的过期锁工厂->createLock (“charts-generation”30.);->获得();试一试//在30秒内完成一个任务最后->release ();}

提示

为了避免锁处于锁定状态,建议将作业封装在try/catch/finally块中,以便始终尝试释放即将到期的锁。

对于长时间运行的任务,最好从一个不太长的TTL开始,然后使用refresh ()方法将TTL重置为原始值:

12 3 4 5 6 7 8 9 10 11 12 13 14
/ /……工厂->createLock (“charts-generation”30.);->获得();试一试(!完成了){//完成工作的一小部分。//更新锁30秒。->refresh ();}}最后->release ();}

提示

类的参数传递自定义TTL,这是长时间运行任务的另一个有用技术refresh ()方法修改默认锁TTL:

1 2 3 4 5 6 7
工厂->createLock (“charts-generation”30.);/ /……//刷新锁30秒->refresh ();/ /……//刷新锁600秒(下一次refresh()调用将再次为30秒)->刷新(600);

该组件还提供了两个与过期锁相关的有用方法:getExpiringDate ()(返回或者一个\ DateTimeImmutable对象),isExpired ()(返回一个布尔值)。

锁的主人

对象第一次获得的锁为[1]_获取它的实例。如果需要检查电流是否实例仍然是锁的所有者,您可以使用isAcquired ()方法:

1 2 3
如果->isAcquired ()) {//我们仍然拥有这把锁

由于一些锁存储有过期的锁(如上所述),实例可能会丢失它自动获得的锁:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
//如果我们自己无法获取,就意味着其他进程已经在处理它了如果(!->获得()){返回;}->beginTransaction ();//执行一个很长的进程,可能超过锁的生存时间如果->isAcquired ()) {//一切正常,在此期间没有其他实例获得锁,我们是安全的->commit ();}其他的/ /懒汉!我们的锁显然已经超过了TTL,另一个进程已经开始//在此期间,所以它对我们来说是不安全的。->rollback ();异常“过程失败”);}

谨慎

一个常见的缺陷可能是使用isAcquired ()方法检查某个锁是否已被任何进程获取。正如你在这个例子中看到的,你必须使用获得()对于这个。的isAcquired ()方法用于检查锁是否已由当前进程只有!

..[1]从技术上讲,锁的真正所有者是那些共享相同实例的人关键
.但从用户的角度来看,关键是内部的,您可能只与实例,这样更容易想到实例作为锁的所有者。

可用的商店

在其中创建和管理锁商店,它们是实现的类PersistingStoreInterface可选地,BlockingStoreInterface

该组件包括以下内置存储类型:

商店 范围 阻塞 即将到期的
FlockStore 当地的 是的 没有
MemcachedStore 远程 没有 是的
PdoStore 远程 没有 是的
RedisStore 远程 没有 是的
SemaphoreStore 当地的 是的 没有
ZookeeperStore 远程 没有 没有

FlockStore

FlockStore使用本地计算机上的文件系统创建锁。它不支持过期,但是当锁对象超出作用域时,锁会自动释放,并由垃圾收集器释放(例如当PHP进程结束时):

1 2 3 4 5
使用ob娱乐下载组件商店FlockStore//参数是创建锁的目录的路径//如果没有,则在内部使用sys_get_temp_dir()。商店FlockStore (/ var /商店的);

谨慎

注意,某些文件系统(例如某些类型的NFS)不支持锁定。在这种情况下,最好使用本地磁盘驱动器上的目录或基于PDO、Redis或Memcached的远程存储。

MemcachedStore

MemcachedStore将锁保存在Memcached服务器上,它需要一个Memcached连接来实现\ Memcached类。这个存储不支持阻塞,并期望TTL来避免锁被卡住:

1 2 3 4 5 6
使用ob娱乐下载组件商店MemcachedStorememcached\ Memcached ();memcached->addServer (“localhost”11211);商店MemcachedStore (memcached);

请注意

Memcached不支持TTL小于1秒。

PdoStore

PdoStore将锁保存在SQL数据库中。它需要PDO连接,原则DBAL连接,或数据源名称(DSN).这个存储不支持阻塞,并期望TTL来避免锁被卡住:

1 2 3 4 5
使用ob娱乐下载组件商店PdoStore// PDO, Doctrine DBAL连接或通过PDO进行延迟连接的DSNdatabaseConnectionOrDSN“mysql:主机= 127.0.0.1;dbname =锁'商店PdoStore (databaseConnectionOrDSN, [“db_username”= >“myuser”“db_password”= >“我的密码”]);

请注意

此商店不支持TTL低于1秒。

在数据库中存储锁之前,必须创建存储信息的表。存储提供了一个方法不知道()根据所使用的数据库引擎设置此表:

1 2 3 4 5
试一试商店->不知道();}(\ PDOException异常){//由于某些原因无法创建表

在生产环境中设置表的一个好方法是调用不知道()方法,然后生成一个数据库迁移

1 2
PHP bin/控制台原则:迁移:diffPHP bin/控制台原则:迁移:迁移

RedisStore

RedisStore将锁保存在Redis服务器上,它需要一个Redis连接来实现\复述,\ RedisArray\ RedisCluster\ Predis类。这个存储不支持阻塞,并期望TTL来避免锁被卡住:

1 2 3 4 5 6
使用ob娱乐下载组件商店RedisStore复述,\复述();复述,->连接(“localhost”);商店RedisStore (复述,);

SemaphoreStore

SemaphoreStore使用PHP信号量函数要创建锁:

1 2 3
使用ob娱乐下载组件商店SemaphoreStore商店SemaphoreStore ();

CombinedStore

组合商店是为高可用性应用程序设计的,因为它同步管理多个商店(例如,几个Redis服务器)。当获取锁时,它将调用转发给所有托管存储,并收集它们的响应。如果简单多数商店已经获得该锁,则该锁被视为已获得;否则如未获得:

12 3 4 5 6 7 8 9 10 11 12 13
使用ob娱乐下载组件商店CombinedStore使用ob娱乐下载组件商店RedisStore使用ob娱乐下载组件策略ConsensusStrategy商店= [];foreach([“server1”server2的“server3”作为服务器){复述,\复述();复述,->连接(服务器);商店[] =RedisStore (复述,);}商店CombinedStore (商店ConsensusStrategy ());

而不是简单多数策略(ConsensusStrategy)一个UnanimousStrategy可用于要求在所有存储中获取锁。

谨慎

为了在使用时获得高可用性ConsensusStrategy时,最小集群大小必须为3台服务器。这允许集群在单个服务器故障时继续工作(因为此策略要求在超过一半的服务器中获得锁)。

ZookeeperStore

ZookeeperStore保存锁动物园管理员服务器。它需要一个ZooKeeper连接来实现\动物园管理员类。这个store不支持阻塞和过期,但是当PHP进程终止时,锁会自动释放:

1 2 3 4 5 6 7
使用ob娱乐下载组件商店ZookeeperStore动物园管理员\管理员(“localhost: 2181”);//使用以下命令定义高可用性集群:// $zookeeper = new \ zookeeper ('localhost1:2181,localhost2:2181,localhost3:2181');商店ZookeeperStore (动物园管理员);

请注意

Zookeeper不需要TTL,因为用于锁定的节点是短暂的,当PHP进程终止时就会死亡。

可靠性

该组件保证只要以以下方式使用该组件,同一资源就不会被锁定两次。

远程存储

远程存储(MemcachedStorePdoStoreRedisStore而且ZookeeperStore)使用唯一令牌来识别锁的真正所有者。标记存储在关键对象在内部使用因此这个键不能在进程(session, caching, fork,…)之间共享。

谨慎

不要在进程之间共享密钥。

每个并发进程必须存储在同一台服务器上。否则两台不同的机器可能允许两个不同的进程获得相同的数据

谨慎

为了保证同一台服务器始终是安全的,不要在LoadBalancer、集群或轮询DNS后面使用Memcached。即使主服务器关闭,也不能将调用转发到备份或故障转移服务器。

到期的商店

即将到期的储存区(MemcachedStorePdoStore而且RedisStore)保证只在规定的时间内获得锁。如果任务需要更长的时间才能完成,那么锁可以由存储释放,并由其他人获取。

提供几种检查其运行状况的方法。的isExpired ()方法检查其生命周期是否结束getRemainingLifetime ()方法返回以秒为单位的时间。

使用上述方法,更健壮的代码将是:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/ /……工厂->createLock (“invoice-publication”30.);->获得();(!完成了){如果->getRemainingLifetime () < =5){如果->isExpired ()) {//锁丢失,执行回滚或发送通知\ RuntimeException (“锁在整个过程中丢失”);}->refresh ();}//执行耗时小于5分钟的任务

谨慎

明智地选择你的一生并检查其剩余的离开时间是否足够执行任务。

谨慎

存储通常需要几毫秒,但网络条件可能会大大增加这个时间(最多几秒)。在选择正确的TTL时要考虑到这一点。

按照设计,锁存储在具有定义的生命周期的服务器中。如果机器的日期或时间发生了变化,锁可能会比预期更快地释放。

谨慎

为了保证日期不会改变,NTP服务应该被禁用,并且在服务停止时应该更新日期。

FlockStore

通过使用文件系统,这商店只要并发进程使用相同的物理目录来存储锁,它就是可靠的。

进程必须运行在同一台机器、虚拟机或容器上。更新Kubernetes或Swarm服务时要小心,因为在短时间内,可能会有两个并行运行的容器。

目录的绝对路径必须保持不变。小心可能随时改变的符号链接:Capistrano和蓝/绿部署经常使用这个技巧。当到该目录的路径在两次部署之间发生变化时要小心。

某些文件系统(例如某些类型的NFS)不支持锁定。

谨慎

所有并发进程必须使用相同的物理文件系统,运行在同一台机器上,并使用相同的绝对路径到locks目录。

根据定义,用法FlockStore在HTTP上下文中与多个前端服务器不兼容,除非确保同一资源始终锁定在同一台机器上,或者使用配置良好的共享文件系统。

文件系统中的文件可以在维护操作时删除。例如,清理/ tmp目录或当目录使用tmpfs时重新启动计算机后。如果在进程结束时释放锁,这不是问题,但如果发生在请求之间重用。

谨慎

如果需要在多个请求中重用volatile文件系统上的锁,则不要将它们存储在volatile文件系统上。

MemcachedStore

Memcached的工作方式是将项存储在内存中。这意味着通过使用MemcachedStore锁没有被持久化,并且可能在任何时候错误地消失。

如果Memcached服务或承载它的机器重新启动,每个锁都将丢失,而不会通知正在运行的进程。

谨慎

为了避免重启后被其他人获得锁,建议延迟服务启动,等待时间至少等于最长锁TTL。

默认情况下,当服务需要空间来添加新项时,Memcached使用LRU机制删除旧项。

谨慎

必须控制存储在Memcached中的项的数量。如果不可能,应该禁用LRU,并将Lock存储在专用的Memcached服务中,远离Cache。

当Memcached服务被共享并用于多个用途时,锁可能会被错误地删除。例如PSR-6的一些实现clear ()方法使用Memcached的冲洗()清除和删除所有内容的方法。

谨慎

该方法冲洗()不能被调用,或者锁应该存储在专用的Memcached服务中,远离Cache。

PdoStore

PdoStore依赖SQL引擎属性。

谨慎

在配置了多个主的集群中,请确保同步地将写操作传播到每个节点,或者始终使用相同的节点。

谨慎

一些SQL引擎(如MySQL)允许禁用唯一约束检查。确保不是这种情况设置unique_checks = 1;

为了清除旧锁,该存储使用当前datetime来定义过期日期引用。该机制依赖于所有服务器节点具有同步时钟。

谨慎

确保锁不会过早过期;ttl应该设置足够的额外时间,以考虑节点之间的任何时钟漂移。

RedisStore

Redis的工作方式是将项目存储在内存中。这意味着通过使用RedisStore锁没有被持久化,并且可能在任何时候错误地消失。

如果Redis服务或承载它的机器重新启动,每个锁都将丢失,而不通知正在运行的进程。

谨慎

为了避免重启后被其他人获得锁,建议延迟服务启动,等待时间至少等于最长锁TTL。

提示

Redis可以配置为将项目持久化在磁盘上,但是这个选项会减慢对服务的写操作。这可能与服务器的其他用途相冲突。

当Redis服务被共享并用于多个用途时,锁可能会被错误地删除。

谨慎

命令FLUSHDB必须不被调用,或者锁应该存储在一个专用的Redis服务远离缓存。

CombinedStore

合并存储允许跨多个后端存储锁。认为锁定机制会更可靠是一个常见的错误。这是错误的。的CombinedStore最好的情况下,将与所有托管存储中最不可靠的存储一样可靠。一旦某个托管存储返回错误信息,则CombinedStore不可靠。

谨慎

所有并发进程必须使用相同的配置,具有相同数量的托管存储和相同的端点。

提示

与其使用Redis或Memcached服务器集群,不如使用CombinedStore每个托管存储都有一个服务器。

SemaphoreStore

信号量由内核级处理。为了保证可靠性,进程必须运行在同一台机器、虚拟机或容器上。更新Kubernetes或Swarm服务时要小心,因为在短时间内,可能会有两个并行运行的容器。

谨慎

所有并发进程必须使用同一台机器。在新机器上启动并发进程之前,请检查旧机器上的其他进程是否已停止。

谨慎

使用非系统用户和选项在systemd上运行时RemoveIPC = yes(默认值),当用户注销时,systemd将删除锁。检查进程是否使用系统用户(UID <= SYS_UID_MAX)运行SYS_UID_MAX中定义的/etc/login.defs,或设置该选项RemoveIPC =了/etc/systemd/logind.conf

ZookeeperStore

zookeeper的工作方式是将锁作为服务器上的临时节点来维护。这意味着通过使用ZookeeperStore锁将在会话结束时自动释放,以防客户端因任何原因无法解锁。

如果ZooKeeper服务或承载它的机器重新启动,每个锁都会丢失,而不会通知正在运行的进程。

提示

为了使用ZooKeeper的高可用性特性,你可以设置多个服务器的集群,这样在其中一个服务器宕机的情况下,大多数服务器仍然是正常的,并为请求服务。集群中的所有可用服务器都将看到相同的状态。

请注意

由于此存储不支持多级节点锁,因此清理中间节点成为一种开销,因此所有锁都在根级别维护。

整体

更改存储的配置应该非常谨慎。例如,在部署新版本期间。当具有旧配置的旧进程仍在运行时,不能启动具有新配置的进程。

此工作,包括代码示例,是根据创作共用BY-SA 3.0许可证。