redis客户端库redis++在嵌入式Linux下的交叉编译及使用

在开发过程中,我们经常会遇到需要在嵌入式Linux系统上与Redis进行交互的需求。因此选择一个适合的Redis客户端库就显得尤为重要。下面介绍下c++中有名的redis-plus-plus(redis++)三方库在嵌入式linux下的交叉编译及使用。该库底层是基于hiredis的良好封装,具备良好的性能。

在C++环境下,hiredishirredisvip是最基础的选择,但它们并没有封装连接池、自动重连等功能,这使得它们在复杂场景中显得力不从心。特别是hiredis,它本身不是线程安全的,每个线程应该维护自己的连接对象,不可在多个线程之间共享一个连接对象。多个线程同时使用同一个连接对象会导致数据竞争和不确定的行为。

还有其他一些库如cloredis,它能够支持集群和单机模式,并且自带连接池功能,但它对Redis哨兵模式的支持并不理想。而redisplus plus这个库,不仅支持Redis的大多数功能,而且提供了更高级的封装和特性,比如自动重连、连接池、哨兵模式等,极大地方便了我们的开发工作。

Redis++介绍

redis++是Redis官网推荐的C++连接库之一,其开源地址为:https://github.com/sewenew/redis-plus-plus。这个库基于hiredis,但提供了更为强大的功能和更好的使用体验。它支持Redis的大多数特性,并对这些特性进行了C++风格的封装,使得在C++项目中使用Redis更加得心应手。

准备交叉编译工具链

在嵌入式Linux开发中,我们通常需要进行交叉编译。这里以ARM和RISC-V架构为例,提供一个配置文件toolchain.cmake,用于设置交叉编译工具链。

bash 复制代码
# 交叉编译工具链配置文件
# 用于嵌入式Linux系统和RISC-V MCU的交叉编译

# 设置系统名称
set(CMAKE_SYSTEM_NAME Linux)

# 设置处理器架构变量,可以通过命令行参数传入
# 例如: cmake -DTARGET_ARCH=arm ..
if(NOT DEFINED TARGET_ARCH)
    set(TARGET_ARCH "arm" CACHE STRING "Target architecture (arm or riscv)")
endif()

# 设置ARM工具链路径
set(ARM_TOOLCHAIN_PATH "/opt/fsl-imx-x11/4.1.15-2.1.0/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi")
# 设置RISC-V工具链路径
set(RISCV_TOOLCHAIN_PATH "/opt/tronlong/tina5.0_v1.0/rtos/lichee/rtos/tools/riscv64-elf-x86_64-20201104")

# 根据目标架构设置主工具链
if(${TARGET_ARCH} STREQUAL "arm")
    # ARM Linux工具链配置
    set(CMAKE_C_COMPILER ${ARM_TOOLCHAIN_PATH}/arm-poky-linux-gnueabi-gcc)
    set(CMAKE_CXX_COMPILER ${ARM_TOOLCHAIN_PATH}/arm-poky-linux-gnueabi-g++)
    set(CMAKE_FIND_ROOT_PATH /opt/fsl-imx-x11/4.1.15-2.1.0/sysroots/cortexa7hf-neon-poky-linux-gnueabi/)
    set(CMAKE_SYSTEM_PROCESSOR arm)
    
    # 设置额外的编译标志
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv7-a -mfloat-abi=hard -mfpu=neon" CACHE STRING "" FORCE)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv7-a -mfloat-abi=hard -mfpu=neon" CACHE STRING "" FORCE)
    
    # 设置链接器
    set(CMAKE_LINKER ${CMAKE_C_COMPILER})
    set(CMAKE_AR ${ARM_TOOLCHAIN_PATH}/arm-poky-linux-gnueabi-ar)
    set(CMAKE_RANLIB ${ARM_TOOLCHAIN_PATH}/arm-poky-linux-gnueabi-ranlib)
    
elseif(${TARGET_ARCH} STREQUAL "riscv")
    # RISC-V工具链配置 (C906核心)
    set(CMAKE_C_COMPILER ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-gcc)
    set(CMAKE_CXX_COMPILER ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-g++)
    set(CMAKE_FIND_ROOT_PATH ${RISCV_TOOLCHAIN_PATH}/riscv64-unknown-elf)
    set(CMAKE_SYSTEM_PROCESSOR riscv)
    
    # 设置RISC-V特定的编译标志 (C906核心)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=rv64gcv0p7 -mabi=lp64d" CACHE STRING "" FORCE)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=rv64gcv0p7 -mabi=lp64d" CACHE STRING "" FORCE)
    
    # 设置链接器
    set(CMAKE_LINKER ${CMAKE_C_COMPILER})
    set(CMAKE_AR ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-ar)
    set(CMAKE_RANLIB ${RISCV_TOOLCHAIN_PATH}/bin/riscv64-unknown-elf-ranlib)
    
else()
    message(FATAL_ERROR "不支持的目标架构: ${TARGET_ARCH}. 请使用 'arm' 或 'riscv'")
endif()

# 设置查找规则
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

# 禁用系统库路径
set(CMAKE_SKIP_RPATH TRUE)

# 设置交叉编译环境的库和头文件搜索路径
set(CMAKE_SYSROOT ${CMAKE_FIND_ROOT_PATH}) 

# 关键!!!,不能漏掉这个设置,如果需要安装到特定路径,特别是三方库安装需要到这里找依赖
set(CMAKE_FIND_ROOT_PATH /root/arm_install)

这个配置文件设置了目标系统的名称、处理器架构、工具链路径等重要信息。根据需要,我们可以选择编译针对ARM或RISC-V架构的库文件。

交叉编译Hiredis库

首先,我们需要交叉编译hiredis库,因为redis++是基于hiredis的。在项目根目录下创建一个build目录,并在其中执行以下命令:

bash 复制代码
cmake .. -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake -DTARGET_ARCH=arm -DCMAKE_INSTALL_PREFIX=${HOME}/arm_install
make
make install

这样,hiredis库就会被交叉编译并安装到${HOME}/arm_install目录下。

交叉编译Redis++库

接下来,我们使用相同的工具链文件来交叉编译redis++。在redis++的源码目录下创建一个build目录,并在其中执行以下命令:

bash 复制代码
cmake .. -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake -DTARGET_ARCH=arm -DCMAKE_INSTALL_PREFIX=${HOME}/arm_install -DCMAKE_PREFIX_PATH=${HOME}/arm_install -DREDIS_PLUS_PLUS_CXX_STANDARD=11 -DREDIS_PLUS_PLUS_BUILD_TEST=OFF
make
make install

这里,我们启用了C++11标准,并且关闭了测试功能,以加快编译速度。编译完成后,redis++库也会被安装到${HOME}/arm_install目录下。

使用Redis++库

下面是一个简单的示例代码,展示了如何使用redis++库。这个示例演示了如何连接到Redis服务器,并进行一些基本的操作,比如读取Hash数据结构中的内容。

cpp 复制代码
#include <unistd.h>
#include <chrono>
#include <tuple>
#include <iostream>
#include <vector>
#include <map>
#include <unordered_set>
#include <sw/redis++/redis++.h>
#include <sw/redis++/sentinel.h>
#include <sw/redis++/connection.h>
#include <sw/redis++/connection_pool.h>

using namespace sw::redis;
using namespace std::chrono;

int main() {
    // 设置连接选项
    ConnectionOptions connection_options;
    connection_options.host = "192.168.11.85";  // 必需,Redis服务器地址
    connection_options.port = 16379; // 可选,默认端口是6379
    connection_options.db = 5;  // 可选,默认使用第0个数据库

    // 设置连接池选项
    ConnectionPoolOptions pool_options;
    pool_options.size = 3;  // 连接池大小,即最大连接数
    pool_options.wait_timeout = std::chrono::milliseconds(100);

    // 创建Redis连接实例
    Redis redis(connection_options, pool_options);

    // 使用hscan命令遍历Hash中的元素
    auto cursor = 0LL;
    auto pattern = "*";
    auto count = 5;
    std::map<std::string, std::string> hashs;
    while (true) {
        cursor = redis.hscan("FORWARD.PLAT.DETAIL", cursor, pattern, count, std::inserter(hashs, hashs.begin()));

        if (cursor == 0) {
            break;
        }
    }

    if(hashs.size() < 1) {
        printf("我们没有获取到任何数据!\n");
    } else {
        for(auto it1 = hashs.begin(); it1 != hashs.end(); it1++) {
            std::cout << "Plat ID:" << it1->first << std::endl;
            std::cout << "Plat UserName & Password:" << it1->second << std::endl;
        }
    }

    // 使用hget命令获取Hash中指定key的value
    OptionalString strValue = redis.hget("XNY.CARINFO", "CRC01211711100232");
    if (strValue) {
        std::cout << "CRC01211711100232 的 vin:" << *strValue << std::endl;
        std::string straa = *strValue;
        if(straa.empty()) {
            std::cout << "我们没有获取到任何数据!" << std::endl;
        } else {
            std::cout << "---- CRC01211711100232 的 details:" << straa << std::endl;
        }
    } else {
        std::cout << "CRC01211711100232 的 vin 不存在!" << std::endl;
    }

    // 使用hincrby命令对Hash中的某个数值字段进行自增操作
    std::cout << "---- 下面我们来试试hincrby ---- " << std::endl;

    auto cursor2 = 0LL;
    auto pattern2 = "*";
    auto count2 = 20;
    std::map<std::string, std::string> vv;
    std::vector<std::string> vlist;
    while (true) {
        cursor2 = redis.hscan("FORWARD.LIST.002", cursor2, pattern2, count2, std::inserter(vv, vv.begin()));

        if (cursor2 == 0) {
            break;
        }
    }

    for(auto it1 = vv.begin(); it1 != vv.end(); it1++) {
        vlist.push_back(it1->first);
    }

    for(auto uu = vlist.begin(); uu != vlist.end(); uu++) {
        std::cout << *uu << std::endl;
    }

    return 0;
}

在这段代码中,我们首先设置了连接选项和连接池选项,然后创建了一个Redis对象。接着,我们使用hscan命令遍历了一个Hash数据结构中的所有元素,并使用hget命令获取了一个特定key的value。最后,我们尝试使用hincrby命令对一个数值字段进行了自增操作。

结论

通过使用redis++库,我们可以更方便地在嵌入式Linux系统中与Redis进行交互。它提供了丰富的功能和易于使用的接口,极大地方便了开发工作。希望这篇博文能够帮助到需要在嵌入式Linux系统中使用Redis的开发者。

相关推荐
sukida10011 分钟前
BIOS主板(非UEFI)安装fedora42的方法
linux·windows·fedora
球求了26 分钟前
C++:继承机制详解
开发语言·c++·学习
●^●30 分钟前
Linux 权限修改详解:chmod 命令与权限数字的秘密
linux
小光学长34 分钟前
基于vue框架的电信用户业务管理系统的设计与实现8ly70(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库
程序员不想YY啊1 小时前
MySQL元数据库完全指南:探秘数据背后的数据
数据库·mysql·oracle
数据最前线1 小时前
Doris表设计与分区策略:让海量数据管理更高效
数据库
超爱笑嘻嘻1 小时前
shared_ptr八股收集 C++
c++
时光追逐者1 小时前
MongoDB从入门到实战之MongoDB快速入门(附带学习路线图)
数据库·学习·mongodb
我想进大厂1 小时前
图论---朴素Prim(稠密图)
数据结构·c++·算法·图论
头顶秃成一缕光1 小时前
Redis的主从模式和哨兵模式
数据库·redis·缓存