mirror of
https://github.com/boostorg/redis.git
synced 2026-01-19 04:42:09 +00:00
Many improvements in the docs.
This commit is contained in:
130
aedis/aedis.hpp
130
aedis/aedis.hpp
@@ -24,58 +24,84 @@
|
||||
|
||||
\section Overview
|
||||
|
||||
Aedis is low-level redis client library built on top of Boost.Asio
|
||||
that implements communication with a Redis server over the latests
|
||||
version of its protocol RESP3. Some of its most important features
|
||||
are
|
||||
Aedis is a low-level redis client library that provides simple and
|
||||
efficient communication with a Redis server built on top of
|
||||
Boost.Asio. Some of its distinctive features are
|
||||
|
||||
1. First class support for asynchronous communication.
|
||||
2. Support for STL containers.
|
||||
3. Serialization and deserialization of your own data types built directly in the parser to avoid unnecessary copies.
|
||||
4. Client class that encapsulates handling of requests for the user.
|
||||
5. etc.
|
||||
1. Support for the latest version of the Redis communication protocol RESP3.
|
||||
2. Asynchronous interface that handles servers pushs optimally.
|
||||
3. Firt class support for STL containers.
|
||||
4. Serialization and deserialization of your own data types built directly into the parser that avoids temporaries.
|
||||
5. Client class that abstracts the management of output messages away from the user.
|
||||
6. Asymptotic zero allocations by means of memory reuse.
|
||||
|
||||
For more information about Redis see https://redis.io/
|
||||
|
||||
\section tutorial Tutorial
|
||||
|
||||
The general structure of a program involves writing a receiver like this
|
||||
|
||||
@code
|
||||
class myreceiver : receiver<std::vector<node<std::string>>> {
|
||||
public:
|
||||
void on_read(command cmd) override
|
||||
{
|
||||
switch (cmd) {
|
||||
case command::hello: on_hello(); break;
|
||||
case command::set: on_set(); break;
|
||||
case command::get: on_get(); break;
|
||||
...
|
||||
default:
|
||||
}
|
||||
}
|
||||
};
|
||||
@endcode
|
||||
|
||||
and to start communication with Redis
|
||||
The general form of a program looks like
|
||||
|
||||
@code
|
||||
int main()
|
||||
{
|
||||
net::io_context ioc;
|
||||
client<net::ip::tcp::socket> db(ioc.get_executor());
|
||||
myreceiver recv;
|
||||
client<net::ip::tcp::socket> db{ioc.get_executor()};
|
||||
receiver recv;
|
||||
|
||||
db.async_run(
|
||||
recv,
|
||||
{net::ip::make_address("127.0.0.1"), 6379},
|
||||
[](auto ec){ std::cout << ec.message() << std::endl;});
|
||||
[](auto ec){ ... });
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
@endcode
|
||||
|
||||
Most of the time users will be concerned only with the
|
||||
implementation of the \c receiver class, to make that simpler,
|
||||
Aedis provides a base receiver class that abstracts all the
|
||||
complexity away from the user. A typical implementation will look
|
||||
like the following
|
||||
|
||||
@code
|
||||
class myreceiver : receiver<response_type> {
|
||||
public:
|
||||
void on_read_impl(command cmd) override
|
||||
{
|
||||
// Handle commands here.
|
||||
}
|
||||
};
|
||||
@endcode
|
||||
|
||||
Sending commands to Redis is also simple, for example
|
||||
|
||||
@code
|
||||
db.>send(command::ping, "O rato roeu a roupa do rei de Roma");
|
||||
db.>send(command::incr, "counter");
|
||||
db.>send(command::set, "key", "Três pratos de trigo para três tigres");
|
||||
db.>send(command::get, "key");
|
||||
db.>send(command::quit);
|
||||
@endcode
|
||||
|
||||
See Tutorial for more details on how to use the library.
|
||||
For more information about Redis see https://redis.io/
|
||||
|
||||
\section tutorial Tutorial
|
||||
|
||||
In the last secion we have seem the general structure of an Aedis
|
||||
program. Here we will give more detail.
|
||||
|
||||
\subsection Requests
|
||||
\subsection Responses
|
||||
\subsubsection Serializaiton
|
||||
\subsubsection Transactions
|
||||
|
||||
Redis commands can fail only if called with a wrong syntax (and
|
||||
the problem is not detectable during the command queueing),
|
||||
or against keys holding the wrong data type: this means that in
|
||||
practical terms a failing command is the result of a programming
|
||||
errors, and a kind of error that is very likely to be detected
|
||||
during development, and not in production.
|
||||
|
||||
\section examples Examples
|
||||
https://redis.io/topics/data-types.
|
||||
See also https://redis.io/topics/transactions.
|
||||
|
||||
\b Basics: Focuses on small examples that show basic usage of
|
||||
the library.
|
||||
@@ -83,11 +109,12 @@
|
||||
- intro.cpp: A good starting point. Some commands are sent to the
|
||||
Redis server and the responses are printed to screen.
|
||||
|
||||
- aggregates.cpp:
|
||||
- aggregates.cpp: Shows how receive RESP3 aggregate data types.
|
||||
|
||||
- transaction.cpp: Shows how to read the responses to a trasaction
|
||||
efficiently. See also https://redis.io/topics/transactions.
|
||||
- stl_containers.cpp:
|
||||
|
||||
- serialization.cpp: Shows how to de/serialize your own
|
||||
non-aggregate data-structures.
|
||||
- subscriber.cpp: Shows how channel subscription works at a low
|
||||
level. See also https://redis.io/topics/pubsub.
|
||||
|
||||
@@ -100,34 +127,9 @@
|
||||
- chat_room.cpp: Shows how to build a scalable chat room that
|
||||
scales to millions of users.
|
||||
|
||||
\b STL \b Containers: Many of the Redis data structures can be
|
||||
directly translated in to STL containers, below you will find some
|
||||
example code. For a list of Redis data types see
|
||||
https://redis.io/topics/data-types.
|
||||
|
||||
- hashes.cpp: Shows how to read Redis hashes in a \c std::map, \c
|
||||
std::unordered_map and \c std::vector.
|
||||
|
||||
- lists.cpp: Shows how to read Redis lists in \c std::list,
|
||||
\c std::deque, \c std::vector. It also illustrates basic serialization.
|
||||
|
||||
- sets.cpp: Shows how to read Redis sets in a \c std::set, \c
|
||||
std::unordered_set and \c std::vector.
|
||||
|
||||
\b Customization \b points: Shows how de/serialize user types
|
||||
avoiding copies. This is particularly useful for low latency
|
||||
applications that want to avoid unneeded copies, for examples when
|
||||
storing json strings in Redis keys.
|
||||
|
||||
- serialization.cpp: Shows how to de/serialize your own
|
||||
non-aggregate data-structures.
|
||||
|
||||
- response_adapter.cpp: Customization point for users that want to
|
||||
- receiver.cpp: Customization point for users that want to
|
||||
de/serialize their own data-structures like containers for example.
|
||||
|
||||
- key_expiration.cpp: Shows how to use \c std::optional to deal
|
||||
with keys that may have expired or do not exist.
|
||||
|
||||
\section using-aedis Using Aedis
|
||||
|
||||
To install and use Aedis you will need
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
#include <aedis/redis/detail/client_ops.hpp>
|
||||
#include <aedis/redis/command.hpp>
|
||||
|
||||
// TODO: What to do if a users send a discard not contained in a
|
||||
// transaction. The client object will try to pop the queue until a
|
||||
// multi is found.
|
||||
|
||||
namespace aedis {
|
||||
namespace redis {
|
||||
|
||||
@@ -20,7 +24,8 @@ namespace redis {
|
||||
*
|
||||
* This Redis client keeps a connection to the database open and
|
||||
* uses it for all communication with Redis. For examples on how to
|
||||
* use see the examples chat_room.cpp, echo_server.cpp and redis_client.cpp.
|
||||
* use see the examples chat_room.cpp, echo_server.cpp and
|
||||
* redis_client.cpp.
|
||||
*
|
||||
* \remarks This class reuses its internal buffers for requests and
|
||||
* for reading Redis responses. With time it will allocate less and
|
||||
@@ -95,8 +100,11 @@ private:
|
||||
}
|
||||
|
||||
// Returns true when the next request can be writen.
|
||||
bool on_cmd()
|
||||
bool on_cmd(command)
|
||||
{
|
||||
// TODO: If the response to a discard is received we have to
|
||||
// remove all commands up until multi.
|
||||
|
||||
assert(!std::empty(req_info_));
|
||||
assert(!std::empty(commands_));
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ struct read_op {
|
||||
return;
|
||||
}
|
||||
|
||||
if (t != resp3::type::push && cli->on_cmd())
|
||||
if (t != resp3::type::push && cli->on_cmd(cmd))
|
||||
cli->timer_.cancel_one();
|
||||
|
||||
recv->on_read(cmd);
|
||||
|
||||
@@ -13,8 +13,6 @@ namespace redis {
|
||||
|
||||
char const* to_string(command c)
|
||||
{
|
||||
assert(c != command::invalid);
|
||||
|
||||
static char const* table[] = {
|
||||
"ACL",
|
||||
"APPEND",
|
||||
@@ -220,6 +218,7 @@ char const* to_string(command c)
|
||||
"ZSCAN",
|
||||
"ZSCORE",
|
||||
"ZUNIONSTORE",
|
||||
"INVALID",
|
||||
};
|
||||
|
||||
return table[static_cast<int>(c)];
|
||||
|
||||
@@ -69,6 +69,9 @@ public:
|
||||
|
||||
void on_read(command cmd)
|
||||
{
|
||||
if (cmd == command::discard)
|
||||
on_transaction_ = false;
|
||||
|
||||
if (on_transaction_)
|
||||
return;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ using aedis::resp3::node;
|
||||
using client_type = aedis::redis::client<net::ip::tcp::socket>;
|
||||
using response_type = std::vector<node<std::string>>;
|
||||
|
||||
// Prints aggregates that don't contain nested aggregates.
|
||||
// Prints aggregates that don't contain any nested aggregates.
|
||||
void print_aggregate(response_type const& v)
|
||||
{
|
||||
auto const m = element_multiplicity(v.front().data_type);
|
||||
|
||||
@@ -31,7 +31,7 @@ private:
|
||||
public:
|
||||
myreceiver(std::shared_ptr<client_type> db) : db_{db} {}
|
||||
|
||||
void on_message(command cmd)
|
||||
void on_read(command cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case command::hello:
|
||||
@@ -72,8 +72,8 @@ listener(
|
||||
for (;;) {
|
||||
auto socket = co_await acc->async_accept(net::use_awaitable);
|
||||
auto session = std::make_shared<user_session>(std::move(socket));
|
||||
recv->add(session);
|
||||
session->start(on_user_msg);
|
||||
recv->add(session);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,10 +95,7 @@ int main()
|
||||
co_spawn(ioc, listener(acc, db, recv), net::detached);
|
||||
|
||||
net::signal_set signals(ioc.get_executor(), SIGINT, SIGTERM);
|
||||
signals.async_wait([=] (auto, int) {
|
||||
db->send(command::quit);
|
||||
acc->cancel();
|
||||
});
|
||||
signals.async_wait([&] (auto, int) { ioc.stop(); });
|
||||
|
||||
ioc.run();
|
||||
} catch (std::exception const& e) {
|
||||
|
||||
@@ -91,10 +91,7 @@ int main()
|
||||
co_spawn(ioc, listener(acc, db, recv), net::detached);
|
||||
|
||||
net::signal_set signals(ioc.get_executor(), SIGINT, SIGTERM);
|
||||
signals.async_wait([=] (auto, int) {
|
||||
db->send(command::quit);
|
||||
acc->cancel();
|
||||
});
|
||||
signals.async_wait([&] (auto, int) { ioc.stop(); });
|
||||
|
||||
ioc.run();
|
||||
} catch (std::exception const& e) {
|
||||
|
||||
Reference in New Issue
Block a user