memcached是常用的缓存服务,为了使用户更快捷地访问memcached并充分利用bthread的并发能力,brpc直接支持memcache协议。示例程序:example/memcache_c++
注意:brpc只支持memcache的二进制协议。memcached在1.3前只有文本协议,但在当前看来支持的意义甚微。如果你的memcached早于1.3,升级版本。
相比使用libmemcached(官方client)的优势有:
当前实现充分利用了RPC的并发机制并尽量避免了拷贝。一个client可以轻松地把一个同机memcached实例(版本1.4.15)压到极限:单连接9万,多连接33万。在大部分情况下,brpc client能充分发挥memcached的性能。
创建一个访问memcached的Channel:
#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中设置一份数据。
// 写入key="hello" value="world" flags=0xdeadbeef,10秒失效,无视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; } ...
上述代码的说明:
目前支持的请求操作有:
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);
建立一个使用c_md5负载均衡算法的channel就能访问挂载在对应命名服务下的memcached集群了。注意每个MemcacheRequest应只包含一个操作或确保所有的操作是同一个key。如果request包含了多个操作,在当前实现下这些操作总会送向同一个server,假如对应的key分布在多个server上,那么结果就不对了,这个情况下你必须把一个request分开为多个,每个包含一个操作。
或者你可以沿用常见的twemproxy方案。这个方案虽然需要额外部署proxy,还增加了延时,但client端仍可以像访问单点一样的访问它。