Drogon supports Redis, a very fast, in-memory data store. Which could be used as a database cache or a message broker. Like everything in Drogon, Redis connections are asynchronous. Which ensures Drogon running with very high concurrency even under heavy load.

Redis support depends on the hiredis library. Redis support won’t be available if hiredis is not available when building Drogon.

Creating a client

Redis clients can be created and retrieve pragmatically through app().

  1. app().createRedisClient("127.0.0.1", 6379);
  2. ...
  3. // After app.run()
  4. RedisClientPtr redisClient = app().getRedisClient();

Redis clients can also be created via the configuration file.

  1. "redis_clients": [
  2. {
  3. //name: Name of the client,'default' by default
  4. //"name":"",
  5. //host: Server IP, 127.0.0.1 by default
  6. "host": "127.0.0.1",
  7. //port: Server port, 6379 by default
  8. "port": 6379,
  9. //passwd: '' by default
  10. "passwd": "",
  11. //db index: 0 by default
  12. "db": 0,
  13. //is_fast: false by default, if it is true, the client is faster but user can't call any synchronous interface of it and can't use it outside of the IO threads and the main thread.
  14. "is_fast": false,
  15. //number_of_connections: 1 by default, if the 'is_fast' is true, the number is the number of connections per IO thread, otherwise it is the total number of all connections.
  16. "number_of_connections": 1,
  17. //timeout: -1.0 by default, in seconds, the timeout for executing a command.
  18. //zero or negative value means no timeout.
  19. "timeout": -1.0
  20. }
  21. ]

Using Redis

execCommandAsync executes Redis commands in an asynchronous manner. It takes at least 3 parameters, the first and second are callback whom will be called when the Redis command succeed or failed. The third being the command it self. The command could be a C-style format string. And the rests are arguments for the format string. For example, to set the key name to drogon:

  1. redisClient->execCommandAsync(
  2. [](const drogon::nosql::RedisResult &r) {},
  3. [](const std::exception &err) {
  4. LOG_ERROR << "something failed!!! " << err.what();
  5. },
  6. "set name drogon");

Or set myid to 587d-4709-86e4

  1. redisClient->execCommandAsync(
  2. [](const drogon::nosql::RedisResult &r) {},
  3. [](const std::exception &err) {
  4. LOG_ERROR << "something failed!!! " << err.what();
  5. },
  6. "set myid %s", "587d-4709-86e4");

The same execCommandAsync can also retrieve data from Redis.

  1. redisClient->execCommandAsync(
  2. [](const drogon::nosql::RedisResult &r) {
  3. if (r.type() == RedisResultType::kNil)
  4. LOG_INFO << "Cannot find variable associated with the key 'name'";
  5. else
  6. LOG_INFO << "Name is " << r.asString();
  7. },
  8. [](const std::exception &err) {
  9. LOG_ERROR << "something failed!!! " << err.what();
  10. },
  11. "get name");

Transaction

Redis transaction allows multiple commands to be executed in a single step. All commands within a transaction are executed in order, no commands by other clients can be executed in middle of a transaction. Note that a transaction is not atomic. This means that after receiving the exec command, the transaction will be executed, If any command in the transaction fails to execute, the rest of the commands will still be executed. redis transactions do not support rollback operations.

The newTransactionAsync method creates a new transaction. Then the transaction could be used just like a normal RedisClient. Finally, the RedisTransaction::execute method executes said transaction.

  1. redisClient->newTransactionAsync([](const RedisTransactionPtr &transPtr) {
  2. transPtr->execCommandAsync(
  3. [](const drogon::nosql::RedisResult &r) { /* this command works */ },
  4. [](const std::exception &err) { /* this command failed */ },
  5. "set name drogon");
  6. transPtr->execute(
  7. [](const drogon::nosql::RedisResult &r) { /* transaction worked */ },
  8. [](const std::exception &err) { /* transaction failed */ });
  9. });

Coroutines

Redis clients support coroutines. One should use the GCC 11 or a newer compiler and use cmake -DCMAKE_CXX_FLAGS="-std=c++20" to enable it. See the (coroutine)[ENG-16-Coroutines] section for more information.

  1. try
  2. {
  3. auto transaction = co_await redisClient->newTransactionCoro();
  4. co_await transaction->execCommandCoro("set zzz 123");
  5. co_await transaction->execCommandCoro("set mening 42");
  6. co_await transaction->executeCoro();
  7. }
  8. catch(const std::exception& e)
  9. {
  10. LOG_ERROR << "Redis failed: " << e.what();
  11. }