2
0
mirror of https://github.com/boostorg/redis.git synced 2026-01-19 04:42:09 +00:00

Improvements in the docs.

This commit is contained in:
Marcelo Zimbres
2023-06-23 22:24:34 +02:00
parent d29a057fa6
commit a715c251bf
2 changed files with 48 additions and 74 deletions

121
README.md
View File

@@ -1,41 +1,30 @@
# boost_redis
Boost.Redis is a [Redis](https://redis.io/) client library built on top of
Boost.Redis is a high-level [Redis](https://redis.io/) client library built on top of
[Boost.Asio](https://www.boost.org/doc/libs/release/doc/html/boost_asio.html)
that implements Redis plain text protocol
[RESP3](https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md).
It can multiplex any number of client
requests, responses, and server pushes onto a single active socket
connection to the Redis server. The library hides low-level code away
from the user, which, in the majority of the cases will be concerned
with only three library entities
* `boost::redis::connection`: A full-duplex connection to the Redis
server with high-level functions to execute Redis commands, receive
server pushes and automatic command [pipelines](https://redis.io/docs/manual/pipelining/).
* `boost::redis::request`: A container of Redis commands that supports
STL containers and user defined data types.
* `boost::redis::response`: Container of Redis responses.
In the next sections we will cover all these points in detail with
examples. The requirements for using Boost.Redis are
connection to the Redis server. The requirements for using Boost.Redis are
* Boost 1.81 or greater.
* C++17 minimum.
* Redis 6 or higher (must support RESP3).
* Gcc (10, 11, 12), Clang (11, 13, 14) and Visual Studio (16 2019, 17 2022).
* Have basic-level knowledge about Redis and understand Asio and its asynchronous model.
* Have basic-level knowledge about [Redis](https://redis.io/docs/)
and [Boost.Asio](https://www.boost.org/doc/libs/1_82_0/doc/html/boost_asio/overview.html).
To install Boost.Redis download the latest release on
https://github.com/boostorg/redis/releases. Boost.Redis is a header only
library, so you can starting using it right away by adding the
`include` subdirectory to your project and including
The latest release can be downloaded on
https://github.com/boostorg/redis/releases. The library headers can be
found in the `include` subdirectory and a compilation of the source
```cpp
#include <boost/redis/src.hpp>
```
in no more than one source file in your applications. To build the
is required. The simplest way to do it is to included this header in
no more than one source file in your applications. To build the
examples and tests cmake is supported, for example
```cpp
@@ -45,21 +34,10 @@ $ BOOST_ROOT=/opt/boost_1_81_0 cmake --preset g++-11
# Windows
$ cmake -G "Visual Studio 17 2022" -A x64 -B bin64 -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake
```
<a name="connection"></a>
## Connection
Readers that are not familiar with Redis are advised to learn more about
it on https://redis.io/docs/ before we start, in essence
> Redis is an open source (BSD licensed), in-memory data structure
> store used as a database, cache, message broker, and streaming
> engine. Redis provides data structures such as strings, hashes,
> lists, sets, sorted sets with range queries, bitmaps, hyperloglogs,
> geospatial indexes, and streams. Redis has built-in replication, Lua
> scripting, LRU eviction, transactions, and different levels of
> on-disk persistence, and provides high availability via Redis
> Sentinel and automatic partitioning with Redis Cluster.
Let us start with a simple application that uses a short-lived
connection to send a [ping](https://redis.io/commands/ping/) command
to Redis
@@ -78,7 +56,7 @@ auto co_main(config const& cfg) -> net::awaitable<void>
response<std::string> resp;
// Executes the request.
co_await conn->async_exec(req, resp);
co_await conn->async_exec(req, resp, net::deferred);
conn->cancel();
std::cout << "PING: " << std::get<0>(resp).value() << std::endl;
@@ -87,12 +65,12 @@ auto co_main(config const& cfg) -> net::awaitable<void>
The roles played by the `async_run` and `async_exec` functions are
* `connection::async_exec`: Execute the commands contained in the
* `async_exec`: Execute the commands contained in the
request and store the individual responses in the `resp` object. Can
be called from multiple places in your code concurrently.
* `connection::async_run`: Resolve, connect, ssl-handshake,
resp3-handshake, health-checks reconnection and coordinate low-level
read and write operations..
* `async_run`: Resolve, connect, ssl-handshake,
resp3-handshake, health-checks, reconnection and coordinate low-level
read and write operations (among other things).
### Server pushes
@@ -114,22 +92,22 @@ receiver(std::shared_ptr<connection> conn) -> net::awaitable<void>
request req;
req.push("SUBSCRIBE", "channel");
while (!conn->is_cancelled()) {
// Loop while reconnection is enabled
while (conn->will_reconnect()) {
// Reconnect to channels.
co_await conn->async_exec(req);
co_await conn->async_exec(req, ignore, net::deferred);
// Loop reading Redis pushs messages.
// Loop reading Redis pushes.
for (generic_response resp;;) {
error_code ec;
co_await conn->async_receive(resp, net::redirect_error(net::use_awaitable, ec));
if (ec)
break; // Connection lost, break so we can reconnect to channels.
std::cout
<< resp.value().at(1).value
<< " " << resp.value().at(2).value
<< " " << resp.value().at(3).value
<< std::endl;
// Use the response resp in some way and then clear it.
...
resp.value().clear();
}
}
@@ -167,22 +145,21 @@ req.push_range("HSET", "key", map);
Sending a request to Redis is performed with `boost::redis::connection::async_exec` as already stated.
<a name="responses"></a>
### Config flags
The `boost::redis::request::config` object inside the request dictates how the
`boost::redis::connection` should handle the request in some important situations. The
reader is advised to read it carefully.
<a name="responses"></a>
## Responses
Boost.Redis uses the following strategy to support Redis responses
* **Static**: For `boost::redis::request` whose sizes are known at compile time use the `response` type.
* `boost::redis::request` is used for requests whose number of commands are not dynamic.
* **Dynamic**: Otherwise use `boost::redis::generic_response`.
For example, below is a request with a compile time size
For example, the request below has three commands
```cpp
request req;
@@ -191,18 +168,19 @@ req.push("INCR", "key");
req.push("QUIT");
```
To read the response to this request users can use the following tuple
and its response also has three comamnds and can be read in the
following response object
```cpp
response<std::string, int, std::string>
```
The pattern might have become apparent to the reader: the tuple must
The response behaves as a tuple and must
have as many elements as the request has commands (exceptions below).
It is also necessary that each tuple element is capable of storing the
response to the command it refers to, otherwise an error will occur.
To ignore responses to individual commands in the request use the tag
`boost::redis::ignore_t`
`boost::redis::ignore_t`, for example
```cpp
// Ignore the second and last responses.
@@ -266,18 +244,14 @@ response<
Where both are passed to `async_exec` as showed elsewhere
```cpp
co_await conn->async_exec(req, resp);
co_await conn->async_exec(req, resp, net::deferred);
```
If the intention is to ignore the response to all commands altogether
use `ignore`
If the intention is to ignore responses altogether use `ignore`
```cpp
// Ignores the response
co_await conn->async_exec(req, ignore);
// The default response argument will also ignore responses.
co_await conn->async_exec(req);
co_await conn->async_exec(req, ignore, net::deferred);
```
Responses that contain nested aggregates or heterogeneous data
@@ -294,7 +268,7 @@ Commands that have no response like
* `"PSUBSCRIBE"`
* `"UNSUBSCRIBE"`
must be **NOT** be included in the response tuple. For example, the request below
must **NOT** be included in the response tuple. For example, the request below
```cpp
request req;
@@ -304,7 +278,7 @@ req.push("QUIT");
```
must be read in this tuple `response<std::string, std::string>`,
that has size two.
that has static size two.
### Null
@@ -320,17 +294,17 @@ response<
...
> resp;
co_await conn->async_exec(req, resp);
co_await conn->async_exec(req, resp, net::deferred);
```
Everything else stays pretty much the same.
### Transactions
To read responses to transactions we must first observe that Redis will
queue the transaction commands and send their individual responses as elements
of an array, the array is itself the response to the `EXEC` command.
For example, to read the response to this request
To read responses to transactions we must first observe that Redis
will queue the transaction commands and send their individual
responses as elements of an array, the array is itself the response to
the `EXEC` command. For example, to read the response to this request
```cpp
req.push("MULTI");
@@ -360,7 +334,7 @@ response<
exec_resp_type, // exec
> resp;
co_await conn->async_exec(req, resp);
co_await conn->async_exec(req, resp, net::deferred);
```
For a complete example see cpp20_containers.cpp.
@@ -373,8 +347,8 @@ There are cases where responses to Redis
commands won't fit in the model presented above, some examples are
* Commands (like `set`) whose responses don't have a fixed
RESP3 type. Expecting an `int` and receiving a blob-string
will result in error.
RESP3 type. Expecting an `int` and receiving a blob-string
will result in error.
* RESP3 aggregates that contain nested aggregates can't be read in STL containers.
* Transactions with a dynamic number of commands can't be read in a `response`.
@@ -408,7 +382,7 @@ using other types
```cpp
// Receives any RESP3 simple or aggregate data type.
boost::redis::generic_response resp;
co_await conn->async_exec(req, resp);
co_await conn->async_exec(req, resp, net::deferred);
```
For example, suppose we want to retrieve a hash data structure
@@ -460,9 +434,8 @@ The examples below show how to use the features discussed so far
* cpp17_intro.cpp: Uses callbacks and requires C++17.
* cpp17_intro_sync.cpp: Runs `async_run` in a separate thread and performs synchronous calls to `async_exec`.
To avoid repetition code that is common to some examples has been
grouped in common.hpp. The main function used in some async examples
has been factored out in the main.cpp file.
The main function used in some async examples has been factored out in
the main.cpp file.
## Echo server benchmark
@@ -701,7 +674,7 @@ https://lists.boost.org/Archives/boost/2023/01/253944.php.
## Changelog
### master (incorporates changes to conform the boost review and more)
### develop (incorporates changes to conform the boost review and more)
* Adds Redis stream example.

View File

@@ -52,6 +52,7 @@ receiver(std::shared_ptr<connection> conn) -> net::awaitable<void>
request req;
req.push("SUBSCRIBE", "channel");
// Loop while reconnection is enabled
while (conn->will_reconnect()) {
// Reconnect to channels.