redox
Modern, asynchronous, and wicked fast C++11 client for Redis
Overview
Redox provides an elegant high-level interface to Redis that makes it easy to write high-performance applications. It is built on top of hiredis (but uses only its asynchronous API, even for synchronous calls) and libev.
- A single
command()method, templated by the user based on the expected return type - Callbacks are std::functions, so they can capture state - lambdas, member functions, bind expressions
- Thread-safe - use one Redox object in multiple threads or multiple Redox objects in one thread
- Automatic pipelining, even for synchronous calls from separate threads
- Small,
- Access to low-level reply objects when needed
- 100% clean Valgrind reports
Performance Benchmarks
Benchmarks are given by averaging the results of five trials of various programs
in examples/. The results are on an AWS t2.medium instance running Ubuntu 14.04 (64-bit).
During these tests, Redox communicated with a local Redis server over TCP. Results are
slightly faster using Unix sockets.
- 100 repeating asynchronous commands (
speed_test_async_multi): 710,014 commands/s - One repeating asynchronous command (
speed_test_async): 195,159 commands/s - One blocking command in a loop (
speed_test_sync): 23,609 commands/s
Build from source
Instructions provided are for Ubuntu, but Redox is fully platform-independent.
Get the build environment:
sudo apt-get install git cmake build-essential
Get the dependencies:
sudo apt-get install libhiredis-dev libev-dev
For testing:
sudo apt-get install libgtest-dev
Build Redox and examples using CMake (a helper script is provided):
cd redox
./make.sh
Tutorial
Coming soon. For now, look at the example programs located in examples/, and the snippets
posted below.
Basic synchronous usage:
redox::Redox rdx = {"localhost", 6379}; // Initialize Redox
rdx.start(); // Start the event loop
rdx.del("occupation");
if(!rdx.set("occupation", "carpenter")) // Set a key, check if succeeded
cerr << "Failed to set key!" << endl;
cout << "key = occupation, value = \"" << rdx.get("occupation") << "\"" << endl;
rdx.stop(); // Shut down the event loop
Output: key = occupation, value = "carpenter"
The command method launches a command asynchronously. This is the root
of every method in Redox that executes a command:
/**
* Create an asynchronous Redis command to be executed. Return a pointer to a
* Command object that represents this command. If the command succeeded, the
* callback is invoked with a reference to the reply. If something went wrong,
* the error_callback is invoked with an error_code. One of the two is guaranteed
* to be invoked. The method is templated by the expected data type of the reply,
* and can be one of {redisReply*, string, char*, int, long long int, nullptr_t}.
*
* cmd: The command to be run.
* callback: A function invoked on a successful reply from the server.
* error_callback: A function invoked on some error state.
* repeat: If non-zero, executes the command continuously at the given rate
* in seconds, until cancel() is called on the Command object.
* after: If non-zero, executes the command after the given delay in seconds.
* free_memory: If true (default), Redox automatically frees the Command object and
* reply from the server after a callback is invoked. If false, the
* user is responsible for calling free() on the Command object.
*/
template<class ReplyT>
Command<ReplyT>* command(
const std::string& cmd,
const std::function<void(const std::string&, const ReplyT&)>& callback = nullptr,
const std::function<void(const std::string&, int status)>& error_callback = nullptr,
double repeat = 0.0,
double after = 0.0,
bool free_memory = true
);
The command_blocking method is the root of all synchronous calls. It calls command then
uses a condition variable to wait for a reply.
/**
* A wrapper around command() for synchronous use. Waits for a reply, populates it
* into the Command object, and returns when complete. The user can retrieve the
* results from the Command object - ok() will tell you if the call succeeded,
* status() will give the error code, and reply() will return the reply data if
* the call succeeded.
*/
template<class ReplyT>
Command<ReplyT>* command_blocking(const std::string& cmd);