這個模組是一個Python
操作memcached
的一個API介面。
Memcached官網:http://memcached.org/
模組官網:https://pypi.python.org/pypi/python-memcached/
What is Memcached?
Free & open source, high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load.
Memcached is an in-memory key-value store for small chunks of arbitrary data (strings, objects) from results of database calls, API calls, or page rendering.
Memcached is simple yet powerful. Its simple design promotes quick deployment, ease of development, and solves many problems facing large data caches. Its API is available for most popular languages.
以上內容摘自官網的介紹,具體資訊訪問官網
安裝Memcached
包安裝
Ubuntu
1 |
apt-get install memcached |
CentOS
1 |
yum install memcached |
原始碼安裝
Memcached
原始碼包安裝的時候需要依賴於libevent
1 2 3 4 |
# Ubuntu apt-get install libevent-dev # CentOS yum install libevent-devel |
編譯安裝memcached
1 2 3 4 5 |
wget https://memcached.org/latest tar -zxf memcached-1.x.x.tar.gz cd memcached-1.x.x ./configure --prefix=/usr/local/memcached make && make test && sudo make install |
具體引數見./configure --help
全選項,SASL支援需要一些可選附加庫。
啟動
我這裡安裝的時候是採用包的方式進行安裝的。
1 |
[root@anshengme ~]# memcached -d -m 10 -u root -l 0.0.0.0 -p 11211 -c 256 -P /tmp/memcached.pid |
引數說明
引數 | 描述 |
---|---|
-d |
是啟動一個守護程式 |
-m |
是分配給Memcache使用的記憶體數量,單位是MB |
-u |
是執行Memcache的使用者 |
-l |
是監聽的伺服器IP地址 |
-p |
是設定Memcache監聽的埠,最好是1024以上的埠 |
-c |
選項是最大執行的併發連線數,預設是1024,按照你伺服器的負載量來設定 |
-P |
是設定儲存Memcache的pid檔案 |
設定開機自啟動
1 2 |
[root@anshengme ~]# chmod +x /etc/rc.d/rc.local [root@anshengme ~]# echo 'memcached -d -m 10 -u root -l 0.0.0.0 -p 11211 -c 256 -P /tmp/memcached.pid' >> /etc/rc.local |
關閉memcached
1 |
[root@anshengme ~]# pkill `cat /tmp/memcached.pid` |
測試
先檢視11211埠是否啟動
1 2 |
[root@anshengme ~]# netstat -tlnp | grep "11211" tcp 0 0 0.0.0.0:11211 0.0.0.0:* LISTEN 4245/memcached |
使用telnet檢視啟動的11211埠是否可以,可以則測試OK,否則就需要你拍錯了,但願沒有問題。
1 2 3 4 |
[root@anshengme ~]# telnet 127.0.0.1 11211 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. |
如果出現以下內容就代表啟動成功!
Memcache使用
安裝Memcache
下載模組包,目前最新版
1 |
[root@anshengme ~]# wget https://pypi.python.org/packages/f7/62/14b2448cfb04427366f24104c9da97cf8ea380d7258a3233f066a951a8d8/python-memcached-1.58.tar.gz#md5=23b258105013d14d899828d334e6b044 |
解壓並安裝
1 2 3 |
[root@anshengme ~]# tar xf python-memcached-1.58.tar.gz [root@anshengme ~]# cd python-memcached-1.58 [root@anshengme python-memcached-1.58]# python setup.py install |
進入Python直譯器匯入模組,如果匯入成功就表示模組安裝成功。
1 2 3 |
[root@anshengme python-memcached-1.58]# python >>> import memcache >>> |
首次體驗
1 2 3 4 5 6 7 8 9 10 |
# 匯入memcache模組 >>> import memcache # 連線到一臺Memcached伺服器 >>> conn = memcache.Client(['192.168.56.100:11211']) # 設定一個值,如果存在則覆蓋 >>> conn.set('k1', 'v1') True # 獲取值的內容 >>> conn.get('k1') 'v1' |
更多的使用方法
設定超時時間
1 2 3 |
>>> conn.set('k', 'v', 1) True >>> conn.get('k') |
設定值,如果存在就報錯
1 2 3 4 5 6 |
>>> conn.add('k','hello') # False設定失敗 False >>> conn.get('k') # 原值沒變 'v' |
修改值,不存在則返回False
1 2 3 4 5 6 7 8 9 |
>>> conn.replace('k','helloworld') # 設定成功 True >>> conn.get('k') # 返回修改後的值 'helloworld' >>> conn.replace('kkkk','hello') # 修改一個不存在的值 False |
設定多個值,值不存在則建立,存在則修改
1 2 3 4 5 |
>>> conn.get('key1') >>> conn.set_multi({'key1':'value1','key2':'value2'}) [] >>> conn.get('key1') 'value1' |
刪除一個值
1 2 3 4 5 |
>>> conn.get('key1') 'value1' >>> conn.delete('key1') 1 >>> conn.get('key1') |
刪除多個值
1 2 3 4 |
>>> conn.set_multi({'key3':'value3','key4':'value4'}) [] >>> conn.delete_multi(['key3', 'key4']) 1 |
獲取一個值和獲取多個值
1 2 3 4 5 6 |
>>> conn.set_multi({'key5':'value5','key6':'value6'}) [] >>> conn.get('key5') 'value5' >>> conn.get_multi(['key5','key6']) {'key5': 'value5', 'key6': 'value6'} |
修改指定key的值,在該值後面
追加內容
1 2 3 4 |
>>> conn.append('key5','after') True >>> conn.get('key5') 'value5after' |
修改指定key的值,在該值 前面 插入內容
1 2 3 4 |
>>> conn.prepend('key5','before') True >>> conn.get('key5') 'beforevalue5after' |
自增與自減,將Memcached中的某一個值加或減少N(N預設為1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
>>> conn.set('number','9') True # 增加 >>> conn.incr('number') 10 # 增加 >>> conn.incr('number', 10) 20 # 減少 >>> conn.decr('number') 19 # 減少 >>> conn.decr('number', 10) 9 |
比如設定了這麼一個值:
1 |
conn.set('n','10) |
現在A使用者和B使用者同時獲取到了這兩個值,如果有其中的任何一個使用者對這個值進行了修改,那麼另外一個使用者在對這個值進行操作的時候就會報錯。
如果要解決以上的問題可以使用gets
與cas
,測試程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# -- s1.py # _*_ coding:utf-8 _*_ import memcache conn1 = memcache.Client(['192.168.56.100:11211'], debug=True, cache_cas=True) conn1.set('n', 9) # gets會獲取到值並且獲取計數器 result = conn1.gets('n') print(result) # 阻塞 input('>>>') # 設定值 conn1.cas('n', 99) # -- s2 # _*_ coding:utf-8 _*_ import memcache conn2 = memcache.Client(['192.168.56.100:11211'], debug=True, cache_cas=True) # gets會獲取到值並且獲取計數器 result = conn2.gets('n') print(result) # 阻塞 input('>>>') # 設定值 conn2.cas('n', 100) |
多節點的操作
首先在伺服器上面起四個memcached
例項,每個例項都是一個單獨的memcached
服務
1 2 3 4 5 |
[root@anshengme ~]# netstat -tlnp | grep "memcached" tcp 0 0 0.0.0.0:11211 0.0.0.0:* LISTEN 1125/memcached tcp 0 0 0.0.0.0:11212 0.0.0.0:* LISTEN 1330/memcached tcp 0 0 0.0.0.0:11213 0.0.0.0:* LISTEN 1337/memcached tcp 0 0 0.0.0.0:11214 0.0.0.0:* LISTEN 1344/memcached |
1 2 3 4 5 6 7 8 9 10 11 |
# _*_ coding:utf-8 _*_ import memcache # 連線到多臺memcached伺服器 conn = memcache.Client( # IP:埠,權重 [('192.168.56.100:11211', 4), ('192.168.56.100:11212', 3), ('192.168.56.100:11213', 1), ('192.168.56.100:11214', 2)] ) conn.set('k', 'v') |
多節點資料儲存流程
- 先將一個字串轉換為一個數字
- 得出的結果和節點的數量進行除法運算
- 得出的結果肯定在節點數量之間,餘數是幾,就在那臺節點上面存放資料
如圖所示
1 2 3 4 5 6 7 8 |
# 將字串轉換為數字模組 import binascii # 摘自memcache原始碼中的一部分 def cmemcache_hash(key): return ((((binascii.crc32(key) & 0xffffffff) >> 16) & 0x7fff) or 1) # result就是返回的數字 result = cmemcache_hash(bytes('k', encoding='utf-8')) print(result) |
基於Memcached的Session例項
主程式指令碼
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 |
# _*_coding:utf-8 _*_ import tornado.ioloop import tornado.web import MemcacheToSession class BaseHandler(tornado.web.RequestHandler): def initialize(self): self.session = MemcacheToSession.Session(self) # pass class MainHandler(BaseHandler): def get(self): Info = self.session.GetAll() self.render("template/index.html", Data=Info) def post(self, *args, **kwargs): # 獲取傳過來的值 Key = self.get_argument('key') Val = self.get_argument('val') action = self.get_argument('action') if action == 'set': # 設定值 self.session[Key] = Val elif action == 'del': del self.session[Key] # 獲取所有資訊 Info = self.session.GetAll() # 返回給前端渲染 self.render("template/index.html", Data=Info) settings = { "tempalte_path": "template", "cookie_secret": "508CE6152CB93994628D3E99934B83CC", } application = tornado.web.Application([ (r'/', MainHandler), ], **settings) if __name__ == "__main__": application.listen(9999) tornado.ioloop.IOLoop.instance().start() |
模板檔案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!-- template\index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <form action="/" method="post"> set/del:<input type="text" name="action" value="set"/> Key: <input type="text" name="key"/> Val: <input type="text" name="val"/> <input type="submit" value="設定"/> </form> {{ Data }} </body> </html> |
設定Session的小模組
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 73 74 75 76 77 |
# _*_ coding: utf-8 _*_ import memcache import hashlib import uuid import json # 連線memcached conn = memcache.Client( ['192.168.56.100:11211'] ) class Session: CookieID = 'uc' ExpiresTime = 60 * 20 def __init__(self, handler): """ 用於建立使用者session在memcached中的字典 :param handler: 請求頭 """ self.handler = handler # 從客戶端獲取隨機字串 SessionID = self.handler.get_secure_cookie(Session.CookieID, None) # 客戶端存在並且在服務端也存在 if SessionID and conn.get(SessionID): self.SessionID = SessionID else: # 獲取隨機字串 self.SessionID = self.SessionKey() # 把隨機字串寫入memcached,時間是20分鐘 conn.set(self.SessionID, json.dumps({}), Session.ExpiresTime) # 每次訪問超時時間就加20分鐘 conn.set(self.SessionID, conn.get(self.SessionID), Session.ExpiresTime) # 設定Cookie self.handler.set_secure_cookie('uc', self.SessionID) def SessionKey(self): """ :return: 生成隨機字串 """ UUID = str(uuid.uuid1()).replace('-', '') MD5 = hashlib.md5() MD5.update(bytes(UUID, encoding='utf-8')) SessionKey = MD5.hexdigest() return SessionKey def __setitem__(self, key, value): """ 設定session :param key: session資訊中的key :param value: 對應的Value """ # 獲取session_id SessionDict = json.loads(conn.get(self.SessionID)) # 設定字典的key SessionDict[key] = value # 重新賦值 conn.set(self.SessionID, json.dumps(SessionDict), Session.ExpiresTime) def __getitem__(self, item): """ :param item: Session資訊中對應的Key :return: 獲取的Session資訊 """ # 獲取SessionID並轉換為字典 SessionDict = json.loads(conn.get(self.SessionID)) # 獲取對應的資料 ResultData = SessionDict.get(item, None) return ResultData def __delitem__(self, key): """ :param key: 要刪除的Key """ # 獲取SessionID並轉換為字典 SessionDict = json.loads(conn.get(self.SessionID)) # 刪除字典的key del SessionDict[key] # 重新賦值 conn.set(self.SessionID, json.dumps(SessionDict), Session.ExpiresTime) def GetAll(self): # 獲取Session中所有的資訊,僅用於測試 SessionData = conn.get(self.SessionID) return SessionData |
演示如下: