锁组件
编辑本页警告:您正在浏览的文档欧宝官网下载appob娱乐下载Symfony 3.4,现已不再维护。
读本页的更新版本用于Syob娱乐下载mfony 6.2(当前稳定版本)。
锁组件
锁组件创建和管理锁,一种提供对共享资源的独占访问的机制。
3.4
Lock组件是在Symfony 3.4中引入的。ob娱乐下载
安装
1
$作曲家需要symfony/lockob娱乐下载:^3.4
请注意
如果在Symfony应用程序外部安装此组件,则必须要求ob娱乐下载供应商/ autoload.php
文件,以启用Composer提供的类自动加载机制。读这篇文章欲知详情。
使用
锁用于保证对某些共享资源的独占访问。例如,ob娱乐下载在Symfony应用程序中,您可以使用锁来确保一个命令不会在同一时间(在相同或不同的服务器上)执行多次。
类创建锁工厂类,这又需要另一个类来管理锁的存储:
1 2 3 4 5
使用ob娱乐下载\组件\锁\工厂;使用ob娱乐下载\组件\锁\商店\SemaphoreStore;$商店=新SemaphoreStore ();$工厂=新工厂($商店);
方法创建锁createLock ()方法。它的第一个参数是表示锁定资源的任意字符串。然后,调用获得()方法将尝试获取锁:
1 2 3 4 5 6 7 8 9
/ /……$锁=$工厂->createLock (“pdf-invoice-generation”);如果($锁->获得()){//资源“pdf-invoice-generation”被锁定。//您可以在这里安全地计算和生成发票。$锁->release ();}
如果无法获取锁,则该方法返回假
.的获得()
方法可以安全地重复调用,即使已经获得了锁。
请注意
与其他实现不同的是,即使是为同一资源创建的锁实例,Lock Component也能区分它们。如果一个锁必须被多个服务使用,那么它们应该共享同一个锁锁
实例返回的工厂:createLock
方法。
提示
如果您没有显式地释放锁,它将在实例销毁时自动释放。在某些情况下,跨多个请求锁定一个资源可能很有用。属性的第三个参数,可禁用自动释放行为createLock ()
方法假
.
阻塞锁
缺省情况下,当无法获取锁时,将使用收购
方法返回假
立即。要等待(无限期地)直到可以创建锁,请通过真正的
作为论证获得()
方法。这叫做阻塞锁因为在获得锁之前,应用程序的执行将停止。
一些内置的商店
类支持此特性。如果没有,可以用RetryTillSaveStore
类:
1 2 3 4 5 6 7 8 9 10
使用ob娱乐下载\组件\锁\工厂;使用ob娱乐下载\组件\锁\商店\RedisStore;使用ob娱乐下载\组件\锁\商店\RetryTillSaveStore;$商店=新RedisStore (新\ Predis \客户端(“tcp: / / localhost: 6379”));$商店=新RetryTillSaveStore ($商店);$工厂=新工厂($商店);$锁=$工厂->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 ();}
该组件还提供了两个与过期锁相关的有用方法: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]从技术上讲,锁的真正所有者是那些共享相同实例的人
关键
, -
不
锁
.但从用户的角度来看,关键
是内部的,您可能只与锁
实例,这样更容易想到锁
实例作为锁的所有者。
可用的商店
在其中创建和管理锁商店
,它们是实现的类StoreInterface.该组件包括以下内置存储类型:
商店 | 范围 | 阻塞 | 即将到期的 |
---|---|---|---|
FlockStore | 当地的 | 是的 | 没有 |
MemcachedStore | 远程 | 没有 | 是的 |
RedisStore | 远程 | 没有 | 是的 |
SemaphoreStore | 当地的 | 是的 | 没有 |
FlockStore
FlockStore使用本地计算机上的文件系统创建锁。它不支持过期,但是当锁对象超出作用域时,锁会自动释放,并由垃圾收集器释放(例如当PHP进程结束时):
1 2 3 4 5
使用ob娱乐下载\组件\锁\商店\FlockStore;//参数是创建锁的目录的路径//如果没有,则在内部使用sys_get_temp_dir()。$商店=新FlockStore (/ var /商店的);
谨慎
注意,某些文件系统(例如某些类型的NFS)不支持锁定。在这种情况下,最好使用本地磁盘驱动器上的目录或基于Redis或Memcached的远程存储。
MemcachedStore
MemcachedStore将锁保存在Memcached服务器上,它需要一个Memcached连接来实现\ Memcached
类。这个存储不支持阻塞,并期望TTL来避免锁被卡住:
1 2 3 4 5 6
使用ob娱乐下载\组件\锁\商店\MemcachedStore;$memcached=新\ Memcached ();$memcached->addServer (“localhost”,11211);$商店=新MemcachedStore ($memcached);
请注意
Memcached不支持TTL小于1秒。
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台服务器。这允许集群在单个服务器故障时继续工作(因为此策略要求在超过一半的服务器中获得锁)。
可靠性
该组件保证只要以以下方式使用该组件,同一资源就不会被锁定两次。
远程存储
远程存储(MemcachedStore而且RedisStore)使用唯一令牌来识别锁的真正所有者。标记存储在关键对象在内部使用锁
因此这个键不能在进程(session, caching, fork,…)之间共享。
谨慎
不要在进程之间共享密钥。
每个并发进程必须存储锁
在同一台服务器上。否则两台不同的机器可能允许两个不同的进程获得相同的数据锁
.
谨慎
为了保证同一台服务器始终是安全的,不要在LoadBalancer、集群或轮询DNS后面使用Memcached。即使主服务器关闭,也不能将调用转发到备份或故障转移服务器。
到期的商店
即将到期的储存区(MemcachedStore而且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。
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
.
整体
更改存储的配置应该非常谨慎。例如,在部署新版本期间。当具有旧配置的旧进程仍在运行时,不能启动具有新配置的进程。