锁组件

编辑本页

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

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

锁组件

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

如果您正在使用Symfony框架,请阅读ob娱乐下载ob娱乐下载Symfony框架锁文档欧宝官网下载app

安装

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 ()方法

序列化锁

关键控件的状态并且可以序列化。这允许用户在一个进程中通过获得锁来开始一个长任务,并在另一个进程中使用相同的锁继续该任务:

1 2 3 4 5 6 7 8
使用ob娱乐下载组件关键使用ob娱乐下载组件关键键(“文章”。文章->getId ());锁(关键->商店,300);->获得(真正的);->公共汽车->调度(RefreshTaxonomy (文章关键));

请注意

不要忘记禁用autoRelease以避免在调用析构函数时释放锁。

并不是所有的存储都与序列化和跨进程锁兼容:例如,内核将自动释放由线程获取的信号量SemaphoreStore商店。如果使用不兼容的存储,则在应用程序尝试序列化密钥时将抛出异常。

阻塞锁

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

一些内置的商店类支持此特性。如果没有,可以用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 ()方法,资源将保持锁定直到超时:

12 3 4 5 6 7 8 9 10 11 12
/ /……//创建一个持续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 15 16
/ /……工厂->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);

该组件还提供了两个与过期锁相关的有用方法:getRemainingLifetime ()(返回或者一个浮动作为秒)和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 远程 没有 是的
MongoDbStore 远程 没有 是的
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秒。

MongoDbStore

5.1

MongoDbStore在Symfony 5.1中引入。ob娱乐下载

MongoDbStore在MongoDB服务器上保存锁> = 2.2,它需要一个\ MongoDB \集合\ MongoDB \客户mongodb / mongodb或者一个MongoDB连接字符串.该存储不支持阻塞,并期望TTL来避免锁被卡住:

1 2 3 4 5 6 7 8 9 10 11
使用ob娱乐下载组件商店MongoDbStore蒙戈mongodb: / / localhost /数据库?收集=锁'选项= (“gcProbablity”= >0.001“数据库”= >“myapp”“收集”= >“锁”“uriOptions”= > [],“driverOptions”=> [],];商店MongoDbStore (蒙戈选项);

MongoDbStore执行以下操作选择美元(取决于第一个参数类型):

选项 描述
gcProbablity 是否应该创建一个TTL索引,表示为从0.0到1.0的概率(默认为0.001
数据库 数据库的名称
集合 集合的名称
uriOptions 的uri选项数组MongoDBClient: __construct
driverOptions 的驱动程序选项数组MongoDBClient: __construct

当第一个参数为a时:

MongoDB \集合:

  • 美元的选项(“数据库”)被忽略
  • 美元的选项(“收集”)被忽略

MongoDB \客户:

  • 美元的选项(“数据库”)是强制性的
  • 美元的选项(“收集”)是强制性的

MongoDB连接字符串:

  • 美元的选项(“数据库”)用于其他情况/路径从DSN,至少一个是强制性的
  • 美元的选项(“收集”)用于其他情况收集? =从DSN,至少一个是强制性的

请注意

集合查询字符串参数不是MongoDB连接字符串定义。它用于构造MongoDbStore使用一个数据源名称(DSN)没有选择美元

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进程终止时就会死亡。

可靠性

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

远程存储

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

谨慎

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

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

谨慎

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

到期的商店

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

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

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

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/ /……工厂->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。

MongoDbStore

谨慎

锁定资源名在_id锁集合的字段。注意,在MongoDB中,索引字段的值可以是长度不超过1024字节包括结构开销。

必须使用TTL索引来自动清除过期的锁。这样的索引可以手动创建:

1 2 3 4
db.lock。ensureIndex ({“expires_at”:1}, {“expireAfterSeconds”:0})

或者,方法MongoDbStore::createTtlIndex(int $expireAfterSeconds = 0)可以在数据库设置期间调用一次来创建TTL索引。阅读更多通过设置TTL使集合中的数据过期在MongoDB。

提示

MongoDbStore将尝试自动创建一个TTL索引。建议设置构造函数optiongcprobability = 0.0如果已手动处理TTL索引创建,则禁用此行为。

谨慎

此存储依赖于所有PHP应用程序和数据库节点具有同步时钟,以便在正确的时间发生锁过期。确保锁不会过早过期;锁的TTL应该被设置为有足够的额外时间expireAfterSeconds考虑节点之间的时钟漂移。

writeConcern而且readConcern没有被MongoDbStore指定,这意味着集合的设置将生效。readPreference主要的对于所有查询。阅读更多副本设置读写语义在MongoDB。

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许可证。