访问 memcached
访问 memcached
学习如何访问 memcached。
memcached 是一种常用的缓存服务。为了更方便地访问 memcached,并充分利用 bthread 的并发能力,brpc 直接支持 memcached 协议。参见 example/memcache_c++ 获取示例。
注意:brpc 只支持 memcache 的二进制协议。文本协议自 memcached 1.3 起已被弃用,支持它几乎没有好处。如果你的 memcached 版本低于 1.3,请升级到更新的版本。
与 libmemcached(官方客户端)相比的优势:
- 线程安全。无需为每个线程创建独立的客户端。
- 支持同步、异步、半同步等多种访问方式。支持 ParallelChannel 等,可声明式地定义访问模式。
- 支持各种连接类型。支持超时、备份请求、取消、追踪、内置服务等 brpc 提供的诸多能力。
- 具备请求和响应的概念,而 libmemcached 没有。用户必须额外做记账工作,才能把收到的消息与发出的消息关联起来,这并非易事。
当前实现充分利用了 RPC 的并发机制,并尽可能避免拷贝。单个客户端即可轻松把同一台机器上的一个 memcached 实例(版本 1.4.15)推到极限:单连接 90,000 QPS,多连接 330,000 QPS。在大多数场景下,brpc 都能充分发挥 memcached 的能力。
访问 memcached 服务器
创建一个 Channel 用于访问 memcached:
#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 is the default port for memcached
LOG(FATAL) << "Fail to init channel to memcached";
return -1;
}
...以下示例尝试向 memcached 写入数据:
// Set key="hello" value="world" flags=0xdeadbeef, expire in 10s, and ignore 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.CallMethod并将method设为 NULL 即可。 - 调用
request.XXX()来添加一个操作,本例中XXX为Set。一个请求中的多个操作会一起发送给 memcached 服务器(通常称为“管道模式”)。 - 调用
response.PopXXX()从响应中弹出一个操作的结果,本例中XXX为Set。成功时返回 true,否则返回 false,此时可用response.LastError()获取错误信息。XXX必须与请求中对应的操作匹配,否则弹出操作会被拒绝。在上面的例子中,一个PopGet会失败并给出“not a GET response”的错误信息。 Pop的结果与 RPC 结果相互独立。即使“无法将值放入 memcached”,RPC 仍可能成功。RPC 失败指的是连接中断、超时等情况。如果业务逻辑要求 memcache 操作必须成功,你应该同时检查 RPC 和PopXXX是否成功。
当前支持的操作:
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();回复中对应的操作:
// 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 应当只包含一个操作,或者所有操作使用相同的键。在当前实现中,单个请求内的多个操作始终会发送到同一台服务器。如果这些键位于不同的服务器上,结果必然是错误的。在这种情况下,你需要将请求拆分为多个只包含一个操作的请求。
另一种选择是使用通用的 twemproxy 解决方案,它让客户端访问集群就像访问单台服务器一样,尽管该方案需要部署代理并会增加额外的延迟。
最后修改于 2022 年 5 月 17 日:update brpc users page (devlive-community/knowforge#71) (a31ce10d3)]
评论
登录后参与评论
KnowForge