当前位置: 首页 > news >正文

C++客服端访问redis

完整C++调用redisAPI示例代码:https://gitee.com/HGtz2222/bitproject.git

Redis协议

网络通信中,不同层都有协议,传输层TCP/UDP,网络层IP,这些协议都是定好的,改不了。

应用层则不一样,为了满足需求可以自定义符合自身要求的协议。redis则有自己定制的协议,RESP则是redis的协议。

redis用RESP协议向应用层协议发送请求,服务器就会按照协议进行解析得知请求放回响应。

选择redis-plus-plus

redis C++客服端->redis-plus-plus。

功能全面:支持 Redis 所有核心命令(字符串、列表、集合、哈希、有序集合等),以及集群、哨兵模式。
易用性强:API 设计简洁直观,与 Redis 原生命令高度一致,降低学习成本。
性能优秀:底层基于 hiredis,保留 C 语言的高性能特性,同时支持异步操作(本文以同步操作为主,满足大部分场景需求)。
跨平台:支持 Linux(Ubuntu/CentOS)、macOS 等主流操作系统,编译配置简单。

环境搭建

ubuntu一般就会有下面

GCC/G++ 7.0+(支持 C++17,redis-plus-plus 依赖 C++17 特性)
Git(用于下载源码)
CMake 3.0+(用于编译 redis-plus-plus,CentOS 需单独升级)
Redis 服务(本地或远程,确保 6379 端口可访问,本文以本地 Redis 为例)

安装hiredis(C语言的依赖库)

redis-plus-plus 基于 hiredis 实现,必须先安装 hiredis。直接通过系统包管理器安装,无需手动编译。

sudo apt update && sudo apt install libhiredis-dev

下载redis-plus-plus源码

git clone https://github.com/sewenew/redis-plus-plus.git
cd redis-plus-plus  # 进入源码目录

编译安装redis-plus-plus

1.创建目录,防止污染源码

mkdir build && cd build

2.生成makefile

cmake ..

3.编译

cmake ..

4.安装到系统目录

sudo make install

连接Redis

redis通信不需要自己实现,有现成的库可以使用。

#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>#include <sw/redis++/redis++.h>using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;int main()
{//创建 Redis 对象的时候,需要在构造函数中,指定 redis 服务器的地址和端口sw::redis::Redis redis("tcp://127.0.0.1:6379");//URL 唯一资源定位符//此时咱们的 redis 客户端和服务端是在同一台主机上,使用本地环回就可以了//使用 ping 方法,让客户端给服务器发一个 PING,然后服务器就会返回一个 PONG,就通过 返回值 获取到string result = redis.ping();cout<< result << endl;return 0;
}

makefile文件

这里是直接链接了静态库

hello: hello.cc
    g++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthread
 
.PHONY:clean
clean:
    rm hello

结果

lfz@instance-hojuqq09:~/WorkPlace/redis-c/test$ ./hello
PONG

Redis-set数据结构实操

bool set(const sw::redis::StringView &key, const sw::redis::StringView &val, bool keepttl, sw::redis::UpdateType type = sw::redis::UpdateType::ALWAYS);

  • const sw::redis::StringView &key:要设置值的键,sw::redis::StringView 是一种只读类型,相比 std::string 针对只读操作进行了优化,在传递键时能提升效率,采用引用传递避免了不必要的拷贝 。
  • const sw::redis::StringView &val:与键 key 对应的要设置的值,同样是 sw::redis::StringView 只读类型,也是通过引用传递。
  • bool keepttl:用于指定在设置新值时,是否保留键原有的过期时间。如果该值为 true,在更新键值对时,原有的过期时间会被保留;如果为 false ,则会根据后续是否设置过期时间相关参数来确定过期时间,若未设置相关过期参数,那么该键就没有过期时间。
  • sw::redis::UpdateType type = sw::redis::UpdateType::ALWAYS:指定更新的类型,是一个枚举类型。默认值为 sw::redis::UpdateType::ALWAYS,表示无论键是否存在,都会进行设置操作。它可能还有其他取值,比如仅在键存在时更新等,具体取决于代码中对该枚举类型的定义。(EXIST就是XX,最后一个就是NX)。

std::optional<sw::redis::StringView> get(const sw::redis::StringView &key);

std::optional<sw::redis::StringView> 类型。若键存在,返回包含对应值的 optional 对象;若键不存在或操作失败,返回空 optional(可通过 has_value() 判断是否成功获取值)。

用cout去打印时需要用optional结构中的value方法获取到能满足cout<<重载函数的参数类型,不然就会报错。

size_t exists(const sw::redis::StringView &key);
// 或支持多个键的重载版本
size_t exists(const std::initializer_list<sw::redis::StringView> &keys);

const sw::redis::StringView &key:要检查的单个键,使用 StringView 类型减少拷贝。
const std::initializer_list<sw::redis::StringView> &keys:要检查的多个键的初始化列表,支持一次性检查多个键。

 

size_t del(const sw::redis::StringView &key);
// 或支持多个键的重载版本
size_t del(const std::initializer_list<sw::redis::StringView> &keys);

  • onst sw::redis::StringView &key:要删除的单个键。
  • const std::initializer_list<sw::redis::StringView> &keys:要删除的多个键的初始化列表。

std::vector<sw::redis::StringView> keys(const sw::redis::StringView &pattern);

const sw::redis::StringView &pattern:匹配模式,支持通配符(如 * 匹配任意字符,? 匹配单个字符,[] 匹配指定范围的字符)

bool expire(const sw::redis::StringView &key, std::chrono::seconds seconds);

  • const sw::redis::StringView &key:要设置过期时间的键。
  • std::chrono::seconds seconds:过期时间(以秒为单位),超过该时间后键会被自动删除

插入迭代器介绍

std::back_inserter(keys)是 C++ 标准库中的一个非常有用的工具,它属于 <iterator>头文件。它的主要作用是为容器(如 std::vectorstd::liststd::deque等)提供一个​​插入迭代器(Insert Iterator)​​,使得可以通过迭代器的方式向容器尾部插入元素,而不需要手动调用 push_back()或 insert()

std::back_inserter

调用容器的 push_back(),适用于 vectorlistdeque等。

底层实现原理

template<typename Container>
class back_insert_iterator {Container* container; // 指向目标容器的指针
public:// 构造函数:保存容器的指针explicit back_insert_iterator(Container& c) : container(&c) {}// 关键:重载 operator=,赋值时调用 push_backback_insert_iterator& operator=(const typename Container::value_type& value) {container->push_back(value); // 实际插入操作return *this;}// 其他迭代器操作(为了兼容算法,实际不做任何事情)back_insert_iterator& operator*() { return *this; }back_insert_iterator& operator++() { return *this; }
};

插入迭代器示例

std::vector<int> dest;
auto iter = std::back_inserter(dest); // 获取插入迭代器// 以下操作会被转换为 dest.push_back(42);
*iter = 42; // 看起来像解引用+赋值,实际调用 operator=

示例

#include <sw/redis++/redis++.h>
#include <cstdio>
#include <string>
#include <vector>
#include <unordered_map>
#include <chrono>// 测试通用命令
void testGenericCommands(sw::redis::Redis& redis) {printf("===================== Generic 系列命令 =====================\n");// 1. EXISTS 命令:判断键是否存在printf("\n【1. EXISTS 命令】\n");redis.flushdb();  // 清空当前数据库(避免干扰测试)bool set_ok = redis.set("key1", "Hello");  // 设置 key1 = Helloprintf("redis < SET key1 \"Hello\"\n");printf("redis > %s\n", set_ok ? "OK" : "(nil)");long long exists_count = redis.exists("key1");  // 判断 key1 是否存在printf("redis < EXISTS key1\n");printf("redis > %lld\n", exists_count);  // 输出 1(存在)exists_count = redis.exists("nosuchkey");  // 判断不存在的键printf("redis < EXISTS nosuchkey\n");printf("redis > %lld\n", exists_count);  // 输出 0(不存在)redis.set("key2", "World");  // 设置 key2 = Worldprintf("redis < SET key2 \"World\"\n");printf("redis > %s\n", set_ok ? "OK" : "(nil)");exists_count = redis.exists({"key1", "key2", "nosuchkey"});  // 批量判断printf("redis < EXISTS key1 key2 nosuchkey\n");printf("redis > %lld\n", exists_count);  // 输出 2(key1、key2 存在)// 2. DEL 命令:删除键printf("\n【2. DEL 命令】\n");redis.flushdb();redis.set("key1", "Hello");redis.set("key2", "World");printf("redis < SET key1 \"Hello\"\nredis > OK\n");printf("redis < SET key2 \"World\"\nredis > OK\n");long long del_count = redis.del({"key1", "key2", "key3"});  // 批量删除printf("redis < DEL key1 key2 key3\n");printf("redis > %lld\n", del_count);  // 输出 2(仅 key1、key2 被删除)// 3. KEYS 命令:模糊查询键printf("\n【3. KEYS 命令】\n");redis.flushdb();// 批量设置键值对std::unordered_map<std::string, std::string> kvs = {{"firstname", "Jack"},{"lastname", "Stuntman"},{"age", "35"}};redis.mset(kvs.begin(), kvs.end());printf("redis < MSET firstname Jack lastname Stuntman age 35\nredis > OK\n");// 查询包含 "name" 的键std::vector<std::string> keys;std::insert_iterator<std::vector<std::string>> ins(keys, keys.begin());redis.keys("*name*", ins);  // * 代表任意字符printf("redis < KEYS *name*\n");int n = 1;for (auto& key : keys) {printf("redis > %d) %s\n", n++, key.c_str());  // 输出 firstname、lastname}// 查询以 "a" 开头、后面跟 2 个字符的键(?? 代表任意单个字符)keys.clear();redis.keys("a??", ins);printf("redis < KEYS a??\n");n = 1;for (auto& key : keys) {printf("redis > %d) %s\n", n++, key.c_str());  // 输出 age}// 4. EXPIRE + TTL 命令:设置过期时间 + 查询剩余时间(秒)printf("\n【4. EXPIRE + TTL 命令】\n");redis.flushdb();redis.set("mykey", "Hello");printf("redis < SET mykey \"Hello\"\nredis > OK\n");bool expire_ok = redis.expire("mykey", std::chrono::seconds(10));  // 10 秒后过期printf("redis < EXPIRE mykey 10\n");printf("redis > %d\n", expire_ok ? 1 : 0);  // 输出 1(设置成功)long long ttl = redis.ttl("mykey");  // 查询剩余时间(秒)printf("redis < TTL mykey\n");printf("redis > %lld\n", ttl);  // 输出 10(接近 10 秒,因执行有延迟)// 5. PTTL 命令:查询剩余时间(毫秒)printf("\n【5. PTTL 命令】\n");redis.flushdb();redis.set("mykey", "Hello");redis.expire("mykey", std::chrono::seconds(10));printf("redis < SET mykey \"Hello\"\nredis > OK\n");printf("redis < EXPIRE mykey 10\nredis > 1\n");long long pttl = redis.pttl("mykey");  // 毫秒级剩余时间printf("redis < PTTL mykey\n");printf("redis > %lld\n", pttl);  // 输出约 10000(10 秒 = 10000 毫秒)// 6. TYPE 命令:判断键的数据类型printf("\n【6. TYPE 命令】\n");redis.flushdb();redis.set("key1", "value");          // 字符串类型redis.lpush("key2", "value");        // 列表类型redis.sadd("key3", "value");         // 集合类型printf("redis < SET key1 \"value\"\nredis > OK\n");printf("redis < LPUSH key2 \"value\"\nredis > 1\n");printf("redis < SADD key3 \"value\"\nredis > 1\n");std::string type = redis.type("key1");printf("redis < TYPE key1\nredis > \"%s\"\n", type.c_str());  // 输出 stringtype = redis.type("key2");printf("redis < TYPE key2\nredis > \"%s\"\n", type.c_str());  // 输出 listtype = redis.type("key3");printf("redis < TYPE key3\nredis > \"%s\"\n", type.c_str());  // 输出 setprintf("===========================================================\n");
}int main() {// 连接本地 Redis(默认端口 6379,无密码)// 若 Redis 有密码,格式为:tcp://:password@127.0.0.1:6379sw::redis::Redis redis("tcp://127.0.0.1:6379");// 测试通用命令testGenericCommands(redis);return 0;
}

http://www.dtcms.com/a/482387.html

相关文章:

  • 用【WPF+Dlib68】实现 侧脸 眼镜虚拟佩戴 - 用平面图表现空间视觉
  • 重庆网站优化排名上海 企业
  • 网站建设的技术需要多少钱上海软件系统开发公司
  • 汽车用颗粒物传感器:市场趋势、技术革新与行业挑战
  • HICom论文阅读
  • Spring Framework源码解析——ServletContextAware
  • 苏州微网站建设公司做镜像网站
  • OpenStack 网络实现的底层细节-PORT/TAP
  • Chrome 安装失败且提示“无可用的更新” 或 “与服务器的连接意外终止”,Chrome 离线版下载安装教程
  • 02-如何使用Chrome工具排查内存泄露问题
  • 通过不同语言建立多元认知,提升创新能力
  • Tomcat 架构解析与线程池优化策略
  • springboot在DTO使用service,怎么写
  • YOLOv1 详解:实时目标检测的开山之作
  • Vue3 + SpringBoot 分片上传与断点续传方案设计
  • CTFSHOW WEB 3
  • 做个网站费用建材营销型的网站
  • POrtSwigger靶场之CSRF where token validation depends on token being present通关秘籍
  • Java 离线视频目标检测性能优化:从 Graphics2D 到 OpenCV 原生绘图的 20 倍性能提升实战
  • 基于 Informer-BiGRUGATT-CrossAttention 的风电功率预测多模型融合架构
  • 如何做旅游网站推销免费企业信息发布平台
  • 基于RBAC模型的灵活权限控制
  • C++内存管理模板深度剖析
  • 新开的公司怎么做网站手机网站设计神器
  • Bootstrap5 选择区间
  • 考研10.5笔记
  • [c++语法学习]Day 9:
  • LeetCode算法日记 - Day 71: 不同路径、不同路径II
  • 掌握string类:从基础到实战
  • 【C++】四阶龙格库塔算法实现递推轨道飞行器位置速度