|
22 | 22 |
|
23 | 23 | Aedis is a high-level [Redis](https://redis.io/) client library
|
24 | 24 | built on top of
|
25 |
| - [Asio](https://www.boost.org/doc/libs/release/doc/html/boost_asio.html), |
26 |
| - some of its distinctive features are |
| 25 | + [Asio](https://www.boost.org/doc/libs/release/doc/html/boost_asio.html). |
| 26 | + Some of its distinctive features are |
27 | 27 |
|
28 | 28 | \li Support for the latest version of the Redis communication protocol [RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md).
|
29 | 29 | \li First class support for STL containers and C++ built-in types.
|
30 | 30 | \li Serialization and deserialization of your own data types.
|
31 | 31 | \li Healthy checks, back pressure and low latency.
|
32 | 32 | \li Hides most of the low level asynchronous operations away from the user.
|
33 | 33 |
|
34 |
| - Let us start with an overview of asynchronous code. |
| 34 | + Let us have a look a some code snippets |
35 | 35 |
|
36 | 36 | @subsection Async
|
37 | 37 |
|
38 |
| - The code below sends a ping command to Redis (see intro.cpp) |
| 38 | + The code below sends a ping command to Redis and quits (see intro.cpp) |
39 | 39 |
|
40 | 40 | @code
|
41 | 41 | int main()
|
|
56 | 56 | }
|
57 | 57 | @endcode
|
58 | 58 |
|
59 |
| - The connection class maintains a healthy connection with |
60 |
| - Redis over which users can execute their commands, without any |
61 |
| - need of queuing. For example, to execute more than one command |
| 59 | + The connection class maintains a healthy connection with Redis |
| 60 | + over which users can execute their commands, without any need of |
| 61 | + queuing. For example, to execute more than one request |
62 | 62 |
|
63 | 63 | @code
|
64 | 64 | int main()
|
|
78 | 78 | }
|
79 | 79 | @endcode
|
80 | 80 |
|
81 |
| - The `async_exec` functions above can be called from different |
| 81 | + The `connection::async_exec` functions above can be called from different |
82 | 82 | places in the code without knowing about each other, see for
|
83 | 83 | example echo_server.cpp. Server-side pushes are supported on the
|
84 | 84 | same connection where commands are executed, a typical subscriber
|
|
88 | 88 | @code
|
89 | 89 | net::awaitable<void> reader(std::shared_ptr<connection> db)
|
90 | 90 | {
|
91 |
| - request req; |
92 |
| - req.push("SUBSCRIBE", "channel"); |
93 |
| -
|
94 | 91 | for (std::vector<node_type> resp;;) {
|
95 |
| - auto ev = co_await db->async_receive_event(aedis::adapt(resp)); |
96 |
| -
|
97 |
| - switch (ev) { |
98 |
| - case connection::event::push: |
99 |
| - // Use resp and clear it. |
100 |
| - resp.clear(); |
101 |
| - break; |
102 |
| -
|
103 |
| - default:; |
104 |
| - } |
| 92 | + co_await db->async_receive_event(adapt(resp)); |
| 93 | + // Use resp and clear it. |
| 94 | + resp.clear(); |
105 | 95 | }
|
106 | 96 | }
|
107 | 97 | @endcode
|
108 | 98 |
|
109 | 99 | @subsection Sync
|
110 | 100 |
|
111 |
| - The `connection` class is async-only, many users however need to |
112 |
| - interact with it synchronously, this is also supported by Aedis as long |
113 |
| - as this interaction occurs across threads, for example (see |
114 |
| - intro_sync.cpp) |
| 101 | + The `connection` class offers only an asynchronous API. |
| 102 | + Synchronous communications with redis is provided by the `aedis::sync` |
| 103 | + wrapper class. (see intro_sync.cpp) |
115 | 104 |
|
116 | 105 | @code
|
117 | 106 | int main()
|
118 | 107 | {
|
119 |
| - try { |
120 |
| - net::io_context ioc{1}; |
121 |
| - connection conn{ioc}; |
122 |
| - |
123 |
| - std::thread thread{[&]() { |
124 |
| - conn.async_run(net::detached); |
125 |
| - ioc.run(); |
126 |
| - }}; |
127 |
| - |
128 |
| - request req; |
129 |
| - req.push("PING"); |
130 |
| - req.push("QUIT"); |
131 |
| - |
132 |
| - std::tuple<std::string, aedis::ignore> resp; |
133 |
| - exec(conn, req, adapt(resp)); |
134 |
| - thread.join(); |
135 |
| - |
136 |
| - std::cout << "Response: " << std::get<0>(resp) << std::endl; |
137 |
| - } catch (std::exception const& e) { |
138 |
| - std::cerr << e.what() << std::endl; |
139 |
| - } |
| 108 | + net::io_context ioc{1}; |
| 109 | + auto work = net::make_work_guard(ioc); |
| 110 | + std::thread t1{[&]() { ioc.run(); }}; |
| 111 | + |
| 112 | + sync<connection> conn{work.get_executor()}; |
| 113 | + std::thread t2{[&]() { boost::system::error_code ec; conn.run(ec); }}; |
| 114 | + |
| 115 | + request req; |
| 116 | + req.push("PING"); |
| 117 | + req.push("QUIT"); |
| 118 | + |
| 119 | + std::tuple<std::string, aedis::ignore> resp; |
| 120 | + conn.exec(req, adapt(resp)); |
| 121 | + std::cout << "Response: " << std::get<0>(resp) << std::endl; |
| 122 | + |
| 123 | + work.reset(); |
| 124 | + |
| 125 | + t1.join(); |
| 126 | + t2.join(); |
140 | 127 | }
|
141 | 128 | @endcode
|
142 | 129 |
|
|
151 | 138 | For a simple installation run
|
152 | 139 |
|
153 | 140 | ```
|
154 |
| - # Clone the repository and checkout the lastest release tag. |
155 |
| - $ git clone --branch v0.3.0 https://github.com/mzimbres/aedis.git |
| 141 | + $ git clone --branch v1.0.0 https://github.com/mzimbres/aedis.git |
156 | 142 | $ cd aedis
|
157 | 143 |
|
158 |
| - # Build an example |
| 144 | + # Option 1: Direct compilation. |
159 | 145 | $ g++ -std=c++17 -pthread examples/intro.cpp -I./include -I/path/boost_1_79_0/include/
|
| 146 | +
|
| 147 | + # Option 2: Use cmake. |
| 148 | + $ BOOST_ROOT=/opt/boost_1_79_0/ cmake -DCMAKE_CXX_FLAGS=-std=c++20 . |
160 | 149 | ```
|
161 | 150 |
|
| 151 | + @note CMake support is still experimental. |
| 152 | +
|
162 | 153 | For a proper full installation on the system run
|
163 | 154 |
|
164 | 155 | ```
|
165 | 156 | # Download and unpack the latest release
|
166 |
| - $ wget https://github.com/mzimbres/aedis/releases/download/v0.3.0/aedis-0.3.0.tar.gz |
167 |
| - $ tar -xzvf aedis-0.3.0.tar.gz |
| 157 | + $ wget https://github.com/mzimbres/aedis/releases/download/v1.0.0/aedis-1.0.0.tar.gz |
| 158 | + $ tar -xzvf aedis-1.0.0.tar.gz |
168 | 159 |
|
169 | 160 | # Configure, build and install
|
170 |
| - $ CXXFLAGS="-std=c++17" ./configure --prefix=/opt/aedis-0.3.0 --with-boost=/opt/boost_1_78_0 |
| 161 | + $ CXXFLAGS="-std=c++17" ./configure --prefix=/opt/aedis-1.0.0 --with-boost=/opt/boost_1_78_0 |
171 | 162 | $ sudo make install
|
172 | 163 | ```
|
173 | 164 |
|
|
177 | 168 | $ make
|
178 | 169 | ```
|
179 | 170 |
|
180 |
| - There is also experimental support cmake, for example |
181 |
| -
|
182 |
| - @code |
183 |
| - $ BOOST_ROOT=/opt/boost_1_79_0/ cmake -DCMAKE_CXX_FLAGS=-std=c++20 . |
184 |
| - @endcode |
185 |
| -
|
186 | 171 | @subsubsection using_aedis Using Aedis
|
187 | 172 |
|
188 | 173 | When writing you own applications include the following header
|
|
380 | 365 |
|
381 | 366 | To read the response to transactions we have to observe that Redis
|
382 | 367 | queues the commands as they arrive and sends the responses back to
|
383 |
| - the user in a single array, in the response to the @c exec command. |
| 368 | + the user as an array, in the response to the @c exec command. |
384 | 369 | For example, to read the response to the this request
|
385 | 370 |
|
386 | 371 | @code
|
|
397 | 382 | using aedis::ignore;
|
398 | 383 | using boost::optional;
|
399 | 384 |
|
400 |
| - using tresp_type = |
| 385 | + using exec_resp_type = |
401 | 386 | std::tuple<
|
402 | 387 | optional<std::string>, // get
|
403 | 388 | optional<std::vector<std::string>>, // lrange
|
|
409 | 394 | ignore, // get
|
410 | 395 | ignore, // lrange
|
411 | 396 | ignore, // hgetall
|
412 |
| - tresp_type, // exec |
| 397 | + exec_resp_type, // exec |
413 | 398 | > resp;
|
414 | 399 |
|
415 | 400 | co_await db->async_exec(req, adapt(resp));
|
|
443 | 428 | There are cases where responses to Redis
|
444 | 429 | commands won't fit in the model presented above, some examples are
|
445 | 430 |
|
446 |
| - @li Commands (like \c set) whose response don't have a fixed |
| 431 | + @li Commands (like \c set) whose responses don't have a fixed |
447 | 432 | RESP3 type. Expecting an \c int and receiving a blob-string
|
448 | 433 | will result in error.
|
449 | 434 | @li RESP3 aggregates that contain nested aggregates can't be read in STL containers.
|
|
487 | 472 | @endcode
|
488 | 473 |
|
489 | 474 | For example, suppose we want to retrieve a hash data structure
|
490 |
| - from Redis with \c hgetall, some of the options are |
| 475 | + from Redis with `HGETALL`, some of the options are |
491 | 476 |
|
492 | 477 | @li \c std::vector<node<std::string>: Works always.
|
493 | 478 | @li \c std::vector<std::string>: Efficient and flat, all elements as string.
|
494 | 479 | @li \c std::map<std::string, std::string>: Efficient if you need the data as a \c std::map
|
495 | 480 | @li \c std::map<U, V>: Efficient if you are storing serialized data. Avoids temporaries and requires \c from_bulk for \c U and \c V.
|
496 | 481 |
|
497 |
| - In addition to the above users can also use unordered versions of the containers. The same reasoning also applies to sets e.g. \c smembers. |
| 482 | + In addition to the above users can also use unordered versions of the containers. The same reasoning also applies to sets e.g. `SMEMBERS`. |
498 | 483 |
|
499 | 484 | \section examples Examples
|
500 | 485 |
|
|
503 | 488 | @li intro.cpp: Basic steps with Aedis.
|
504 | 489 | @li intro_sync.cpp: Synchronous version of intro.cpp.
|
505 | 490 | @li containers.cpp: Shows how to send and receive stl containers.
|
506 |
| - @li serialization.cpp: Shows the \c request support to serialization of user types. |
507 |
| - @li subscriber.cpp: Shows how to subscribe to a channel and how to reconnect when connection is lost. |
| 491 | + @li serialization.cpp: Shows how to serialize your own types. |
| 492 | + @li subscriber.cpp: Shows how to use pubsub. |
508 | 493 | @li subscriber_sync.cpp: Synchronous version of subscriber.cpp.
|
509 | 494 | @li echo_server.cpp: A simple TCP echo server that uses coroutines.
|
510 | 495 | @li chat_room.cpp: A simple chat room that uses coroutines.
|
|
0 commit comments