1. Redis client library adapted to C++ language
Use the C++ language officially recommended by redis redis client: redis_plus_plus official website
redis_plus_plus features are as follows:
This is a C++ client library for Redis. It's based on hiredis, and is compatible with C++17, C++14, and C++11. NOTE: I'm not a native speaker. So if the documentation is unclear, please feel free to open an issue or pull request. I'll respond ASAP. Features Most commands for Redis. Connection pool. Redis scripting. Thread safe unless otherwise stated. Redis publish/subscribe. Redis pipeline. Redis transaction. Redis Cluster. Redis Sentinel. STL-like interfaces. Generic command interface. Redis Stream. Redlock. Redis ACLs. TLS/SSL support.
2. Compile and install redis_plus_plus under Linux
Refer to the “Installation” section of redis_plus_plus official readme to install.
The compilation environment is: Ubuntu 18.04, gcc 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04).
redis_plus_plus depends on hiredis (version >= v0.12.1), so the compilation order is: first compile hiredis, then compile redis_plus_plus.
2.1 Compile hiredis
The official tutorial clearly states “Do not install multiple versions of hiredis”! It has already been installed, please search for the removal method by yourself.
Compile the latest version and install it to the default path (/usr/local). The entire compilation process is completed within 1 minute
$ git clone
https://github.com/redis/hiredis.git$ cd hiredis
$ make
$ sudo make install
make install only does the following simple operations. If you want to remove the installation of hiredis, you only need to do the reverse operation of the following operations.
2.2 Compile redis_plus_plus
The default compilation generates dynamic and static libraries, using the C++17 standard (note: your application must also use the C++17 version, that is, the redis_plus_plus library and the application need to use the same C++ language standard). redis_plus_plus supports C++11, C++14, C++17 language standards.
If cmake is not installed, you can install it yourself: $ sudo apt install cmake
The whole process of git clone takes a long time, and the rest of the operations are completed within 2 minutes.
$ git clone
https://github.com/sewenew/redis-plus-plus.git$ cd redis-plus-plus
$ mkdir build
$ cd build
$ cmake -DREDIS_PLUS_PLUS_CXX_STANDARD=17 .. #Use C++17 version, C++11 is modified to 11, C++14 is modified to 14
$ make
$ sudo make install
make install did the following:
dog@dog:build$ sudo make install
[ 47%] Built target redis ++
[ 94%] Built target redis + + _static
[100%] Built target test_redis++
Install the project…
— Install configuration: “Release”
— Installing: /usr/local/lib/libredis++ + .a
— Installing: /usr/local/lib/libredis++.so.1.3.3
— Installing: /usr/local/lib/libredis++.so.1
— Installing: /usr/local/lib/libredis++.so
— Set runtime path of “/usr/local/lib/libredis + + .so.1.3.3” to “”
— Installing: /usr/local/share/cmake/redis + + /redis + + -targets.cmake
— Installing: /usr/local/share/cmake/redis + + /redis + + -targets-release.cmake
— Installing: /usr/local/include/sw/redis++ /cmd_formatter.h
— Installing: /usr/local/include/sw/redis++ /command.h
— Installing: /usr/local/include/sw/redis++ /command_args.h
— Installing: /usr/local/include/sw/redis++ /command_options.h
— Installing: /usr/local/include/sw/redis++ /connection.h
— Installing: /usr/local/include/sw/redis++ /connection_pool.h
— Installing: /usr/local/include/sw/redis++ /errors.h
— Installing: /usr/local/include/sw/redis++ /pipeline.h
— Installing: /usr/local/include/sw/redis + + /queued_redis.h
— Installing: /usr/local/include/sw/redis + + /queued_redis.hpp
— Installing: /usr/local/include/sw/redis + + /redis + + .h
— Installing: /usr/local/include/sw/redis++ /redis.h
— Installing: /usr/local/include/sw/redis++ /redis.hpp
— Installing: /usr/local/include/sw/redis++ /redis_cluster.h
— Installing: /usr/local/include/sw/redis++ /redis_cluster.hpp
— Installing: /usr/local/include/sw/redis++ /reply.h
— Installing: /usr/local/include/sw/redis++ /sentinel.h
— Installing: /usr/local/include/sw/redis++ /shards.h
— Installing: /usr/local/include/sw/redis++ /shards_pool.h
— Installing: /usr/local/include/sw/redis++ /subscriber.h
— Installing: /usr/local/include/sw/redis++ /transaction.h
— Installing: /usr/local/include/sw/redis++ /utils.h
— Installing: /usr/local/include/sw/redis + + /tls.h
— Installing: /usr/local/include/sw/redis++ /cxx_utils.h
— Installing: /usr/local/share/cmake/redis + + /redis + + -config.cmake
— Installing: /usr/local/share/cmake/redis + + /redis + + -config-version.cmake
— Installing: /usr/local/lib/pkgconfig/redis++.pc
3. Test redis_plus_plus
Use the official example for testing.
3.1 Start redis_server
$ redis-server
View redis_server status:
$ systemctl restart redis-server # restart
$ systemctl stop redis-server # stop
$ systemctl start redis-server # start
3.2 Test code
#include <sw/redis + + /redis + + .h> #include <iostream> #include <unordered_set> #include <algorithm> using namespace std; using namespace sw::redis; // cout << vector template <typename T> std::ostream & amp;operator<<(std::ostream & amp;out, const std::vector<T> & amp;v) { if (!v. empty()) { out << '['; std::copy(v.begin(), v.end(), std::ostream_iterator<T>(out, ", ")); out << "\b\b]"; // delete ", " at the end } return out; } // cout << unordered_map template <typename T, typename U> std::ostream & amp;operator<<(std::ostream & amp;out, const std::unordered_map<T, U> & amp;umap) { out << '['; for (auto item : umap) { out << "(" << item.first << "," << item.second << "),"; } out << "\b]"; // delete "," at the end return out; } // cout << unordered_set template <typename T> std::ostream & amp;operator<<(std::ostream & amp;out, const std::unordered_set<T> & amp;uset) { out << '('; for (auto item : uset) { cout << item << ","; } out << "\b)"; // delete "," at the end return out; } int main() { try { // Create an Redis object, which is movable but NOT copyable. auto redis = Redis("tcp://127.0.0.1:6379"); /// ***** STRING commands ***** redis.set("key", "val"); auto val = redis.get("key"); // val is of type OptionalString. See 'API Reference' section for details. if (val) { // Dereference val to get the returned value of std::string type. std::cout << *val << std::endl; } // else key doesn't exist. /// ***** LIST commands ***** // std::vector<std::string> to Redis LIST. std::vector<std::string> vec = {"a", "b", "c"}; redis.rpush("list", vec.begin(), vec.end()); // std::initializer_list to Redis LIST. redis.rpush("list", {"a", "b", "c"}); // Redis LIST to std::vector<std::string>. vec. clear(); redis.lrange("list", 0, -1, std::back_inserter(vec)); cout << "list: " << vec << endl; /// ***** HASH commands ***** redis.hset("hash", "field", "val"); // Another way to do the same job. redis.hset("hash", std::make_pair("field", "val")); // std::unordered_map<std::string, std::string> to Redis HASH. std::unordered_map<std::string, std::string> m = { {"field1", "val1"}, {"field2", "val2"}}; redis.hmset("hash", m.begin(), m.end()); // Redis HASH to std::unordered_map<std::string, std::string>. m. clear(); redis.hgetall("hash", std::inserter(m, m.begin())); cout << "hash:" << m << endl; // Get value only. // NOTE: since field might NOT exist, so we need to parse it to OptionalString. std::vector<OptionalString> vals; redis.hmget("hash", {"field1", "field2"}, std::back_inserter(vals)); /// ***** SET commands ***** redis.sadd("set", "m1"); // std::unordered_set<std::string> to Redis SET. std::unordered_set<std::string> set = {"m2", "m3"}; redis.sadd("set", set.begin(), set.end()); // std::initializer_list to Redis SET. redis.sadd("set", {"m2", "m3"}); // Redis SET to std::unordered_set<std::string>. set. clear(); redis.smembers("set", std::inserter(set, set.begin())); cout << "set:" << set << endl; if (redis.sismember("set", "m1")) { std::cout << "m1 exists" << std::endl; } // else NOT exist. /// ***** SORTED SET commands ***** redis.zadd("sorted_set", "m1", 1.3); // std::unordered_map<std::string, double> to Redis SORTED SET. std::unordered_map<std::string, double> scores = { {"m2", 2.3}, {"m3", 4.5}}; redis.zadd("sorted_set", scores.begin(), scores.end()); // Redis SORTED SET to std::vector<std::pair<std::string, double>>. // NOTE: The return results of zrangebyscore are ordered, if you save the results // in to `std::unordered_map<std::string, double>`, you'll lose the order. std::vector<std::pair<std::string, double>> zset_result; redis.zrangebyscore("sorted_set", UnboundedInterval<double>{}, // (-inf, + inf) std::back_inserter(zset_result)); // Only get member names: // pass an inserter of std::vector<std::string> type as output parameter. std::vector<std::string> without_score; redis.zrangebyscore("sorted_set", BoundedInterval<double>(1.5, 3.4, BoundType::CLOSED), // [1.5, 3.4] std::back_inserter(without_score)); // Get both member names and scores: // pass an back_inserter of std::vector<std::pair<std::string, double>> as output parameter. std::vector<std::pair<std::string, double>> with_score; redis.zrangebyscore("sorted_set", BoundedInterval<double>(1.5, 3.4, BoundType::LEFT_OPEN), // (1.5, 3.4] std::back_inserter(with_score)); /// ***** SCRIPTING commands ***** // Script returns a single element. auto num = redis.eval<long long>("return 1", {}, {}); // Script returns an array of elements. std::vector<std::string> nums; redis.eval("return {ARGV[1], ARGV[2]}", {}, {"1", "2"}, std::back_inserter(nums)); // mset with TTL auto mset_with_ttl_script = R"( local len = #KEYS if (len == 0 or len + 1 ~= #ARGV) then return 0 end local ttl = tonumber(ARGV[len + 1]) if (not ttl or ttl <= 0) then return 0 end for i = 1, len do redis. call("SET", KEYS[i], ARGV[i], "EX", ttl) end return 1 )"; // Set multiple key-value pairs with TTL of 60 seconds. auto keys = {"key1", "key2", "key3"}; std::vector<std::string> args = {"val1", "val2", "val3", "60"}; redis.eval<long long>(mset_with_ttl_script, keys.begin(), keys.end(), args.begin(), args.end()); /// ***** Pipeline ***** //Create a pipeline. auto pipe = redis. pipeline(); // Send mulitple commands and get all replies. auto pipe_replies = pipe.set("key", "value") .get("key") .rename("key", "new-key") .rpush("list", {"a", "b", "c"}) .lrange("list", 0, -1) .exec(); // Parse reply with reply type and index. auto set_cmd_result = pipe_replies.get<bool>(0); auto get_cmd_result = pipe_replies.get<OptionalString>(1); // rename command result pipe_replies. get<void>(2); auto rpush_cmd_result = pipe_replies.get<long long>(3); std::vector<std::string> lrange_cmd_result; pipe_replies.get(4, back_inserter(lrange_cmd_result)); /// ***** Transaction ***** //Create a transaction. auto tx = redis.transaction(); // Run multiple commands in a transaction, and get all replies. auto tx_replies = tx.incr("num0") .incr("num1") .mget({"num0", "num1"}) .exec(); // Parse reply with reply type and index. auto incr_result0 = tx_replies.get<long long>(0); auto incr_result1 = tx_replies.get<long long>(1); std::vector<OptionalString> mget_cmd_result; tx_replies.get(2, back_inserter(mget_cmd_result)); /// ***** Generic Command Interface ***** // There's no *Redis::client_getname* interface. // But you can use *Redis::command* to get the client name. val = redis.command<OptionalString>("client", "getname"); if (val) { std::cout << *val << std::endl; } // Same as above. auto getname_cmd_str = {"client", "getname"}; val = redis.command<OptionalString>(getname_cmd_str.begin(), getname_cmd_str.end()); // There's no *Redis::sort* interface. // But you can use *Redis::command* to send sort the list. std::vector<std::string> sorted_list; redis.command("sort", "list", "ALPHA", std::back_inserter(sorted_list)); // Another *Redis::command* to do the same work. auto sort_cmd_str = {"sort", "list", "ALPHA"}; redis.command(sort_cmd_str.begin(), sort_cmd_str.end(), std::back_inserter(sorted_list)); /// ***** Redis Cluster ***** // Create a RedisCluster object, which is movable but NOT copyable. auto redis_cluster = RedisCluster("tcp://127.0.0.1:7000"); // RedisCluster has similar interfaces as Redis. redis_cluster.set("key", "value"); val = redis_cluster. get("key"); if (val) { std::cout << *val << std::endl; } // else key doesn't exist. // Keys with hash-tag. redis_cluster.set("key{tag}1", "val1"); redis_cluster.set("key{tag}2", "val2"); redis_cluster.set("key{tag}3", "val3"); std::vector<OptionalString> hash_tag_res; redis_cluster.mget({"key{tag}1", "key{tag}2", "key{tag}3"}, std::back_inserter(hash_tag_res)); } catch (const Error &e) { // Error handling. } return 0; }
3.3 Build redis client app
Use a static library:
g++ -std=c++17 -o app main.cpp /path/to/your/libredis++ + .a /path/to/your/libhiredis.a -pthread
Use a dynamic library:
$ g++ -std=c++17 -o app main.cpp -lredis++ -lhiredis -pthread
The running prompt cannot find “libredis++.so.1:
$ ./app
./app: error while loading shared libraries: libredis + + .so.1: cannot open shared object file: No such file or directory
Because the path of libredis++.so.1 cannot be found, its path needs to be added to LD_LIBRARY_PATH. The default installation path of the redis_plus_plus library is as follows:
Add the “/usr/local/lib” path to LD_LIBRARY_PATH at the end of “~/.bashrc”:
Save the “~/.bashrc” modification, close the terminal terminal, restart and open a new terminal terminal, and run the app in the new terminal: