memcache クライアントを実装してみた

目的

  • 主に自分の勉強です
  • クライアントはいろんな言語で実装されてるけど、やっぱり自分でやってみることに意義があるよね


実装項目

以下の3つを実装する

  • set
  • get
  • stats


ソース

my_memcache.py

  1 # -*- coding:utf-8 -*-
  2 import socket
  3 import pickle
  4 import base64
  5 
  6 class MyMemcache:
  7 
  8   def __init__(self):
  9     self.__sock = socket.socket()
 10 
 11   def __del__(self):
 12     self.__sock.close()
 13 
 14   def connect(self, host, port):
 15     self.__sock.connect((host, port))
 16 
 17   def close(self):
 18     self.__sock.close()
 19 
 20   def set(self, key, value, exptime=0, flags=0):
 21     serialized_value = base64.b64encode(pickle.dumps(value))
 22     self.__sock.send("set %s %d %s %d\r\n" % (key, flags, exptime, len(serialized_value)) )
 23     self.__sock.send("%s\r\n" % serialized_value)
 24 
 25     result = self.__sock.recv(64)
 26 
 27     if result == "STORED\r\n":
 28       return True
 29     else:
 30       return False
 31 
 32   def get(self, key):
 33     self.__sock.send("get %s\r\n" % key)
 34     lines = self.__sock.recv(1024).splitlines()
 35     serialized_value = lines[1]
 36     value = pickle.loads(base64.b64decode(serialized_value))
 37     return value
 38 
 39   def stats(self):
 40     self.__sock.send("stats\r\n")
 41     lines = self.__sock.recv(1024).splitlines()
 42 
 43     values = {}
 44 
 45     for line in lines:
 46       if line == 'END':
 47         break
 48 
 49       else:
 50         [dummy, key, value] = line.split()
 51         values[key] = value
 52 
 53     return values
 54 

解説

set

  • 引数は以下の4つ
    • key : 必須
    • value : 必須
    • exptime : 任意(デフォルトは0)
    • flags : 任意(デフォルトは0)
  • 整数や文字列以外に、リストや辞書などもくることを考慮し、pickle を使って value を直列化した
    • 直列化した際、改行が含まれていたため、base64 エンコードすることで、1行の文字列として格納した
  • 返り値が "STORED\r\n" のときに "True" を返すようにした
    • それ以外は "False" としたため、若干処理が甘いかもしれない

get

  • 返り値が3行の文字列なので、まず改行文字で分割した
    • そのうち2行目が set で登録した値である
      • set したときに value を直列化してエンコードしたので、デコードして非直列化して value を取り出す

stats

  • 以下のフォーマットで返ってくる
    • STAT version 1.2.8
  • なので、文字列を分割して辞書に登録する
    • 文字列 "END" が返ってきたら終了する


テストコード

my_memcache.py

 55 if __name__ == '__main__':
 56   memcache = MyMemcache()
 57   memcache.connect("localhost", 11211)
 58 
 59   print "########## stats ##########"
 60   print memcache.stats()
 61   print "\n"
 62 
 63   print "########## string ##########"
 64   key = "string_key"
 65   value = "string_value"
 66   print "key :" , key
 67   print "value :" , value
 68   print "---------- set ----------"
 69   print memcache.set(key, value)
 70   print "---------- get ----------"
 71   print memcache.get(key)
 72   print type(memcache.get(key))
 73   print "\n"
 74 
 75   print "########## list ##########"
 76   key = "list_key"
 77   value = ['a', 'b', 'c']
 78   print "key :" , key
 79   print "value :" , value
 80   print "---------- set ----------"
 81   print memcache.set(key, value)
 82   print "---------- get ----------"
 83   print memcache.get(key)
 84   print type(memcache.get(key))
 85   print "\n"
 86 
 87   print "########## tuple ##########"
 88   key = "tuple_key"
 89   value = (1, 2, 3)
 90   print "key :" , key
 91   print "value :" , value
 92   print "---------- set ----------"
 93   print memcache.set(key, value)
 94   print "---------- get ----------"
 95   print memcache.get(key)
 96   print type(memcache.get(key))
 97   print "\n"
 98 
 99   print "########## dict ##########"
100   key = "dict_key"
101   value = {'a' : 1, 'b' : 2, 'c' : 3}
102   print "key :" , key
103   print "value :" , value
104   print "---------- set ----------"
105   print memcache.set(key, value)
106   print "---------- get ----------"
107   print memcache.get(key)
108   print type(memcache.get(key))
109   print "\n"
110 


実行

$ python my_memcache.py
########## stats ##########
{'pid': '11509', 'total_items': '64', 'uptime': '47141', 'listen_disabled_num': '0', 'version': '1.2.8', 'limit_maxbytes': '134217728', 'rusage_user': '0.138425', 'bytes_read': '4793', 'accepting_conns': '1', 'rusage_system': '0.238628', 'cmd_get': '86', 'curr_connections': '6', 'threads': '2', 'total_connections': '35', 'cmd_set': '65', 'curr_items': '6', 'get_misses': '0', 'cmd_flush': '0', 'evictions': '0', 'bytes': '618', 'connection_structures': '7', 'bytes_written': '8006', 'time': '1272860539', 'pointer_size': '64', 'get_hits': '86'}


########## string ##########
key : string_key
value : string_value
---------- set ----------
True
---------- get ----------
string_value
<type 'str'>


########## list ##########
key : list_key
value : ['a', 'b', 'c']
---------- set ----------
True
---------- get ----------
['a', 'b', 'c']
<type 'list'>


########## tuple ##########
key : tuple_key
value : (1, 2, 3)
---------- set ----------
True
---------- get ----------
(1, 2, 3)
<type 'tuple'>


########## dict ##########
key : dict_key
value : {'a': 1, 'c': 3, 'b': 2}
---------- set ----------
True
---------- get ----------
{'a': 1, 'c': 3, 'b': 2}
<type 'dict'>
  • 格納した値が、ちゃんと返ってきてるよ、やったね
    • リストとか辞書は、型も正しいよ、やったね


参考