mirror of
https://github.com/boostorg/redis.git
synced 2026-01-19 04:42:09 +00:00
Progresses with docs.
This commit is contained in:
127
aedis/aedis.hpp
127
aedis/aedis.hpp
@@ -25,8 +25,8 @@
|
||||
\section Overview
|
||||
|
||||
Aedis is a low-level redis client library that provides simple and
|
||||
efficient communication with a Redis server. It is built on top of
|
||||
Boost.Asio and some of its distinctive features are
|
||||
efficient communication with a Redis server (see https://redis.io/).
|
||||
It is built on top of Boost.Asio and some of its distinctive features are
|
||||
|
||||
1. Support for the latest version of the Redis communication protocol RESP3.
|
||||
2. Asynchronous interface that handles servers pushs optimally.
|
||||
@@ -57,53 +57,134 @@
|
||||
implementation of the \c receiver class, to make that simpler,
|
||||
Aedis provides a base receiver class that abstracts most of the
|
||||
complexity away from the user. In a typical implementation the
|
||||
following two function will have to be implemented
|
||||
following functions will be overriden by the user
|
||||
|
||||
@code
|
||||
class myreceiver : receiver<response_type> {
|
||||
public:
|
||||
void on_read_impl(command cmd) override { ... }
|
||||
void on_push_impl() override { ... }
|
||||
void on_read_impl(command cmd) override
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void on_write_impl(std::size_t n) override
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void on_push_impl() override
|
||||
{
|
||||
// ...
|
||||
}
|
||||
};
|
||||
@endcode
|
||||
|
||||
Sending commands to Redis is also simple, for example
|
||||
The \c client object (\c db above) can be passed
|
||||
around in the program to send commands to Redis, 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);
|
||||
void foo(client<net::ip::tcp::socket>& db)
|
||||
{
|
||||
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");
|
||||
...
|
||||
}
|
||||
@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 section we have seen the general structure of an Aedis
|
||||
program. Here we will go in depth.
|
||||
Now let us see how to make requests and receive responses with more detail.
|
||||
|
||||
\subsection Requests
|
||||
|
||||
A request is composed of one or more commands (in Redis
|
||||
Request are composed of one or more commands (in Redis
|
||||
documentation they are called pipeline, see
|
||||
https://redis.io/topics/pipelining). The individual commands in a
|
||||
request assume different forms, for example
|
||||
request assume many different forms
|
||||
|
||||
1. With key: \c set, \c get, \c expire etc.
|
||||
2. Without key: \c hello, \c quit etc.
|
||||
3. Ranges with a key: \c rpush, \c hgtall etc.
|
||||
4. Ranges without a key: \c subscribe, etc.
|
||||
5. etc.
|
||||
|
||||
To account for all these possibilities, the \c client class offer
|
||||
some member functions, for example
|
||||
|
||||
@code
|
||||
// Some data to send to Redis.
|
||||
std::string value = "some value";
|
||||
|
||||
std::list<std::string> list {"channel1", "channel2", "channel3"};
|
||||
|
||||
std::map<std::string, mystruct> map
|
||||
{ {"key1", "value1"}
|
||||
, {"key2", "value2"}
|
||||
, {"key3", "value3"}};
|
||||
|
||||
// No key or arguments.
|
||||
db.>send(command::quit);
|
||||
db.>send(command::subscribe, "channel1", "channel2");
|
||||
db_->send_range(command::hset, "key", std::cbegin(map), std::cend(map));
|
||||
|
||||
// With key and arguments.
|
||||
db.>send(command::set, "key", value, "EX", "2");
|
||||
|
||||
// Sends a container, no key
|
||||
db.>send_range(command::subscribe, list);
|
||||
|
||||
// As above but an iterator range.
|
||||
db->send_range2(command::subscribe, std::cbegin(list), std::cend(list));
|
||||
|
||||
// Sends a container, with key.
|
||||
db->send_range(command::hset, "key", map);
|
||||
|
||||
// As above but as iterator range.
|
||||
db->send_range2(command::hset, "key", std::cbegin(map), std::cend(map));
|
||||
@endcode
|
||||
|
||||
The \c send functions above adds commands to the output queue and
|
||||
send only if there is no pending response of a previously sent
|
||||
command. Users can also easily send their own data types to Redis
|
||||
by defining a to_string function, for example
|
||||
|
||||
@code
|
||||
struct mystruct {
|
||||
int a;
|
||||
int b;
|
||||
};
|
||||
|
||||
std::string to_string(mystruct const& obj)
|
||||
{
|
||||
// Convert to string
|
||||
}
|
||||
|
||||
std::map<std::string, mystruct> map
|
||||
{ {"key1", {1, 2}}
|
||||
, {"key2", {3, 4}}
|
||||
, {"key3", {5, 6}}};
|
||||
|
||||
db.send_range(command::hset, "serialization-hset-key", map);
|
||||
@endcode
|
||||
|
||||
See https://redis.io/commands/hset for \c hset documentation.
|
||||
|
||||
\subsection Responses
|
||||
|
||||
The Redis protocol RESP3 defines two types of data, they are.
|
||||
|
||||
1. Simple data types: simple string, simple error, \c blob string, \c blob error, number, double, etc.
|
||||
2. Aggregates: array, push, set, map, etc.
|
||||
|
||||
Most of these types can be translated into C++ containers and bilt-in types, for example
|
||||
|
||||
1. \c std::string: simple and blob string and error.
|
||||
2. `long long int`: number
|
||||
3. \c double: double.
|
||||
|
||||
https://redis.io/topics/data-types.
|
||||
|
||||
\subsubsection Optional
|
||||
\subsubsection Serializaiton
|
||||
\subsubsection Transactions
|
||||
\subsubsection Serializaiton
|
||||
|
||||
See also https://redis.io/topics/transactions.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#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
|
||||
// TODO: What to do if users send a discard command not contained in a
|
||||
// transaction. The client object will try to pop the queue until a
|
||||
// multi is found.
|
||||
|
||||
@@ -203,8 +203,10 @@ public:
|
||||
timer_.cancel_one();
|
||||
}
|
||||
|
||||
/** \brief Sends and iterator range (overload with key).
|
||||
*/
|
||||
template <class Key, class ForwardIterator>
|
||||
void send_range(command cmd, Key const& key, ForwardIterator begin, ForwardIterator end)
|
||||
void send_range2(command cmd, Key const& key, ForwardIterator begin, ForwardIterator end)
|
||||
{
|
||||
if (begin == end)
|
||||
return;
|
||||
@@ -227,6 +229,54 @@ public:
|
||||
timer_.cancel_one();
|
||||
}
|
||||
|
||||
/** \brief Sends and iterator range (overload without key).
|
||||
*/
|
||||
template <class ForwardIterator>
|
||||
void send_range2(command cmd, ForwardIterator begin, ForwardIterator end)
|
||||
{
|
||||
if (begin == end)
|
||||
return;
|
||||
|
||||
auto const can_write = prepare_next();
|
||||
|
||||
auto sr = redis::make_serializer(requests_);
|
||||
auto const before = std::size(requests_);
|
||||
sr.push_range(cmd, begin, end);
|
||||
auto const after = std::size(requests_);
|
||||
assert(after - before != 0);
|
||||
req_info_.front().size += after - before;;
|
||||
|
||||
if (!has_push_response(cmd)) {
|
||||
commands_.push_back(cmd);
|
||||
++req_info_.front().cmds;
|
||||
}
|
||||
|
||||
if (can_write)
|
||||
timer_.cancel_one();
|
||||
}
|
||||
|
||||
/** \brief Sends a range.
|
||||
*/
|
||||
template <class Key, class Range>
|
||||
void send_range(command cmd, Key const& key, Range const& range)
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
send_range2(cmd, key, begin(range), end(range));
|
||||
}
|
||||
|
||||
/** \brief Sends a range.
|
||||
*/
|
||||
template <class Range>
|
||||
void send_range(command cmd, Range const& range)
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
send_range2(cmd, begin(range), end(range));
|
||||
}
|
||||
|
||||
/** \brief Starts communication with the Redis server asynchronously.
|
||||
*/
|
||||
template <
|
||||
class Receiver,
|
||||
class CompletionToken = default_completion_token_type
|
||||
|
||||
@@ -11,6 +11,14 @@
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
// TODO: The signature from to_string should be changed from
|
||||
//
|
||||
// std::string to_string(T const&)
|
||||
// to
|
||||
//
|
||||
// void to_string(std::string& s, T const&)
|
||||
//
|
||||
|
||||
namespace aedis {
|
||||
namespace resp3 {
|
||||
namespace detail {
|
||||
@@ -63,8 +71,8 @@ void add_bulk(Storage& to, T const& data, typename std::enable_if<needs_to_strin
|
||||
add_bulk(to, s);
|
||||
}
|
||||
|
||||
// Consider adding overload for std::tuple.
|
||||
// Overload for pairs.
|
||||
// TODO: Overload for tuples.
|
||||
template <class Storage, class T1, class T2>
|
||||
void add_bulk(Storage& to, std::pair<T1, T2> const& pair)
|
||||
{
|
||||
|
||||
@@ -24,9 +24,7 @@ div.contents {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
/*
|
||||
code
|
||||
{
|
||||
background-color:#EFD25E;
|
||||
background-color:#f0e9ce;
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -54,9 +54,9 @@ private:
|
||||
{"one", "two", "three", "four"};
|
||||
|
||||
// Sends the stl containers.
|
||||
db_->send_range(command::hset, "hset-key", std::cbegin(map), std::cend(map));
|
||||
db_->send_range(command::rpush, "rpush-key", std::cbegin(vec), std::cend(vec));
|
||||
db_->send_range(command::sadd, "sadd-key", std::cbegin(set), std::cend(set));
|
||||
db_->send_range(command::hset, "hset-key", map);
|
||||
db_->send_range(command::rpush, "rpush-key", vec);
|
||||
db_->send_range(command::sadd, "sadd-key", set);
|
||||
|
||||
// Retrieves the containers.
|
||||
db_->send(command::hgetall, "hset-key");
|
||||
|
||||
@@ -31,8 +31,8 @@ private:
|
||||
case command::hello:
|
||||
db_->send(command::ping, "O rato roeu a roupa do rei de Roma");
|
||||
db_->send(command::incr, "intro-counter");
|
||||
db_->send(command::set, "intro-set-key", "Três pratos de trigo para três tigres");
|
||||
db_->send(command::get, "intro-set-key");
|
||||
db_->send(command::set, "intro-key", "Três pratos de trigo para três tigres");
|
||||
db_->send(command::get, "intro-key");
|
||||
db_->send(command::quit);
|
||||
break;
|
||||
|
||||
|
||||
@@ -104,9 +104,9 @@ private:
|
||||
|
||||
// Sends
|
||||
db_->send(command::set, "serialization-var-key", var, "EX", "2");
|
||||
db_->send_range(command::hset, "serialization-hset-key", std::cbegin(map), std::cend(map));
|
||||
db_->send_range(command::rpush, "serialization-rpush-key", std::cbegin(vec), std::cend(vec));
|
||||
db_->send_range(command::sadd, "serialization-sadd-key", std::cbegin(set), std::cend(set));
|
||||
db_->send_range(command::hset, "serialization-hset-key", map);
|
||||
db_->send_range(command::rpush, "serialization-rpush-key", vec);
|
||||
db_->send_range(command::sadd, "serialization-sadd-key", set);
|
||||
|
||||
// Retrieves
|
||||
db_->send(command::get, "serialization-var-key");
|
||||
|
||||
@@ -70,9 +70,9 @@ private:
|
||||
{"one", "two", "three", "four"};
|
||||
|
||||
// Sends the stl containers.
|
||||
db_->send_range(command::hset, "hset-key", std::cbegin(map), std::cend(map));
|
||||
db_->send_range(command::rpush, "rpush-key", std::cbegin(vec), std::cend(vec));
|
||||
db_->send_range(command::sadd, "sadd-key", std::cbegin(set), std::cend(set));
|
||||
db_->send_range(command::hset, "hset-key", map);
|
||||
db_->send_range(command::rpush, "rpush-key", vec);
|
||||
db_->send_range(command::sadd, "sadd-key", set);
|
||||
|
||||
//_ Retrieves the containers.
|
||||
db_->send(command::hgetall, "hset-key");
|
||||
|
||||
Reference in New Issue
Block a user