brpc/docs/cn/memcache_client.md
2022-12-14 20:13:26 +08:00

103 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[English version](../en/memcache_client.md)
[memcached](http://memcached.org/)是常用的缓存服务为了使用户更快捷地访问memcached并充分利用bthread的并发能力brpc直接支持memcache协议。示例程序[example/memcache_c++](https://github.com/brpc/brpc/tree/master/example/memcache_c++/)
**注意**brpc只支持memcache的二进制协议。memcached在1.3前只有文本协议但在当前看来支持的意义甚微。如果你的memcached早于1.3,升级版本。
相比使用[libmemcached](http://libmemcached.org/libMemcached.html)(官方client)的优势有:
- 线程安全。用户不需要为每个线程建立独立的client。
- 支持同步、异步、半同步等访问方式,能使用[ParallelChannel等](combo_channel.md)组合访问方式。
- 支持多种[连接方式](client.md#连接方式)。支持超时、backup request、取消、tracing、内置服务等一系列brpc提供的福利。
- 有明确的request和response。而libmemcached是没有的收到的消息不能直接和发出的消息对应上用户得做额外开发而且并没有那么容易做对。
当前实现充分利用了RPC的并发机制并尽量避免了拷贝。一个client可以轻松地把一个同机memcached实例(版本1.4.15)压到极限单连接9万多连接33万。在大部分情况下brpc client能充分发挥memcached的性能。
# 访问单台memcached
创建一个访问memcached的Channel
```c++
#include <brpc/memcache.h>
#include <brpc/channel.h>
brpc::ChannelOptions options;
options.protocol = brpc::PROTOCOL_MEMCACHE;
if (channel.Init("0.0.0.0:11211", &options) != 0) { // 11211是memcached的默认端口
LOG(FATAL) << "Fail to init channel to memcached";
return -1;
}
...
```
往memcached中设置一份数据。
```c++
// 写入key="hello" value="world" flags=0xdeadbeef10秒失效无视cas。
brpc::MemcacheRequest request;
brpc::MemcacheResponse response;
brpc::Controller cntl;
if (!request.Set("hello", "world", 0xdeadbeef/*flags*/, 10/*expiring seconds*/, 0/*ignore cas*/)) {
LOG(FATAL) << "Fail to SET request";
return -1;
}
channel.CallMethod(NULL, &cntl, &request, &response, NULL/*done*/);
if (cntl.Failed()) {
LOG(FATAL) << "Fail to access memcached, " << cntl.ErrorText();
return -1;
}
if (!response.PopSet(NULL)) {
LOG(FATAL) << "Fail to SET memcached, " << response.LastError();
return -1;
}
...
```
上述代码的说明:
- 请求类型必须为MemcacheRequest回复类型必须为MemcacheResponse否则CallMethod会失败。不需要stub直接调用channel.CallMethodmethod填NULL。
- 调用request.XXX()增加操作本例XXX=Set一个request多次调用不同的操作这些操作会被同时送到memcached常被称为pipeline模式
- 依次调用response.PopXXX()弹出操作结果本例XXX=Set成功返回true失败返回false调用response.LastError()可获得错误信息。XXX必须和request的依次对应否则失败。本例中若用PopGet就会失败错误信息为“not a GET response"。
- Pop结果独立于RPC结果。即使“不能把某个值设入memcached”RPC可能还是成功的。RPC失败指连接断开超时之类的。如果业务上认为要成功操作才算成功那么你不仅要判RPC成功还要判PopXXX是成功的。
目前支持的请求操作有:
```c++
bool Set(const Slice& key, const Slice& value, uint32_t flags, uint32_t exptime, uint64_t cas_value);
bool Add(const Slice& key, const Slice& value, uint32_t flags, uint32_t exptime, uint64_t cas_value);
bool Replace(const Slice& key, const Slice& value, uint32_t flags, uint32_t exptime, uint64_t cas_value);
bool Append(const Slice& key, const Slice& value, uint32_t flags, uint32_t exptime, uint64_t cas_value);
bool Prepend(const Slice& key, const Slice& value, uint32_t flags, uint32_t exptime, uint64_t cas_value);
bool Delete(const Slice& key);
bool Flush(uint32_t timeout);
bool Increment(const Slice& key, uint64_t delta, uint64_t initial_value, uint32_t exptime);
bool Decrement(const Slice& key, uint64_t delta, uint64_t initial_value, uint32_t exptime);
bool Touch(const Slice& key, uint32_t exptime);
bool Version();
```
对应的回复操作:
```c++
// Call LastError() of the response to check the error text when any following operation fails.
bool PopGet(IOBuf* value, uint32_t* flags, uint64_t* cas_value);
bool PopGet(std::string* value, uint32_t* flags, uint64_t* cas_value);
bool PopSet(uint64_t* cas_value);
bool PopAdd(uint64_t* cas_value);
bool PopReplace(uint64_t* cas_value);
bool PopAppend(uint64_t* cas_value);
bool PopPrepend(uint64_t* cas_value);
bool PopDelete();
bool PopFlush();
bool PopIncrement(uint64_t* new_value, uint64_t* cas_value);
bool PopDecrement(uint64_t* new_value, uint64_t* cas_value);
bool PopTouch();
bool PopVersion(std::string* version);
```
# 访问memcached集群
建立一个使用c_md5负载均衡算法的channel就能访问挂载在对应命名服务下的memcached集群了。注意每个MemcacheRequest应只包含一个操作或确保所有的操作是同一个key。如果request包含了多个操作在当前实现下这些操作总会送向同一个server假如对应的key分布在多个server上那么结果就不对了这个情况下你必须把一个request分开为多个每个包含一个操作。
或者你可以沿用常见的[twemproxy](https://github.com/twitter/twemproxy)方案。这个方案虽然需要额外部署proxy还增加了延时但client端仍可以像访问单点一样的访问它。