当初为什么参与设计开发 Redisson?
自 04 年从事工业自动化, 工业 IoT 工作至今, 涉及到很多场景需要对一系列设备进行监控和信号处理等工作. 该类场景对实时处理能力, 系统稳定性, 高可用性, 容灾能力等等要求非常高. 从 12 年时决定采用 Redis 作为实时数据库时就产生了许多想法. Redis 与 Java 这样的编程语言中的常用数据结构看似相像却又不同, 一直希望能够用什么方法将两者联系起来. 13 年开始商用 Redis 以后这种想法越加强烈. 于是在工作之余自行开始了一些相关的摸索与实践, 最终决定采用动态类的形式让 Redis 的数据结构操作起来更像 Java 对应的结构. 谁知远在莫斯科的 Nikita 似乎也有类似的想法, 他从 14 年元旦便开始了实际应用的开发, 并很快的开源了 Redisson. 于此同时我的实践也有了许些进展, 并初步的实现了一些基本功能. 不过由于工作上的种种原因, 再加上当时自己也缺乏足够的信心, 毕竟这是条没人走过的路, 大半年过去了进展比较缓慢. 殊不知 Nikita 面对这同样的问题, 但是他不仅艰难地坚持了下来, 而且丝毫没有放弃的意思. 14 年下半年时我开始注意到了 Redisson 项目, 仔细了解了以后顿时产生了很强的共鸣, 虽然和我的实践有着同样的理念却又是不同的出发点. 于是乎, 在有了这样的火花以后, 我们开始了相互之间的沟通和交流, 最后在 15 年初时决定, 放弃自己的实践项目, 加入 Redisson. 至此, 在这条没人走过的路上我们不再独行.
Redisson 解决了什么问题? 相比其他 Redis 客户端它有什么优势?
2.1)IoT 行业里, 一组设备的各种实时状态值往往是作为一个具有业务意义的对象, 由 JVM 管理在内存里, 如果将这个对象存储到 Redis 数据库的 String 结构里, 每次更新一个状态值, 就需要做一次序列化和反序列化. 同时还有可能面临着同一时刻操作同一个对象的不同状态值带来的并发难题. 实际应用时采用了 Redis 提供的 Hash 数据结构来储存这个对象, 只有这样才能有效地避免这类问题的发生. 尽管 Redis 的 Hash 结构和 Java 里的 HashMap 极为相似, 但是在程序操作 Redis 的时候不能像操作 HashMap 一样便捷. 而且如果对 Redis 相关命令的用法不能稔熟于心, 或在细节之处处理不当, 便会最终造成业务上的各种问题. Redisson 的 Map 就是为了填补 Redis 的 Hash 和 Java 的 HashMap 两者之间的空缺而产生.
图 1 - Jedis 的 Hash 操作
图 2 - Java 的 ConcurrentHashMap
图 3 - Redisson 的 ConcurrentHashMap
2.2)工控和某些 IoT 场景对实时处理能力要求很高, 所有的信号都必须实现毫秒级响应. 这类场景还具有并发量巨大的特点. 与社交电商等场景不同的是这类应用场景基本没有峰谷流量, 时时刻刻都是峰值. 因此其它场景里常见的削峰填谷措施在这里只能加重负担. 在这样的场景下如果使用像 Jedis 这样采用同步编程模型的客户端时, 就需要随时确保并发线程数与连接数一对一, 否则获取不到可用连接会直接报错. 相比之下 Redisson 利用了 Netty 异步编程框架, 使用了与 Redis 服务端结构类似的事件循环 (EventLoop) 式的线程池, 并结合连接池的方式弹性管理连接. 最终做到了使用少量的连接既可以满足对大量线程的要求, 从根本上缓解线程之间的竞争关系. 同时异步操作的模式还能够避免数据请求造成业务线程的阻塞.
2.3)Redis 发展至今经历了多次技术变迁. 官方版在迭代的过程中不但增加了许多有用的功能, 同时也发展了几种高可用性方案. 于此同时, 社区和云计算商在官方版上进而开发出了多种基于代理 (Proxy) 的高可用方案. 相比之下, 这些方案各有优劣, 适用场景也各自不一. 多样化的方案在带来便利的同时也带来了麻烦. 比如在业务扩容, 从简单的单机或主从模式迁移到哨兵或集群模式; 或是业务迁移, 从自建的 Redis 环境迁移到云上; 亦或是项目的持续性交付 CD/CI 过程中, 不同的阶段使用不同 Redis 运行模式等等情况. 往往需要开发人员针对不同的高可用方案开发出一套与之匹配的使用方法. 使得一个项目对 Redis 运行模式的耦合度高, 在 Redis 运行模式变化时就必须更改业务代码. Redisson 针对这种情况提供了一套便捷的文件化配置方法, 在无需修改程序代码的情况下, 通过不同的 JSON,YAML 或 SpringXML 文件实现对不同 Redis 运行模式和环境的支持. 这既降低了开发难度, 也降低了运维难度.
Redisson 在分布式锁方面的工作非常多, 能否介绍下这方面的实践?
对于 Redis 分布式锁的实现方式, 网上讨论相关文章都基本都 "烂大街" 了. 然而几乎所有相关介绍都是在单纯使用 setnx 命令的基础上进行一个简单封装, 且少有文章分析这样设计的缺陷. 在这个博客满天飞, 代码随便贴的时代, 这样的局面无形之中给了大家一个假象, 就是 Redis 分布式锁只能是以这样简单的形式存在, 即便有缺陷也只能在业务代码里规避. 那么为什么不换位思考一下, 即用稍微复杂点的设计来弥补它的不足, 从而换取业务上的灵活性呢? 再重新设计 Redis 分布式锁之前, 我们先了解一下单纯使用 setnx 命令封装的分布式锁有哪些不足.
1). 不具备可重入性
在执行 setnx 命令时, 通常采用业务上指定的名称作为 key 名, 用时间或随机值作为 value 来实现. 这样的实现方式不具备追踪请求线程的能力, 同时也不具备统计重入次数的能力, 甚至有些实现方式都不具备操作的原子性. 当遇到业务上需要在多个地方用到同样一个锁的时候, 很显然使用不具有可重入的锁会很容易发生死锁的现象. 特别是在有递归逻辑的场景里, 发生死锁的几率会更高. Java 并发工具包里的 Lock 对象和 sychronized 语块都具有可重入性, 对于经常使用这些工具的人来说, 往往会很容易忽略 setnx 的这个缺陷.
2). 不支持续约
在分布式环境中, 为了保证锁的活性和避免程序宕机造成的死锁现象, 分布式锁往往会引入一个失效时间, 超过这个时间则认为自动解锁. 这样的设计前提是开发人员对这个自动解锁时间的粒度有一个很好的把握, 太短了可能会出现任务没做完锁就失效了, 而太长了在出现程序宕机或业务节点挂掉时, 其它节点需要等很长时间才能恢复, 而难以保证业务的 SLA.setnx 的设计缺乏一个延续有效期的续约机制, 无法保证业务能够先工作做完再解锁, 也不能确保在某个程序宕机或业务节点挂掉的时候, 其它节点能够很快的恢复业务处理能力.
3). 不具备阻塞的能力
平常大家多少都接触过的锁, 由于加锁策略 (Locking Strategy) 的差别, 使得每种锁都有各自不同的特性. 但是在通常情况下这些锁都具备两个共性: 一是互斥性, 二是阻塞性. 互斥性是指在任何时刻最多只能有一个线程获得通行的资格. 阻塞性是指的在有竞争的情况下, 未获取到资源的线程会停止继续操作, 直到成功获取到资源或取消操作. 很显然 setnx 命令只提供了互斥的特性, 却没有提供阻塞的能力. 虽然在业务代码里可以引入自旋机制来进行再次获取, 但这仅仅是把原本应该在锁里实现的功能搬到了业务代码里, 通过增加业务代码的复杂程度来简化锁的实现似乎显得有点南辕北辙.
Redisson 的分布式锁在满足以上三个基本要求的同时还增加了线程安全的特点. 利用 Redis 的 Hash 结构作为储存单元, 将业务指定的名称作为 key, 将随机 UUID 和线程 ID 作为 field, 最后将加锁的次数作为 value 来储存. 同时 UUID 作为锁的实例变量保存在客户端. 将 UUID 和线程 ID 作为标签在运行多个线程同时使用同一个锁的实例时, 仍然保证了操作的独立性, 满足了线程安全的要求.
加锁时通过 Lua 脚本先检查锁是否存在, 如不存在则创建 hash 相关字段并设定过期时间后返回, 这表示加锁成功. 如果该 hash 字段已经存在, 再检查随机字段和线程 id 是否一致. 如果一致则递增 value 的值并重新更新过期时间后返回, 此时表示同一节点同一线程再次成功加锁, 从而保证了可重入性. 如果 hash 存在且字段不一致, 说明其他节点或线程已经拥有了这个锁. 因此 Lua 脚本返回这个 hash 的当前有效期. 当结果返回到在客户端后, 如果加锁成功, 则通过线程池依照设定好的参数定时执行续约, 最后通知请求线程继续后续操作. 如果加锁没有成功, 则监听一个以这个 key 为后缀的 pubsub 频道, 直到收到解锁消息后再次重试.
解锁时通过 Lua 脚本先检查锁是否存在, 如果已经不存在则直接发布解锁消息并返回. 如果任然存在则检查标签是否存在, 如果不存在则表示这个锁并不为本线程所拥有, 这种情况请求线程将收到报错. 如果存在则表示该锁正是被该线程所拥有. 在这种情况下, 递减标签字段后判断, 如果返回的加锁数量仍然大于 0, 说明当前的锁仍然有效, 仅仅只是重入次数减少了. 相反这表示锁已经完全解开, 则立即删除该锁并发布解锁信息.
Redisson 的可重入锁解决了 setnx 锁的许多先天性不足, 但是由于它仍然是以单一一个 key 的方式储存在固定的一个 Redis 节点里, 并且有自动失效期. 这样的设计虽然可以很大程度上避免客户端程序宕机或业务节点挂掉造成的影响, 但是随之带来的弊端是遇到服务端 Redis 进程宕机或节点挂掉的情况, 还是有可能会造成锁的信息丢失, 这样的缺陷显然无法满足某些特定场景提出的高可用性要求.
介于这种情况, Redis 作者 Salvatore 提出了一个基于多个节点的高可用分布式锁的算法, 起名叫红锁(RedLock: https://redis.io/topics/distlock ). 在这种算法下, 客户端需要同时在多个节点里同时尝试获取一个独立的锁, 只有当一次性成功获取了大多数锁的情况下才能被视为赢得了高可用分布式锁, 否则需要解除已经部分获取到的锁, 等待一个随机时间后再次重试.
在算法设计上, Salvatore 依然采用的是 setnx 作为举例讲解分布式锁的互斥特性. 在算法实现上, Redisson 的 RedissonRedLock 采用的是前面提到的更加灵活方便的可重入锁. Redisson 的扩展算法是 Redis 官网唯一认可的 Java 实现.
虽然 Redlock 的算法提供了高可用的特性, 但建立在大多数可见原则的前提下, 这样的算法适用性仍然有一定局限. Redisson 为此提供了基于增强型的算法的高可用分布式联锁 RedissonMultiLock. 这种算法要求客户端必须成功获取全部节点的锁才被视为加锁成功, 从而更进一步提高了算法的可靠性.
4. 能否介绍下 Redisson 最前沿的发展方向?
Redisson 的发展路线决定了它在 Redis 的功能扩展及应用方式上始终走在业界的前列, 其中最具有代表性的便是本地缓存功能了. 2016 年为了解决一企业版用户的切实需求开发了这一功能. 其原理是采用牺牲客户端自身内存的空间的方式, 换取在频繁获取某些常用数据时消耗在网络上的时间. 该功能在同年 9 月开源后便立即受到了广大用户的关注. 这一功能的出现加速了传统 IT 用户从其他类似平台迁移到 Redis 的速度. 其受欢迎程度大大超乎了 Nikita 和我的想象. 以至于每年都有企业用户不远万里去 Redis 大会等类似国际交流大会, 并分享它们使用 Redisson 从其他平台向 Redis 迁移过程和经验. 也正是因为这种趋势而引起了 Redis 作者 Salvatore 的注意, 在同一些用户面对面沟通交流之后, Salvatore 决定将客户端缓存功能作为 Redis 今后发展的重要方向, 并为此提出了 RESP3 协议. RESP3 的出现将为客户端缓存功能提供服务端协调的能力. 同时 Salvatore 还邀请 Redisson 团队作为专家组成员参与 Redis 客户端缓存标准的指定.
5.Redisson 做为开源项目如何保证持续的发展?
为了保证 Redisson 项目的可持续性的健康发展, 为了避免像其他开源项目面临的一段时间以后就无人维护的尴尬局面, 17 年初 Nikita 和我商量后决定在开源项目基础上提供收费咨询服务, 为项目的正常运作提供必要的资金. 同时还针对大型企业用户遇到的特殊场景提供了企业级的综合性解决方案, 最后将这些所有的方案与企业级 SLA 支持服务打包作为 Redisson PRO 正式面向企业用户.
相对于其他客户端而言, 虽然 Redisson 项目创立的时间较短, 但已经受到了来自不同行业企业的信任, 其中不乏许多行业领头羊企业, 其中最值得介绍的是这几个世界级的企业用户:
计算机行业的 IBM. 想必大家都熟悉 IBM,PC 机的鼻祖., 业界少有同时具有超强硬件软件研发能力的企业, 即便如此, IBM 也心甘情愿的使用 Redisson, 这种信任是对我们最大的支持.
航空国防制造业的波音. 在它们主动联系我们以前, 我很难想象波音也会对 Redisson 感兴趣. 事实上波音除了造飞机以外, 它也是全球最大的飞行航图提供商和移动电子飞行包的方案提供商, 几乎每个航空公司都是他们的用户. Redisson 为他们的在线飞行导航业务提供了扎实的基础.
保险业的美国国际集团(AIG). 美国国际集团成立于 1919 年的中国上海, 它是首个将保险概念带给中国人的西方企业, 其业务遍布全球 130 多个国家和地区. 虽然 08 年经济危机中, 遭遇股价瞬间暴跌的惨剧将 AIG 推入了吃瓜群主的视线, 但它今天仍是一个拥有 99 年历史, 总资产为 6 千多亿美元的国际性大型企业. 在经过 AIG 团队长时间的调研后, Redisson 被用于支持其名目众多的金融保险业务.
金融机构标准普尔 (S&P Global). 提到经济危机就不得不提一下世界权威金融分析机构标准普尔. 它是美国证券交易委员会(SEC) 认可的三大评级组织之一, 专门为投资者提供信用评级, 投资研究和咨询等服务. 在业内外的知名度很高, 享有盛名的 S&P 500 美国股指便是由它创建并维护着. 标准普尔不仅对外提供针对上市企业的评级, 还提供针对国家政府的评级. 它在 2011 年时断然降低美国政府的评级, 并把其前景调整为负面以后, 立马引发了金融业的剧烈波动. 但正是这个呼风唤雨无所不能, 连美国政府都不放眼里的机构也成为了 Redisson 的忠实用户, 并将其用于提供复杂的金融数据的分析和处理. 由此可见 Redisson 的信任评级是非常的高[奸笑].
来源: https://yq.aliyun.com/articles/603575