仓颉原生Redis客户端:探索与实践指南

发表时间: 2024-07-02 18:57

仓颉原生Redis客户端探索与实践###

仓颉编程语言是一个支持高效、安全、全场景应用开发的现代编程语言,具有强安全、高性能、高效率、全场景等特点,同时具备以下现代编程语言的核心特性:

  • 支持自动内存管理

  • 支持完备的类型系统

  • 支持扩展、类型推断、元编程

  • 支持过程式、函数式和面向对象多种编程范式

  • 支持轻量级线程模型

  • 支持自动微分以及AI编程

  • 支持多种运行模式,满足不同场景应用开发诉求

  • 内置完整的工具链,包括包管理、代码格式化、构建和测试等

当前,大量在线业务系统需要使用Redis作为企业应用数据缓存来提升业务处理性能,提供一个仓颉原生的高性能Redis客户端SDK,支持Redis单机模式、哨兵模式、集群模式的客户端成为当务之急。

仓颉Redis客户端redis-sdk项目已经开源并发布到仓颉社区,特性为:

  • 支持RESP2和RESP3协议

  • 接口设计兼容jedis接口语义

  • 支持哨兵模式和集群模式

  • 支持发布订阅模式

  • 支持单连接多线程模式

  • 丰富的管理命令支持

  • 完备的单元测试覆盖

  • 架构简洁,易于扩展

总体设计

一、仓颉redis-sdk项目的总体设计如下:

1、接口层

提供创建到Redis单实例服务端、哨兵模式高可用服务端、集群模式高可用服务端的连接,提供执行Redis命令的接口方法。

2、服务层

  • 命令模块:发送Redis命令,并收取命令对应的响应返回给调用方。

  • 订阅模块:发动订阅命令或者模式匹配的订阅命令,订阅相应频道,并收取服务端推送的订阅消息。支持阻塞订阅和非阻塞订阅,阻塞订阅等到服务端返回的订阅频道数为0,或者订阅客户端关闭时退出;非阻塞客户端发送订阅命令后立即返回。

  • 哨兵模块:记录哨兵模式的高可用集群的状态,连接到多个哨兵实例并监听主实例的变化,当主实例发生变化时,后续命令切换到新的主实例上执行。

  • 集群模块:记录集群实例的状态,根据命令的参数计算出对应的槽位后,将命令发送到该槽位对应的集群实例执行。

3、编解码层

将Redis命令编码为二进制数组,用于在网络上传输。将从网络上收到的数据进行解码,转换为Redis命令对应的响应。支持RESP2和RESP3协议的编解码。

4、通信层

创建TCP连接,使用TCP连接发送请求报文和收取响应报文。支持连接空闲超时和连接重建。支持TLS通信。

二、Redis命令的处理流程如下:

发送命令的流程如下:

客户端执行命令时,将命令名称和命令对应的参数封装为RedisCommand类。

RedisCommandToByteEncoder将RedisCommand编码为RESP协议的二进制数据,最终由SocketConnection将二进制数据发送给Redis服务端。

客户端调用RedisComand的waitForResponse等待响应。

接收命令响应的流程如下:

SocketConnection在读取到二进制数据后,将二进制数据交ByetToRedisMessageDecoder解码。

ByetToRedisMessageDecoder将解码结果RedisMessage交给RedisCommandHandler进行下一步处理。

RedisCommandHandler将RedisMesssage设置到RedisCommand中,并通知等待响应的客户端。

三、哨兵模式执行命令的流程如下:

客户端执行命令时,会通过SentinelCommandExectutor从SentinelConnectionProvider中获取到master实例的连接;

SentinelManager负责维护哨兵实例的高可用集群的状态,会创建连接到每一个哨兵实例的订阅器RedisSubscriber;

RedisSubscriber从哨兵实例订阅switch-master频道;

SentinelManager在收到RedisSubscriber的master实例发生变化的通知后,会通知SentinelConnectionProvider更新连接到新的master实例。

四、集群模式执行命令的流程如下:

客户端执行命令时,会先计算命令的Key对应的槽位;

ClusterCommandExectutor从ClusterConnectionProvider获取槽位对应的集群实例的连接用于执行命令;

ClusterInfoCache负责维护集群模式的高可用集群的状态,会缓存所有集群实例的槽位信息;

当集群实例的槽位信息发生变化导致命令执行失败时,ClusterInfoCache会重新更新槽位信息,并在更新完成后再次执行命令;

TopologyRefreshExecutor会定期更新ClusterInfoCache中缓存的槽位信息。

五、发布订阅模块的处理流程如下:

使用RedisSubscriber类向Redis服务端订阅频道;

使用RedisClient或者redis-cli向Redis服务端发布消息;

Redis服务端收到消息后,会将消息推送给订阅的客户端,由RedisSubscriberHandler进行处理;

RedisSubscriberHandler回调RedisSubscriberListener的onMessage方法处理收到的订阅消息。

客户端的使用

单实例的客户端

创建单实例的Redis客户端需要使用RedisClientBuilder构建器,并提供Redis服务的主机名和监听端口:

使用RedisClient执行Redis命令操作:

哨兵模式的客户端

创建哨兵模式的Redis客户端需要使用SentinelRedisClientBuilder构建器,并提供哨兵监控的主从集群名称masterName和哨兵实例的地址列表:

集群模式的客户端

创建集群模式的Redis客户端需要使用ClusterRedisClientBuilder构建器,并提供集群实例的地址列表:

性能测试

测试用例

为了便于对比性能,仓颉redis-sdk项目提供了压测客户端,位于项目根目录下的benchmark/mutli_thread目录。使用方式如下:

export cjHeapSize=2048mb benchmark/build/release/bin/main --host=Redis服务的主机名 --port=Redis服务的端口 --maxConnections=Socket连接数 --password=Redis服务的密码 --threadCount=客户端并发的线程数 --totalRequestCount=压测总请求数

测试环境

性能测试时使用两台PC机器,一台运行Redis服务,一台运行压测客户端,两台机器的配置如下:

  • 操作系统:CentOS 8.4

  • CPU:Intel(R) Core(TM) i7-8700 @ 3.20GHz

  • 内存:16GB

  • Redis版本:7.2.3

  • 仓颉SDK版本:0.51.4

测试数据

为了对比仓颉redis-sdk客户端和Java Jedis客户端的性能,选取如下两个经典场景:

  • 场景一:200个客户端线程/协程,1个Socket连接

  • 场景二:200个客户端线程/协程,3个Socket连接

其余场景,感兴趣的读者可以下载压测用例自行对比。

1、200个客户端线程/协程,1个Socket连接

2、200个客户端线程/协程,3个Socket连接

3、测试结论

综合以上测试数据TPS的平均值,对比如下:

在多线程1条Socket连接的场景,仓颉redis-sdk客户端比Java Jedis客户端TPS高29.21倍;多线程3条Socket连接的场景,仓颉redis-sdk客户端比Java Jedis客户端TPS高15.87倍。仓颉redis-sdk客户端使用单条Socket连接就能达到较高的TPS。

造成这种差距的主要原因一方面是仓颉语言本身的高性能优势,另外一方面是客户端在设计和实现上的差异:Jedis客户端默认采用同步模式,每次只能发送一个请求并等待其响应;并且Jedis的客户端连接不支持多线程同时使用,需要使用连接池来管理Jedis连接以应对多线程环境。仓颉Redis客户端默认使用Pipeline机制,支持在一个连接上串行发送多个请求并异步接收响应,能有效减少线程之间的锁的竞争并提高吞吐率;并且仓颉Redis客户端支持多线程共享使用,无需引入连接池来协调多个线程对连接资源的访问控制,规避了由连接池引入的线程同步开销及潜在的锁竞争问题,提升了系统效率和响应速度。

共建仓颉生态

基于仓颉语言完备的产品文档和先进的开发工具链,我们团队迅速完成了仓颉Redis客户端的开发和测试工作,达到了Java语言Redis客户端的同等能力。通过此次为社区贡献仓颉原生Redis客户端的探索与实践,我们深刻体会到了仓颉编程语言的多重优势:

拥有现代编程语言的核心特性,如类型推断、自动内存管理以及元编程等,使得编程更加高效和灵活。

强大的运行时安全性,从根本上消除了空指针的隐患,保障了代码的稳健性。

易于扩展,仓颉的扩展功能(Extensions)允许为现有类添加新功能,无需继承或使用装饰模式,使代码简洁、易读,且维护性更好。

支持轻量级线程模型,多线程高IO场景具有较好的性能表现。

内置单元测试支持,易于开发高效稳定的代码。

内置诊断工具,极大地方便了性能优化和问题排查。

仓颉生态的持续发展仍需我们共同努力,我们诚挚地呼吁更多的开发者加入仓颉社区,共同为仓颉生态的建设贡献自己的力量。

关于团队

我们是北京宝兰德软件股份有限公司中间件系列产品的开发团队。北京宝兰德软件股份有限公司成立于2008年,是在科创板上市的高新技术软件企业,领先的基础软件供应商,专注于中间件、智能运维等基础软件和基于大数据、人工智能行业通用软件产品及解决方案研发推广,是CNCF/LF、Eclipse、CCSA、CESA、ITSS等关键组织、协会成员,参与了中间件、云计算、大数据、人工智能、智能运维等领域多项标准的制定和推广。公司产品方案广泛应用于运营商、金融、政府、能源等国计民生行业,中间件相关产品方案在运营商业务支撑域部署规模排名遥遥领先,金融、党政、能源等信创市场领先。

宝兰德自成立以来,一直对基础设施中间件类软件及智能运维软件领域核心技术的发展持续跟踪并进行深入研究开发,通过不断加大技术研究、产品开发投入力度,对产品技术不断进行改进和创新,形成应用架构基础设施软件和智能运维系列产品,包括:BESWare中间件系列、CloudLink云计算系列、DataLink大数据系列、AILink人工智能系列、WebGate 融合监控系列、OpsLink智能运维系列等六个系列产品品牌。经过多年的产品方案积累,宝兰德逐渐成为融合“建”、“运”一体的基础软件和方案供应商,赋能数字经济数字基础设施和数据资源体系建设。

宝兰德积极拥抱仓颉语言。宝兰德核心产品BES Ware系列中间件,包括分布式缓存数据库BCS、传统消息中间件BES MQ,以及云原生消息流平台BES CloudMQ,均全面融入仓颉语言生态,提供了仓颉语言客户端支持。简化了企业级应用向仓颉开发环境的迁移流程,不仅让企业能够充分利用仓颉语言的高效、安全及灵活性,还促进了现有中间件架构与仓颉语言环境的无缝融合与效能提升,为企业数字化转型注入了新的活力。

结语

仓颉编程语言是一个支持高效、安全、全场景应用开发的现代编程语言,具有强安全、高性能、高效率、全场景等特点。Cangjie-TPC(Third Party Components)用于汇集基于仓颉编程语言开发的开源三方库,帮助开发者方便、快捷、高质量构建仓颉程序。

仓颉生态的持续发展仍需我们共同努力,我们诚挚地呼吁更多的开发者加入仓颉社区,共同为仓颉生态的建设贡献自己的力量。宝兰德将携手华为持续为仓颉社区贡献力量。