業務で libmemcached を利用した小さなモジュールを作ることになった。このライブラリにはC++インタフェースも用意されているのでCとC++の両方で試してみる。
環境は CentOS 5.6 (x86_64) , gcc4.4, libmemcached-devel
* インストール
remiレポジトリから新しいバージョンを入れる
1 |
$ sudo yum install libmemcached-devel.x86_64 --enablerepo=remi |
* 各基本操作(set, get, delete)を行う C版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
// libmemcached_test.c #include <stdio.h> #include <string.h> #include <unistd.h> #include <libmemcached/memcached.h> int main(void) { memcached_st *memc; memcached_server_st *servers = NULL; memcached_return rc; const char* server_list = "localhost:11211"; const char* key= "key"; const char* value= "value"; char* retval = NULL; size_t len = 0; time_t expire = 0; uint32_t flags = 0; // 初期化 memc = memcached_create(NULL); servers = memcached_servers_parse(server_list); rc = memcached_server_push(memc, servers); memcached_server_list_free(servers); // すぐに解放してOK if(rc == MEMCACHED_SUCCESS) { printf("added server successfully\n"); } else { printf("couldn't add server: %s\n", memcached_strerror(memc, rc)); } // set rc = memcached_set(memc, key, strlen(key), value, strlen(value), expire, flags); if(rc == MEMCACHED_SUCCESS) { printf("key stored successfully\n"); } else { printf("couldn't store key: %s\n", memcached_strerror(memc, rc)); } // get retval = memcached_get(memc, key, strlen(key), &len, &flags, &rc); if(rc == MEMCACHED_SUCCESS) { printf("key got successfully\n"); printf("value: %s\n", retval); } else { printf("couldn't get key: %s\n", memcached_strerror(memc, rc)); } free(retval); // 忘れないように!! // delete rc = memcached_delete(memc, key, strlen(key), expire); if(rc == MEMCACHED_SUCCESS) { printf("key deleted successfully\n"); } else { printf("couldn't delete key: %s\n", memcached_strerror(memc, rc)); } // get retval = memcached_get(memc, key, strlen(key), &len, &flags, &rc); if(rc == MEMCACHED_SUCCESS) { printf("key got successfully\n"); printf("value: %s\n", retval); } else if(rc == MEMCACHED_NOTFOUND) { // keyが存在しない printf("couldn't get key (not found): %s\n", memcached_strerror(memc, rc)); } else if(rc == MEMCACHED_FAILURE) { // memcachedに接続できない printf("couldn't get key (failure): %s\n", memcached_strerror(memc, rc)); } free(retval); // 解放 memcached_free(memc); return 0; } |
* コンパイル, 実行 (+ valgrindでメモリリークのチェック)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$ gcc44 -o -libmemcached_test libmemcached.c -L/usr/lib64 -lmemcached $ valgrind ./libmemcached_test ==10510== Memcheck, a memory error detector ==10510== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al. ==10510== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info ==10510== Command: ./libmemcached_test ==10510== added server successfully key stored successfully key got successfully value: value key deleted successfully couldn't get key (not found): NOT FOUND ==10510== ==10510== HEAP SUMMARY: ==10510== in use at exit: 0 bytes in 0 blocks ==10510== total heap usage: 54 allocs, 54 frees, 42,214 bytes allocated ==10510== ==10510== All heap blocks were freed -- no leaks are possible ==10510== ==10510== For counts of detected and suppressed errors, rerun with: -v ==10510== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4) |
memcached_get 内で malloc が行われるので free で解放するのを忘れずに。また、key が存在しない場合は NOT FOUND が、memcachedに接続出来ない場合などは FAILURE が返る。
あと、libmemcachedは関係ないけど以下のような警告が出た場合は文字列の扱いを見直す必要がある。
1 |
deprecated conversion from string constant to ‘char*’ |
どうやら gcc4.2 から(? 文字列の扱いに厳しくなっていて、const を付けるべき所はちゃんと付けないとだめらしい。見かけない警告だったので少し焦ったけど、そういえば会社の開発環境は gcc4.1 だったかもしれない。
次はC++インタフェースを使う。APIの使い方は libmmecached/memcached.hpp を読んで確認。
* 各基本操作(set, get, delete)を行う C++版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
#include <iostream> // ヘッダは memcached.h ではなく memcached.hpp を include #include <libmemcached/memcached.hpp> int main(void) { using namespace std; try { // Memcache インスタンス生成 // 内部で memcached_create などが呼ばれて初期化処理が行われる memcache::Memcache memc("localhost:11211"); string key = "mykey"; string value_str = "myvalue"; // value は std::string ではなく std::vector<char> で扱う vector<char> value(value_str.begin(), value_str.end()); vector<char> retval; time_t expire = 0; uint32_t flags = 0; // set // bool set(const std::string &key, const std::vector<char> &value, // time_t expiration, uint32_t flags) if(memc.set(key, value, expire, flags)) { cout<<"key stored successfully"<<endl; } else { cerr<<"couldn't store key"<<endl; } // get // bool get(const std::string &key, std::vector<char> &ret_val) if(memc.get(key, retval)) { cout<<"key got successfully"<<endl; cout<<&retval[0]<<endl; } else { cerr<<"couldn't get key"<<endl; } // delete // bool remove(const std::string &key) if(memc.remove(key)) { cout<<"key deleted successfully"<<endl; } else { cerr<<"couldn't delete key"<<endl; } } catch(const memcache::Error& e) { cerr<<"catch exception"<<endl; cerr<<e.what()<<endl; cerr<<e.getErrno()<<endl; } return 0; } |
注意する点は、value は std::string ではなく std::vector<char> で扱うところ。
ほとんどのC++ APIは内部でC APIを呼んでいるだけの薄いラッパーになっていて、それらのC APIの結果が MEMCACHED_SUCCESS の場合は true を返す仕様になっている (一部のAPIは memcached_return_t を返す)。ただ、異常系処理において memcache::Error はあまり役に立たず、APIの戻り値チェックがいちいち必要なところは変わらない。。
C APIがどれくらいカバーされているかまだ調べきれてないけど、使うのは簡単なのでつまづくところは少なそう。