
于计算机, 在并发范畴之内进行编程的时候, 常常都会跟锁有所关联, 而锁存在着诸多类别, 像是互斥锁, 还有自旋锁等等。总与线程、进程这类词汇一同出现的锁, 阮一峰有的一篇文章曾针对这些名词给出了简单易懂的诠释。按照我的理解, 线程以及进程的运用, 目的在于达成并发, 进而获取性能方面的提升, 这其中利用了多核CPU以及多台服务器等条件, 然而, 这种并发情况, 因为调度存在不确定性缘故, 极易出现问题, 为了在一些共享资源以及关键节点之处不出问题, 又不得不对资源施加锁, 在于操作该资源期间, 对这种并发予以控制, 从而把问题消除掉。许多语言都给出了一些处于线程层面的锁实现, 还有一些与之相应的工具, 然而在进程这一方面却没办法了。而一项服务被部署到生产环境当中, 通常会部署多个实例, 在这种情形下, 就常常会用到针对不同进程的锁, 分布式锁就是在分布式系统里对某共享资源实施加锁的构建物体。现在来试着展示一下在项目中如何使用简单的分布式互斥锁。不使用分布式锁会怎样先拿一个简易的实例去演示一番, 要是不运用分布式锁, 会出现何种不堪的乱象呢。要是商城系统打算开展秒杀活动, 在redis里头记录着count为1的信息状况, 到了秒杀的那个时间点之际, 会接收到许许多多的请求, 在这种情形下, 各个应用程序去查询redis当中count的数值。要是count依旧大于0, 那就把count减去1, 如此一来别的请求就没办法再成功秒杀到了。# -*- coding: utf-8 -*- import os import arrow import redis from multiprocessing import Pool HOT_KEY count r redis.Redis(hostlocalhost, port6379) def seckilling(): name os.getpid() v r.get(HOT_KEY) if int(v) 0: print name, decr redis. r.decr(HOT_KEY) else: print name, can not set redis., v def run_without_lock(name): while True: if arrow.now().second % 5 0: seckilling() return if __name__ __main__: p Pool(16) r.set(HOT_KEY, 1) for i in range(16): p.apply_async(run_without_lock, args(i, )) print now 16 processes are going to get lock! p.close() p.join() print(All subprocesses done.)在上述代码当中, 借助多进程的方式, 对这种并发请求场景予以模仿, 程序启动之际, 把count设定成1, 随后各个进程开始进入到等待状态, 当秒数是5的时候, 所有进程一同去访问秒杀函数, 以此来看一下效果:运行结果redis查询展示就程序打印查redis的结果而言, 并未达成预期, 原本秒杀商品仅有一件, 可却成功被抢购到了4次, 这是因为各进程于处理get count这件事上, 针对redis值更新的指令已然发布, 却处在未完成的状况, 会致使其他进程觉得自身能够购得。这种问题可归为 不可重复读 种类的数据并发问题。于这种全然没有保护的情形之下, 其他常见的并发方面问题, 像是幻读现象、脏读状况、第一类以及第二类丢失更新之类的全都存在产生可能, 此处就不再逐个去列举示例了。使用作分布式锁作为知名工具, 它致力于解决分布式协同问题, 利用所提供的 API, 可以实现分式式锁, 它对于节点唯一性与顺序一致性有保证。实现的思路是这样的, 各个进程去着手创建, 那个名为//lock的结点, 要确保只有其中一个能够创建成功, 一旦这样的话, 那就认定创建成功的那一个获取到了锁, 当这个获得锁的进程处理完相关业务之后, 把那个node给删除掉, 其他进程会监听到这样的一个事件, 然后再次去尝试创建该节点, 按这样的方式持续进行下去。Kazoo库达成了这般Lock, 运用时相当简便, 编程人员能够不必再自行去实现等候锁的通用接口。与此同时, 在其中, 对于锁的运用, 常常能够借助优雅的上下文管理器with。def run_with_zk_lock(name): zk KazooClient() zk.start() lock zk.Lock(/lockpath, my-identifier) while True: if arrow.now().second % 5 0: with lock: seckilling() return使用zk结果redis查询展示当秒杀发生时只有获得锁的进程可以去进行秒杀操作。在锁的帮助下程序按照预想的方式运行了。使用redis作分布式锁有一篇文章, 在redis的网站上, 它专门介绍怎么使用redis当作分布式锁, 文尾附带了针对此文章的反对文章, 还有再次回击的文章, 这挺有意思且有点精彩。文章提到了一个的分布式锁设计。将设置锁所采用的redis命令规定为SET NX PX 30000 , 当添加NX参数之际, 若不存在这类情况才会进行创建操作, 若不存在的话则会返回不一样的结果, 凭借这个机制, 仅有一个能够set成功, 如同上面所提及的zk那般。然而, 达成这样一个分布式锁绝非仅仅如此容易, redis并非如同zk那般是一个分布式协同工具, 它不会做出分布式里有关各种一致性以及容错、可用性的保证。Redis自身便也是以集群方式进行部署的, 它们相互之间存在着异步复制所产生的时间差, 以及容错等等问题也许会出现, 若要切实达成这个锁能够在在线上如此大规模的分布式系统里得以使用, 确实是需要去考量各种各样的状况, 这显然是极为不容易的。就语言方面怎样去达成一个锁的接口, 其原理以及代码实现, 还有上述kazoo包里针对lock的源码, 我会在另外一篇专门的文中讲一讲。- py包, 是语言里针对上述文章的一种实现方式, 当前, 我们借助它来展开尝试。rlock RedLock([{host: localhost, port: 6379, db: 0}, ]) def run_with_redis_lock(name): while True: if arrow.now().second % 5 0: with rlock: seckilling() returnredis锁运行结果运行结果和上面使用zk一样符合程序设计预期。下面这些仅仅是依据语言的一部分代码呈现, 借助运用两个第三方的工具包, 以此来运用分布式锁用以防止并发程序里出现的杂乱情况。其实, 这中间存在一个断层, 为何这么说、是因为这两个工具, 都只是提供出去了一个机制, 而非直接朝着外部去给出来操作系统锁的API , 那怎样运用这般机制来达成这样的锁, 恰好是这两个第三方所付诸行动在推动的事。简略瞧过它们达成的源码, 包括其中某些lock的代码, 察觉在锁的达成存在相通之处, 皆存在通用的属性与方法, 接着把enter以及exit指向先前那两个方法以此达成上下文管理器with的用法。除此之外, 能够借助关系型数据库像MySQL所具备的固有的锁机制当作分布式锁, 然而因为数据库常常是系统的瓶颈之处, 没必要给它引入不必要的压力。与此同时, MySQL里的锁、隔离级别存在诸多可讲述的内容, 在上面查找一番也没有找到一个成熟的象上面那样基于MySQL实现的对外暴露锁通用API的第三方包, 所以没能在上面进行展示。想把这个事情清晰表述出来并非那般轻松后, 我会试着弄明白怎样撰写一个较为纯正风格的锁, 并且对上面那两个第三方包的明确实现予以探究, 力求把这一断层填补起来。之后, 也许能够尝试去实现基于MySQL的类似第三方包, 这得要对MySQL的某些机制了解得更为透彻才行。上述这些便是这篇文章的所有内容, 期望能给大家的求学带来一些助力, 同时也期望大家能够给予脚本之家诸多支持。