Commit 1f9b6893 authored by gejun's avatar gejun

reviewed redis_client.md

parent 4eb69978
[redis](http://redis.io/)是最近几年比较火的缓存服务,相比memcached在server端提供了更多的数据结构和操作方法,简化了用户的开发工作,在百度内有比较广泛的应用。为了使用户更快捷地访问redis并充分利用bthread的并发能力,brpc直接支持redis协议。示例程序:[example/redis_c++](https://github.com/brpc/brpc/tree/master/example/redis_c++/)
[English version](../en/redis_client.md)
[redis](http://redis.io/)是最近几年比较火的缓存服务,相比memcached在server端提供了更多的数据结构和操作方法,简化了用户的开发工作。为了使用户更快捷地访问redis并充分利用bthread的并发能力,brpc直接支持redis协议。示例程序:[example/redis_c++](https://github.com/brpc/brpc/tree/master/example/redis_c++/)
相比使用[hiredis](https://github.com/redis/hiredis)(官方client)的优势有:
- 线程安全。用户不需要为每个线程建立独立的client。
- 支持同步、异步、批量同步、批量异步等访问方式,能使用ParallelChannel等组合访问方式。
- 支持多种[连接方式](client.md#连接方式)。支持超时、backup request、取消、tracing、内置服务等一系列RPC基本福利。
- 一个进程和一个redis-server只有一个连接。多个线程同时访问一个redis-server时更高效(见[性能](#性能))。无论reply的组成多复杂,内存都会连续成块地分配,并支持短串优化(SSO)
- 一个进程中的所有brpc client和一个redis-server只有一个连接。多个线程同时访问一个redis-server时更高效(见[性能](#性能))。无论reply的组成多复杂,内存都会连续成块地分配,并支持短串优化(SSO)进一步提高性能
像http一样,brpc保证在最差情况下解析redis reply的时间复杂度也是O(N),N是reply的字节数,而不是O($N^2$)。当reply是个较大的数组时,这是比较重要的。
......@@ -45,11 +47,12 @@ if (cntl.Failed()) {
LOG(ERROR) << "Fail to access redis-server";
return -1;
}
// 可以通过response.reply(i)访问某个reply
if (response.reply(0).is_error()) {
LOG(ERROR) << "Fail to set";
return -1;
}
// 你可以通过response.reply(0).c_str()访问到值或多种方式打印出结果。
// 可用多种方式打印reply
LOG(INFO) << response.reply(0).c_str() // OK
<< response.reply(0) // OK
<< response; // OK
......@@ -69,7 +72,7 @@ if (response.reply(0).is_error()) {
LOG(ERROR) << "Fail to incr";
return -1;
}
// 返回了incr后的值,你可以通过response.reply(0).integer()访问到值,或以多种方式打印结果。
// 可用多种方式打印结果
LOG(INFO) << response.reply(0).integer() // 2
<< response.reply(0) // (integer) 2
<< response; // (integer) 2
......@@ -103,7 +106,7 @@ CHECK_EQ(-10, response.reply(3).integer());
# RedisRequest
一个[RedisRequest](https://github.com/brpc/brpc/blob/master/src/brpc/redis.h)可包含多个Command,调用AddCommand*增加命令,成功返回true,失败返回false并会打印调用处的栈
一个[RedisRequest](https://github.com/brpc/brpc/blob/master/src/brpc/redis.h)可包含多个Command,调用AddCommand*增加命令,成功返回true,失败返回false**并会打印调用处的栈**
```c++
bool AddCommand(const char* fmt, ...);
......@@ -113,9 +116,9 @@ bool AddCommandByComponents(const butil::StringPiece* components, size_t n);
格式和hiredis基本兼容:即%b对应二进制数据(指针+length),其他和printf的参数类似。对一些细节做了改进:当某个字段包含空格时,使用单引号或双引号包围起来会被视作一个字段。比如AddCommand("Set 'a key with space' 'a value with space as well'")中的key是a key with space,value是a value with space as well。在hiredis中必须写成redisvCommand(..., "SET %s %s", "a key with space", "a value with space as well");
AddCommandByComponents类似hiredis中的redisCommandArgv,用户通过数组指定命令中的每一个部分。这个方法不是最快捷的,但效率最高,且对AddCommand和AddCommandV可能发生的转义问题免疫,如果你在使用AddCommand和AddCommandV时出现了“引号不匹配”,“无效格式”等问题且无法定位,可以试下这个方法。
AddCommandByComponents类似hiredis中的redisCommandArgv,用户通过数组指定命令中的每一个部分。这个方法对AddCommand和AddCommandV可能发生的转义问题免疫,且效率最高。如果你在使用AddCommand和AddCommandV时出现了“Unmatched quote”,“无效格式”等问题且无法定位,可以试下这个方法。
如果AddCommand*失败,后续的AddCommand*和CallMethod都会失败。一般来说不用判AddCommand*的结果,失败后自然会通过RPC失败体现出来。
如果AddCommand\*失败,后续的AddCommand\*和CallMethod都会失败。一般来说不用判AddCommand*的结果,失败后自然会通过RPC失败体现出来。
command_size()可获得(成功)加入的命令个数。
......@@ -123,28 +126,28 @@ command_size()可获得(成功)加入的命令个数。
# RedisResponse
[RedisResponse](https://github.com/brpc/brpc/blob/master/src/brpc/redis.h)可能包含一个或多个[RedisReply](https://github.com/brpc/brpc/blob/master/src/brpc/redis_reply.h),reply_size()可获得reply的个数,reply(i)可获得第i个reply的引用(从0计数)。注意在hiredis中,如果请求包含了N个command,获取结果也要调用N次redisGetReply。但在brpc中这是不必要的,RedisResponse已经包含了N个reply,通过reply(i)获取就行了。只要RPC成功,response.reply_size()应与request.command_size()相等,除非redis-server有bug(redis-server工作的基本前提就是response和request按序一一对应)
[RedisResponse](https://github.com/brpc/brpc/blob/master/src/brpc/redis.h)可能包含一个或多个[RedisReply](https://github.com/brpc/brpc/blob/master/src/brpc/redis_reply.h),reply_size()可获得reply的个数,reply(i)可获得第i个reply的引用(从0计数)。注意在hiredis中,如果请求包含了N个command,获取结果也要调用N次redisGetReply。但在brpc中这是不必要的,RedisResponse已经包含了N个reply,通过reply(i)获取就行了。只要RPC成功,response.reply_size()应与request.command_size()相等,除非redis-server有bug,redis-server工作的基本前提就是reply和command按序一一对应。
每个reply可能是:
- REDIS_REPLY_NIL:redis中的NULL,代表值不存在。可通过is_nil()判定。
- REDIS_REPLY_STATUS:在redis文档中称为Simple String。一般是操作的返回值,比如SET返回的OK。可通过is_string()判定(status和string暂时不让用户区分),c_str()或data()获得值。
- REDIS_REPLY_STRING:在redis文档中称为Bulk String。大多数值都是这个类型,包括那些可以incr的。可通过is_string()判定,c_str()或data()获得值。
- REDIS_REPLY_STATUS:在redis文档中称为Simple String。一般是操作的返回状态,比如SET返回的OK。可通过is_string()判定(和string相同),c_str()或data()获得值。
- REDIS_REPLY_STRING:在redis文档中称为Bulk String。大多数值都是这个类型,包括incr返回的。可通过is_string()判定,c_str()或data()获得值。
- REDIS_REPLY_ERROR:操作出错时的返回值,包含一段错误信息。可通过is_error()判定,error_message()获得错误信息。
- REDIS_REPLY_INTEGER:一个64位有符号数。可通过is_integer()判定,integer()获得值。
- REDIS_REPLY_ARRAY:另一些reply的数组。可通过is_array()判定,size()获得数组大小,[i]获得对应的子reply引用。
比如response包含三个reply,类型分别是integer,string和array (size=2)。那么可以分别这么获得值:response.reply(0).integer(),response.reply(1).c_str(), repsonse.reply(2)[0]和repsonse.reply(2)[1]。如果类型对不上,调用处的栈会被打印出来,并返回一个undefined的值。
如果response包含三个reply,分别是integer,string和一个长度为2的array。那么可以分别这么获得值:response.reply(0).integer(),response.reply(1).c_str(), repsonse.reply(2)[0]和repsonse.reply(2)[1]。如果类型对不上,调用处的栈会被打印出来,并返回一个undefined的值。
response中的所有reply的ownership属于response。当response析构时,reply也析构了。相应地,RedisReply被禁止拷贝。
response中的所有reply的ownership属于response。当response析构时,reply也析构了。
调用Clear()后RedisResponse可以重用。
# 访问redis集群
暂时请沿用常见的[twemproxy](https://github.com/twitter/twemproxy)方案,像访问单点一样访问proxy。如果你之前用hiredis访问BDRP(使用了twemproxy),那把client更换成brpc就行了。通过client(一致性哈希)直接访问redis集群虽然能降低延时,但同时也(可能)意味着无法直接利用BDRP的托管服务,这一块还不是很确定
建立一个使用一致性哈希负载均衡算法(c_md5或c_murmurhash)的channel就能访问挂载在对应名字服务下的memcached集群了。注意每个MemcacheRequest应只包含一个操作或确保所有的操作是同一个key。如果request包含了多个操作,在当前实现下这些操作总会送向同一个server,假如对应的key分布在多个server上,那么结果就不对了,这个情况下你必须把一个request分开为多个,每个包含一个操作
如果你自己维护了redis集群,和memcache类似,应该是可以用一致性哈希访问的。但每个RedisRequest应只包含一个command或确保所有的command始终落在同一台server。如果request包含了多个command,在当前实现下总会送向同一个server。比方说一个request中包含了多个Get,而对应的key分布在多个server上,那么结果就肯定不对了,这个情况下你必须把一个request分开为多个
或者你可以沿用常见的[twemproxy](https://github.com/twitter/twemproxy)方案。这个方案虽然需要额外部署proxy,还增加了延时,但client端仍可以像访问单点一样的访问它
# 查看发出的请求和收到的回复
......@@ -159,7 +162,7 @@ response中的所有reply的ownership属于response。当response析构时,rep
# 性能
redis版本:2.6.14 (不是最新的3.0)
redis版本:2.6.14
分别使用1,50,200个bthread同步压测同机redis-server,延时单位均为微秒。
......@@ -221,11 +224,13 @@ TRACE: 02-13 18:07:42: * 0 client.cpp:180] Accessing redis server at qps=75238
16878 gejun 20 0 48136 2520 1004 R 99.9 0.0 9:52.33 redis-server
```
可以看到qps相比单链接时有大幅回落,同时redis-server的CPU打满了。原因在于redis-server每次只能从一个连接中读到一个请求,IO开销大幅增加。
可以看到qps相比单链接时有大幅回落,同时redis-server的CPU打满了。原因在于redis-server每次只能从一个连接中读到一个请求,IO开销大幅增加。这也是单个hiredis client的极限性能。
# Command Line Interface
example/redis_c++/redis_cli是一个类似于官方CLI的命令行工具,以展示brpc对redis协议的处理能力。当使用brpc访问redis-server出现不符合预期的行为时,也可以使用这个CLI进行交互式的调试。
[example/redis_c++/redis_cli](https://github.com/brpc/brpc/blob/master/example/redis_c%2B%2B/redis_cli.cpp)是一个类似于官方CLI的命令行工具,以展示brpc对redis协议的处理能力。当使用brpc访问redis-server出现不符合预期的行为时,也可以使用这个CLI进行交互式的调试。
和官方CLI类似,`redis_cli <command>`也可以直接运行命令,-server参数可以指定redis-server的地址。
```
$ ./redis_cli
......@@ -250,5 +255,3 @@ OK
redis 127.0.0.1:6379> client getname
"brpc-cli"
```
和官方CLI类似,redis_cli <command>也可以直接运行命令,-server参数可以指定redis-server的地址。
......@@ -4,7 +4,7 @@
**NOTE**: brpc only supports the binary protocol of memcache. There's little benefit to support the textual protocol which is replaced since memcached 1.3. If your memcached is older than 1.3, upgrade to a newer version.
The advantages compared to [libmemcached](http://libmemcached.org/libMemcached.html) (the official client):
Advantages compared to [libmemcached](http://libmemcached.org/libMemcached.html) (the official client):
- Thread safety. No need to set up separate clients for each thread.
- Support synchronous, asynchronous, semi-synchronous accesses etc. Support [ParallelChannel etc](combo_channel.md) to define access patterns declaratively.
......@@ -97,6 +97,6 @@ bool PopVersion(std::string* version);
# Request a memcached cluster
Create a `Channel` using the `c_md5` as the load balancing algorithm to access a memcached cluster mounted under a naming service. Note that each `MemcacheRequest` should contain only one operation or all operations have the same key. Under current implementation, multiple operations inside a single request are always sent to a same server. If the corresponding keys are located at different servers, the result must be wrong. In which case, you have to divide the request into multilples which has one operation each.
Create a `Channel` using the `c_md5` as the load balancing algorithm to access a memcached cluster mounted under a naming service. Note that each `MemcacheRequest` should contain only one operation or all operations have the same key. Under current implementation, multiple operations inside a single request are always sent to a same server. If the keys are located on different servers, the result must be wrong. In which case, you have to divide the request into multilple ones with one operation each.
Another choice is to use the common [twemproxy](https://github.com/twitter/twemproxy) solution, which makes clients access the cluster just like accessing a single server, although the solution needs to deploy proxies and adds more latency.
\ No newline at end of file
[redis](http://redis.io/) has been one of the most popular cache service in recent years. Compared to memcached it provides users with more data structures and interfaces, which frees the user from lots of development and thus covers a wide range of applications in baidu. In order to speed up the access to redis and make full use of bthread concurrency, brpc directly support the redis protocol. For examples please refer to: [example/redis_c++](https://github.com/brpc/brpc/tree/master/example/redis_c++/)
[中文版](../cn/redis_client.md)
Compared to [hiredis](https://github.com/redis/hiredis) (the official redis client), we have advantages in:
[redis](http://redis.io/) is one of the most popular caching service in recent years. Compared to memcached it provides users with more data structures and operations, speeding up developments. In order to access redis servers more conveniently and make full use of bthread's capability of concurrency, brpc directly supports the redis protocol. Check [example/redis_c++](https://github.com/brpc/brpc/tree/master/example/redis_c++/) for an example.
- Thread safety. No need to set up a separate client for each thread.
- Support access patterns of synchronous, asynchronous, batch synchronous, batch asynchronous. Can be used with ParallelChannel to enable access combinations.
- Support various [connection types](client.md#Connection Type). Support timeout, backup request, cancellation, tracing, built-in services, and other basic benefits of the RPC framework.
- Only a single connection between one process and one redis-server, which is more efficient when multiple threads access one redis-server at the same time (see [performance](#Performance)). The internal memory will be allocated block by block in succession regardless of the complexity of the reply, and short string optimization (SSO) is also implemented.
Advantages compared to [hiredis](https://github.com/redis/hiredis) (the official redis client):
Like http, brpc guarantees the time complexity of parsing redis reply is O(N) instead of O($N^2$) in the worst case, where N is the number of bytes of reply. This is important when reply consists of a large array.
- Thread safety. No need to set up separate clients for each thread.
- Support synchronous, asynchronous, semi-synchronous accesses etc. Support [ParallelChannel etc](combo_channel.md) to define access patterns declaratively.
- Support various [connection types](client.md#connection-type). Support timeout, backup request, cancellation, tracing, built-in services, and other benefits offered by brpc.
- All brpc clients in a process share a single connection to one redis-server, which is more efficient when multiple threads access one redis-server simultaneously (see [performance](#Performance)). Memory is allocated in blocks regardless of complexity of the reply, and short string optimization (SSO) is implemented to further improve performance.
For debugging, please turn on [-redis_verbose](#Debug) to print all the redis request and response to stderr.
Similarly with http, brpc guarantees that the time complexity of parsing redis replies is O(N) in worst cases rather than O(N^2) , where N is the number of bytes of reply. This is important when the reply consists of large arrays.
# Request to single redis
Turn on [-redis_verbose](#Debug) to print contents of all redis requests and responses to stderr, which is for debugging only.
Create a `Channel` to access redis:
# Request a redis server
Create a `Channel` for accessing redis:
```c++
#include <brpc/redis.h>
......@@ -29,7 +31,7 @@ if (redis_channel.Init("0.0.0.0:6379", &options) != 0) { // 6379 is the default
...
```
Execute `SET` followed by `INCR`:
Execute `SET`, then `INCR`:
```c++
std::string my_key = "my_key_1";
......@@ -45,11 +47,12 @@ if (cntl.Failed()) {
LOG(ERROR) << "Fail to access redis-server";
return -1;
}
// Get a reply by calling response.reply(i)
if (response.reply(0).is_error()) {
LOG(ERROR) << "Fail to set";
return -1;
}
// You can fetch and print response through the reply object
// A reply is printable in multiple ways
LOG(INFO) << response.reply(0).c_str() // OK
<< response.reply(0) // OK
<< response; // OK
......@@ -69,13 +72,13 @@ if (response.reply(0).is_error()) {
LOG(ERROR) << "Fail to incr";
return -1;
}
// The increased value
// A reply is printable in multiple ways
LOG(INFO) << response.reply(0).integer() // 2
<< response.reply(0) // (integer) 2
<< response; // (integer) 2
```
Execute `incr` and `decr` in batch
Execute `incr` and `decr` in batch:
```c++
brpc::RedisRequest request;
......@@ -103,7 +106,7 @@ CHECK_EQ(-10, response.reply(3).integer());
# RedisRequest
A [RedisRequest](https://github.com/brpc/brpc/blob/master/src/brpc/redis.h) object can hold multiple Command by calling `AddCommand*`, which returns true on success and false on error along with the calling stack.
A [RedisRequest](https://github.com/brpc/brpc/blob/master/src/brpc/redis.h) may contain multiple commands by calling `AddCommand*`, which returns true on success and false otherwise. **The callsite backtrace is also printed on error**.
```c++
bool AddCommand(const char* fmt, ...);
......@@ -111,46 +114,46 @@ bool AddCommandV(const char* fmt, va_list args);
bool AddCommandByComponents(const butil::StringPiece* components, size_t n);
```
The format parameter is compatible with hiredis: `%b` represents for binary data (pointer + length), and others are similar to those of `printf`. Some improvements have been made such as characters enclosed by single or double quotes will be recognized as a complete field regardless of the blanks inside the quote. For example, `AddCommand("Set 'a key with space' 'a value with space as well'")` sets value `a value with space as well` to key `a key with space`, while in hiredis it must be written as `redisvCommand(..., "SET% s% s", "a key with space", "a value with space as well");`
Formatting is compatible with hiredis, namely `%b` corresponds to binary data (pointer + length), others are similar to those in `printf`. Some improvements have been made such as characters enclosed by single or double quotes are recognized as one field regardless of the spaces inside. For example, `AddCommand("Set 'a key with space' 'a value with space as well'")` sets value `a value with space as well` to key `a key with space`, while in hiredis the command must be written as `redisvCommand(..., "SET% s% s", "a key with space", "a value with space as well");`
`AddCommandByComponents` is similar to `redisCommandArgv` in hiredis. Users specify each part of the command through an array. It's not the fastest way, but the most efficient, and it's immune to the escape problem, which usually occurs in `AddCommand` and `AddCommandV` . If you encounters an error of "quotation marks do not match" or "invalid format" when using `AddCommand` and `AddCommandV`, you should this method.
`AddCommandByComponents` is similar to `redisCommandArgv` in hiredis. Users specify each part of the command in an array, which is immune to escaping issues often occurring in `AddCommand` and `AddCommandV`. If you encounter errors such as "Unmatched quote" or "invalid format" when using `AddCommand` and `AddCommandV`, try this method.
If `AddCommand` fails, **subsequent `AddCommand` and `CallMethod` will also fail**. In general, there is no need to check whether `AddCommand*` failed or not, since it will be reflected through the RPC failure.
If `AddCommand*` fails, subsequent `AddCommand*` and `CallMethod` also fail. In general, there is no need to check return value of `AddCommand*`, since the RPC fails directly anyway.
Use `command_size()` to fetch the number of commands that have been added successfully.
Use `command_size()` to get number of commands added successfully.
Call `Clear()` to reuse the `RedisRequest` object.
Call `Clear()` before re-using the `RedisRequest` object.
# RedisResponse
[RedisResponse](https://github.com/brpc/brpc/blob/master/src/brpc/redis.h) can contain one or multiple [RedisReply](https://github.com/brpc/brpc/blob/master/src/brpc/redis_reply.h) objects. Use `reply_size()` for the total number of the replies and `reply(i)` for reference to the i-th reply (based from 0). Note that in hiredis, you have to call `redisGetReply` for N times to fetch response to N commands, while it's unnecessary in brpc as `RedisResponse` has already included the N replies. As long as RPC is successful, `response.reply_size()` should be equal to `request.command_size()`, unless redis-server has a bug (It's a basic premise of the redis-server that response and request have one by one correspondence)
A [RedisResponse](https://github.com/brpc/brpc/blob/master/src/brpc/redis.h) may contain one or multiple [RedisReply](https://github.com/brpc/brpc/blob/master/src/brpc/redis_reply.h)s. Use `reply_size()` for total number of replies and `reply(i)` for reference to the i-th reply (counting from 0). Note that in hiredis, if the request contains N commands, you have to call `redisGetReply` N times to get replies, while it's unnecessary in brpc as the `RedisResponse` already includes the N replies which are accessible by reply(i). As long as RPC is successful, `response.reply_size()` should be equal to `request.command_size()`, unless the redis-server is buggy. The precondition that redis works correctly is that replies correspond to commands one by one in the same sequence (positional correspondence).
Each `RedisReply` object could be:
Each `RedisReply` may be:
- REDIS_REPLY_NIL: NULL in redis, which means value does not exist. Can be determined by `is_nil()`.
- REDIS_REPLY_STATUS: Referred to `Simple String` in the redis document. It's usually used as the return value, such as the `OK` string returned by `SET`. Can be determined by `is_string()` (It's the same function for REDIS_REPLY_STRING, so users can't distinguish status from string for now). Use `c_str()` or `data()` to get the value.
- REDIS_REPLY_STRING: Referred to `Bulk String` in the redis document. Most return values are of this type, including those can be `incr`ed. You can use `is_string()` to validate and `c_str()` or `data()` for the value.
- REDIS_REPLY_ERROR: The error message when operation failed. Can be determined by `is_error()` and fetched by `error_message()`.
- REDIS_REPLY_INTEGER: A 64-bit signed integer. Can be determined by `is_integer()` and fetched by `integer()`.
- REDIS_REPLY_ARRAY: Array of replies. Can be determined by `is_array()`. Use `size()` for the total size of the array and `[i]` for the reference to the corresponding sub-reply.
- REDIS_REPLY_NIL: NULL in redis, which means value does not exist. Testable by `is_nil()`.
- REDIS_REPLY_STATUS: Referred to `Simple String` in the redis document, usually used as the status of operations, such as the `OK` returned by `SET`. Testable by `is_string()` (same function for REDIS_REPLY_STRING). Use `c_str()` or `data()` to get the value.
- REDIS_REPLY_STRING: Referred to `Bulk String` in the redis document. Most return values are of this type, including those returned by `incr`. Testable by `is_string()`. Use `c_str()` or `data()` for the value.
- REDIS_REPLY_ERROR: The error message for a failed operation. Testable by `is_error()`. Use `error_message()` to get the message.
- REDIS_REPLY_INTEGER: A 64-bit signed integer. Testable by `is_integer()`. Use `integer()` to get the value.
- REDIS_REPLY_ARRAY: Array of replies. Testable by `is_array()`. Use `size()` for size of the array and `[i]` for the reference to the corresponding sub-reply.
For example, a response contains three replies: an integer, a string and an array (size = 2). Then we can use `response.reply(0).integer()`, `response.reply(1).c_str()`, and `repsonse.reply(2)[0], repsonse.reply(2)[1]` to fetch their values respectively. If the type is not correct, the call stack will be printed and an undefined is returned.
If a response contains three replies: an integer, a string and an array with 2 items, we can use `response.reply(0).integer()`, `response.reply(1).c_str()`, and `repsonse.reply(2)[0]`, `repsonse.reply(2)[1]` to fetch values respectively. If the type is not correct, backtrace of the callsite is printed and an undefined value is returned.
The ownership of all the reply objects belongs to `RedisResponse`. All relies will be destroyed when response has been freed. Also note that copy is forbidden for `RedisReply`.
Ownership of all replies belongs to `RedisResponse`. All relies are destroyed when response is destroyed.
Call `Clear()` to reuse the `RedisRespones` object.
Call `Clear()` before re-using the `RedisRespones` object.
# Request to redis cluster
# Request a redis cluster
For now please use [twemproxy](https://github.com/twitter/twemproxy) as a common way to wrap redis cluster so that it can be used just like a single node proxy, in which case you can just replace your hiredis with brpc. Accessing the cluster directly from client (using consistent hash) may reduce the delay, but at the cost of other management services. Make sure to double check that in redis document.
Create a `Channel` using the consistent hashing as the load balancing algorithm(c_md5 or c_murmurhash) to access a redis cluster mounted under a naming service. Note that each `RedisRequest` should contain only one command or all commands have the same key. Under current implementation, multiple commands inside a single request are always sent to a same server. If the keys are located on different servers, the result must be wrong. In which case, you have to divide the request into multilple ones with one command each.
If you maintain a redis cluster like the memcache all by yourself, it should be accessible using consistent hash. In general, you have to make sure each `RedisRequest` contains only one command or keys from multiple commands fall on the same server, since under the current implementation, if a request contains multiple commands, it will always be sent to the same server. For example, if a request contains a number of Get while the corresponding keys distribute in different servers, the result must be wrong, in which case you have to separate the request according to key distribution.
Another choice is to use the common [twemproxy](https://github.com/twitter/twemproxy) solution, which makes clients access the cluster just like accessing a single server, although the solution needs to deploy proxies and adds more latency.
# Debug
Turn on [-redis_verbose](http://brpc.baidu.com:8765/flags/redis_verbose) to print all redis request and response to stderr. Note that this should only be used for debug instead of online production.
Turn on [-redis_verbose](http://brpc.baidu.com:8765/flags/redis_verbose) to print contents of all redis requests and responses to stderr. Note that this should only be used for debugging rather than online services.
Turn on [-redis_verbose_crlf2space](http://brpc.baidu.com:8765/flags/redis_verbose_crlf2space) to replace the `CRLF` (\r\n) with spaces for better readability.
Turn on [-redis_verbose_crlf2space](http://brpc.baidu.com:8765/flags/redis_verbose_crlf2space) to replace the `CRLF` (\r\n) with spaces in debugging logs for better readability.
| Name | Value | Description | Defined At |
| ------------------------ | ----- | ---------------------------------------- | ---------------------------------- |
......@@ -159,9 +162,9 @@ Turn on [-redis_verbose_crlf2space](http://brpc.baidu.com:8765/flags/redis_verbo
# Performance
redis version: 2.6.14 (latest version is 3.0+)
redis version: 2.6.14
Start a client to send requests to redis-server from the same machine using 1, 50, 200 bthreads synchronously. The time unit for latency is microseconds.
Start a client to send requests to a redis-server on the same machine using 1, 50, 200 bthreads synchronously. The latency is in microseconds.
```
$ ./client -use_bthread -thread_num 1
......@@ -180,9 +183,9 @@ TRACE: 02-13 19:43:49: * 0 client.cpp:180] Accessing redis server at qps=41167
TRACE: 02-13 19:43:50: * 0 client.cpp:180] Accessing redis server at qps=412583 latency=482
```
The QPS reaches the limit after 200 threads, which is much higher than hiredis since brpc uses a single connection to redis-server by default and requests from multiple threads will be [merged in a wait-free way](io.md#发消息). As a result, from the redis-server's view, it received a bunch of requests and read/handle them in batch, thus getting much higher QPS than non-batched ones. The QPS drop in the following test using connection pool to visit redis-server is another proof.
The peak QPS at 200 threads is much higher than hiredis since brpc uses a single connection to redis-server by default and requests from multiple threads are [merged in a wait-free way](io.md#sending-messages), making the redis-server receive requests in batch and reach a much higher QPS. The lower QPS in following test that uses pooled connections is another proof.
Start a client to send requests (10 commands per request) to redis-server from the same machine using 1, 50, 200 bthreads synchronously. The time unit for latency is microseconds.
Start a client to send requests in batch (10 commands per request) to redis-server on the same machine using 1, 50, 200 bthreads synchronously. The latency is in microseconds.
```
$ ./client -use_bthread -thread_num 1 -batch 10
......@@ -207,9 +210,9 @@ TRACE: 02-13 19:49:11: * 0 client.cpp:180] Accessing redis server at qps=29271
16878 gejun 20 0 48136 2508 1004 R 99.9 0.0 13:36.59 redis-server // thread_num=200
```
Note that the actual commands processed per second of redis-server is 10 times the QPS value, which is about 400K. When thread_num equals 50 or higher, the CPU usage of the redis-server reaches its limit. Since redis-server runs in [single-threaded reactor mode](threading_overview.md#单线程reactor), 99.9% on one core is the maximum CPU it can use.
Note that the commands processed per second by the redis-server is the QPS times 10, which is about 400K. When thread_num equals 50 or higher, the CPU usage of the redis-server reaches limit. Note that redis-server is a [single-threaded reactor](threading_overview.md#single-threaded-reactor), utilizing one core is the maximum that it can do.
Now start a client to send requests to redis-server from the same machine using 50 bthreads synchronously through connection pool.
Now start a client to send requests to redis-server on the same machine using 50 bthreads synchronously through pooled connections.
```
$ ./client -use_bthread -connection_type pooled
......@@ -221,11 +224,13 @@ TRACE: 02-13 18:07:42: * 0 client.cpp:180] Accessing redis server at qps=75238
16878 gejun 20 0 48136 2520 1004 R 99.9 0.0 9:52.33 redis-server
```
We can see a tremendous drop of QPS compared to single connection and the redis-server has reached full CPU usage. The reason is that each time only one request from a connection can be read by the redis-server, which greatly increases the cost of IO operation.
We can see a tremendous drop of QPS compared to the one using single connection above, and the redis-server has reached the CPU cap. The cause is that each time only one request can be read from a connection by the redis-server, which significantly increases cost of IO operations. This is also the peak performance of a hiredis client.
# Command Line Interface
example/redis_c++/redis_cli is a command line tool just like the official CLI, which shows the ability of brpc to handle the redis protocol. When you encounter an unexpected result from redis-server using brpc, you should try this CLI to debug interactively.
[example/redis_c++/redis_cli](https://github.com/brpc/brpc/blob/master/example/redis_c%2B%2B/redis_cli.cpp) is a command line tool similar to the official CLI, demostrating brpc's capability to talk with redis servers. When unexpected results are got from a redis-server using a brpc client, you can debug with this tool interactively as well.
Like the official CLI, `redis_cli <command>` runs the command directly, and `-server` which is address of the redis-server can be specified.
```
$ ./redis_cli
......@@ -250,5 +255,3 @@ OK
redis 127.0.0.1:6379> client getname
"brpc-cli"
```
Like the official CLI, `redis_cli <command>` can be used to issue commands directly, and use `-server` to specify the address of redis-server.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment