redox

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介:

Modern, asynchronous, and wicked fast C++11 client for Redis [Build Status] (https://travis-ci.org/hmartiro/redox)

Redox is a C++ interface to the Redis key-value store that makes it easy to write applications that are both elegant and high-performance. Communication should be a means to an end, not something we spend a lot of time worrying about. Redox takes care of the details so you can move on to the interesting part of your project.

Features:

  • Expressive asynchronous and synchronous API, templated by return value
  • Callbacks can be lambdas, class methods, bind expressions, or any std::function
  • Thread-safe - use one client in multiple threads or multiple clients in one
  • Automatic pipelining, even for synchronous calls from separate threads
  • Low-level access when needed
  • Accessible and robust error handling
  • Configurable logging level and output to any ostream
  • Full support for binary data (keys and values)
  • Fast - developed for robotics applications
  • 100% clean Valgrind reports

Redox is built on top of hiredis and libev. It uses only the asynchronous API of hiredis, even for synchronous commands. There is no dependency on Boost or any other libraries.

Benchmarks

Benchmarks are given by averaging the results of ten trials of the speed tests in examples/ on an AWS t2.medium instance running Ubuntu 14.04 (64-bit) and a local Redis server.

  • speed_test_async_multi over TCP: 879,589 commands/s
  • speed_test_async_multi over Unix socket: 901,683 commands/s
  • speed_test_async over TCP: 203,285 commands/s
  • speed_test_async over Unix socket: 301,823 commands/s
  • speed_test_sync over TCP: 21,072 commands/s
  • speed_test_sync over Unix socket: 24,911 commands/s

A mid-range laptop gives comparable results. Numbers can be much higher on a high-end machine.

Tutorial

This section introduces the main features of redox. Look in examples/ for more inspiration.

Hello world

Here is the simplest possible redox program:

#include <iostream>
#include <redox.hpp>

using namespace std;
using namespace redox;

int main(int argc, char* argv[]) {

  Redox rdx;
  if(!rdx.connect("localhost", 6379)) return 1;

  rdx.set("hello", "world!");
  cout << "Hello, " << rdx.get("hello") << endl;

  rdx.disconnect();
  return 0;
}

Compile and run:

$ g++ hello.cpp -o hello -std=c++11 -lredox -lev -lhiredis
$ ./hello
Hello, world!

This example is synchronous, in the sense that the commands don't return until a reply is received from the server.

Asynchronous commands

In a high-performance application, we don't want to wait for a reply, but instead do other work. At the core of Redox is a generic asynchronous API for executing any Redis command and providing a reply callback. The command method accepts a Redis command in the form of an STL vector of strings, and a callback to be invoked when a reply is received or if there is an error.

rdx.command<string>({"GET", "hello"}, [](Command<string>& c) {
  if(c.ok()) {
    cout << "Hello, async " << c.reply() << endl;
  } else {
    cerr << "Command has error code " << c.status() << endl;
  }
});

This statement tells redox to run the command GET hello. The <string> template parameter means that we want the reply to be put into a string and that we expect the server to respond with something that can be put into a string. The full list of reply types is listed in this document and covers convenient access to anything returned from the Redis protocol. The input vector can contain arbitrary binary data.

The second argument is a callback function that accepts a reference to a Command object of the requested reply type. The Command object contains the reply and any error information. If c.ok() is true, the expected reply is accessed fromc.reply() (a string in this case). If c.ok() is false, then the error code is given by c.status(), which can report an error or nil reply, a reply of the wrong type, a send error, etc. The callback is guaranteed to be invoked exactly once, and the memory for the Command object is freed automatically once the callback returns.

Here is a simple example of running GET hello asynchronously ten times:

Redox rdx;

// Block until connected, localhost by default
if(!rdx.connect()) return 1;

auto got_reply = [](Command<string>& c) {
  if(!c.ok()) return;
  cout << c.cmd() << ": " << c.reply() << endl;
};

for(int i = 0; i < 10; i++) rdx.command<string>({"GET", "hello"}, got_reply);

// Do useful work
this_thread::sleep_for(chrono::milliseconds(10));

rdx.disconnect(); // Block until disconnected

The .command() method returns immediately, so this program doesn't wait for a reply from the server - it just pauses for ten milliseconds and then shuts down. If we want to shut down after we get all replies, we could do something like this:

Redox rdx;
if(!rdx.connect()) return 1;

int total = 10; // Number of commands to run
atomic_int count(0); // Number of replies expected
auto got_reply = [&](Command<string>& c) {
  count++;
  if(c.ok()) cout << c.cmd() << " #" << count << ": " << c.reply() << endl;
  if(count == total) rdx.stop(); // Signal to shut down
};

for(int i = 0; i < total; i++) rdx.command<string>({"GET", "hello"}, got_reply);

// Do useful work

rdx.wait(); // Block until shut down complete

This example tracks of how how many replies are received and signals the Redox instance to stop once they all process. We use an std::atomic_int to be safe because the callback is invoked from a separate thread. The stop() method signals Redox to shut down its event loop and disconnect from Redis. The wait() method blocks until stop() has been called and everything is brought down. The disconnect() method used earlier is just a call to stop() and then a call to wait().

Synchronous commands

Redox implements synchronous commands by running asynchronous commands and waiting on them with condition variables. That way, we can reap the benefits of pipelining between synchronous commands in different threads. The commandSync method provides a similar API to command, but instead of a callback returns a Command object when a reply is received.

Command<string>& c = rdx.commandSync<string>({"GET", "hello"});
if(c.ok()) cout << c.cmd() << ": " << c.reply() << endl;
c.free();

When using synchronous commands, the user is responsible for freeing the memory of the Command object by calling c.free(). The c.cmd() method just returns a string representation of the command (GET hello in this case).

Looping and delayed commands

We often want to run commands on regular invervals. Redox provides the commandLoop method to accomplish this. It is easier to use and more efficient than running individual commands in a loop, because it only creates a single Command object.commandLoop takes a command vector, a callback, and an interval (in seconds) to repeat the command. It then runs the command on the given interval until the user calls c.free().

Command<string>& cmd = rdx.commandLoop<string>({"GET", "hello"}, [](Command<string>& c) {
  if(c.ok()) cout << c.cmd() << ": " << c.reply() << endl;
}, 0.1);

this_thread::sleep_for(chrono::seconds(1));
cmd.free();
rdx.disconnect();

Finally, commandDelayed runs a command after a specified delay (in seconds). It does not return a command object, because the memory is automatically freed after the callback is invoked.

rdx.commandDelayed<string>({"GET", "hello"}, [](Command<string>& c) {
  if(c.ok()) cout << c.cmd() << ": " << c.reply() << endl;
}, 1);
this_thread::sleep_for(chrono::seconds(2));

Convenience methods

The four methods commandcommandSynccommandLoop, and commandDelayed form the core of Redox's functionality. There are convenience methods provided that are simple wrappers over the core methods. Some examples of those are .get().set().del(), and .publish(). These methods are nice because they return simple values, and there are no Command objects or template parameters. However, they make strong assumptions about how to deal with errors (ignore or throw exceptions), and since their implementations are a few lines of code it is often easier to create custom convenience methods for your application.

Publisher / Subscriber

Redox provides an API for the pub/sub functionality of Redis. Publishing is done just like any other command using a Redox instance. There is a separate Subscriber class that receives messages and provides subscribe/unsubscribe and psubscribe/punsubscribe methods.

Redox rdx; Subscriber sub;
if(!rdx.connect() || !sub.connect()) return 1;

sub.subscribe("hello", [](const string& topic, const string& msg) {
  cout << topic << ": " << msg << endl;
});

for(int i = 0; i < 10; i++) {
  rdx.publish("hello", "this is a pubsub message");
  this_thread::sleep_for(chrono::milliseconds(500));
}

sub.disconnect(); rdx.disconnect();

strToVec and vecToStr

Redox provides helper methods to convert between a string command and a vector of strings as needed by its API. rdx.strToVec("GET foo") will return an std::vector<std::string> containing GET and foo as entries. rdx.vecToStr({"GET", "foo"}) will return the string GET foo.

No-Wait Mode

Redox provides a no-wait mode, which tells the event loop not to sleep in between processing events. It means that the event thread will run at 100% CPU, but it can greatly improve performance when critical. It is disabled by default and can be enabled with rdx.noWait(true);.

Reply types

These the available template parameters in redox and the Redis return types they can hold. If a given command returns an incompatible type you will get a WRONG_TYPE or NIL_REPLY status.

  • <redisReply*>: All reply types, returns the hiredis struct directly
  • <char*>: Simple Strings, Bulk Strings
  • <std::string>: Simple Strings, Bulk Strings
  • <long long int>: Integers
  • <int>: Integers (careful about overflow, long long int recommended)
  • <std::nullptr_t>: Null Bulk Strings, any other receiving a nil reply will get a NIL_REPLY status
  • <std::vector<std::string>>: Arrays of Simple Strings or Bulk Strings (in received order)
  • <std::set<std::string>>: Arrays of Simple Strings or Bulk Strings (in sorted order)
  • <std::unordered_set<std::string>>: Arrays of Simple Strings or Bulk Strings (in no order)

Installation

Instructions provided are for Ubuntu, but all components are platform-independent.

Build from source

Get the build environment and dependencies:

sudo apt-get install git cmake build-essential
sudo apt-get install libhiredis-dev libev-dev

Build the library:

mkdir build && cd build
cmake ..
make

Install into system directories (optional):

sudo make install

Build examples and test suite

Enable examples using ccmake or the following:

cmake -Dexamples=ON ..
make examples

To run the test suite, first make sure you have gtest set up, then:

cmake -Dtests=ON ..
make test_redox
./test_redox

Build documentation

Redox documentation is generated using doxygen.

cd docs
doxygen

The documentation can then be viewed in a browser at docs/html/index.html.

Build RPM and DEB packages

Basic support to build RPMs and DEBs is in the build system. To build them, issue the following commands:

mkdir release && cd release
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
make package

NOTE: To build RPM packages, you will need rpmbuild.

Contributing

Redox is in its early stages and I am looking for feedback and contributors to make it easier, faster, and more robust. Open issues on GitHub or message me directly.

Redox is not currently recommended for production use. It has no support yet for sentinels or clusters. Feel free to provide them!






本文作者:陈群
本文来自云栖社区合作伙伴rediscn,了解相关信息可以关注redis.cn网站。
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
Linux
Linux(CentOS 7.9)镜像下载
Linux、Centos 7.9、系统镜像
29719 0
LD_LIBRARY_PATH shouldn't contain the current directory when building glibc. Please change the envir
版权声明:本文为 testcs_dn(微wx笑) 原创文章,非商用自由转载-保持署名-注明出处,谢谢。 https://blog.csdn.net/testcs_dn/article/details/45438023 执行# .
4340 0
|
4天前
|
弹性计算 运维 网络安全
上云“加速器”——基于云效流水线快速上线企业门户网站
阿里云提出使用云效将项目代码部署到ECS,快速构建企业门户网站。该方案融合云原生技术和持续交付,通过云效流水线简化从开发到部署的全过程,实现快速迭代。文章详细阐述了技术架构,包括客户端、云解析DNS、VPC、ECS等组件,以及部署流程,包括准备阶段、部署网站服务、解析域名和可选的静态资源加速。此外,还介绍了如何使用云效平台创建流水线,实现自动化构建与部署,以及如何通过一键部署简化流程。整个方案旨在降低运维成本,提高速度和灵活性,同时提供域名备案和SSL证书配置的指导。
104769 63
上云“加速器”——基于云效流水线快速上线企业门户网站
|
5天前
|
Kubernetes 测试技术 应用服务中间件
基于 Nginx Ingress + 云效 AppStack 实现灰度发布
本文将演示结合云效 AppStack,来看下如何在阿里云 ACK 集群上进行应用的 Ingress 灰度发布。
64331 10
|
9天前
|
人工智能 Linux Docker
一文详解几种常见本地大模型个人知识库工具部署、微调及对比选型(1)
近年来,大模型在AI领域崭露头角,成为技术创新的重要驱动力。从AlphaGo的胜利到GPT系列的推出,大模型展现出了强大的语言生成、理解和多任务处理能力,预示着智能化转型的新阶段。然而,要将大模型的潜力转化为实际生产力,需要克服理论到实践的鸿沟,实现从实验室到现实世界的落地应用。阿里云去年在云栖大会上发布了一系列基于通义大模型的创新应用,标志着大模型技术开始走向大规模商业化和产业化。这些应用展示了大模型在交通、电力、金融、政务、教育等多个行业的广阔应用前景,并揭示了构建具有行业特色的“行业大模型”这一趋势,大模型知识库概念随之诞生。
123501 23
|
7天前
|
云计算 存储 数据可视化
阿里云研发工程师:HPC优化实例动手实验讲解
近日,全球领先的云计算厂商阿里云宣布最新HPC优化实例hpc8ae的正式商业化,该实例依托阿里云自研的「飞天+CIPU」架构体系,搭载第四代AMD EPYC处理器,专为高性能计算应用优化,特别适用于计算流体、有限元分析、多物理场模拟等仿真类应用,CAE场景下的性价比最少提升50%。
阿里云研发工程师:HPC优化实例动手实验讲解
|
11天前
|
存储 SQL 搜索推荐
一站式实时数仓Hologres整体能力介绍—2024实时数仓Hologres公开课 01
一站式实时数仓Hologres整体能力介绍—2024实时数仓Hologres公开课 01
|
11天前
|
存储 运维 安全
Greenplum闭源?平滑迁移到 AnalyticDB 开启Data+AI新范式
知名开源 MPP 数据库 Greenplum 由于其丰富的企业级特性和出色的数据处理能力成为很多企业构建数仓的首选。近期 GP 公开 Github 仓库无法访问仅保留只读归档代码,业界纷纷猜测 GP 即将闭源。云原生数仓 AnalyticDB PostgreSQL 版完全掌控内核代码,完全兼容GP语法,全自研计算及存储引擎较比开源GP有五倍性能提升,全自研企业级特性在实时计算、弹性扩展、安全增强、高可用等方面实现对GP的全面超越,并在数仓能力上扩展了向量检索及一站式 RAG 服务,帮助企业快速构建 AI 应用、开启 Data+AI 新范式。
58873 3