21Python標準庫系列之Memcache模塊

Python標準庫系列之Memcache模塊


這個模塊是一個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

apt-get install memcached

CentOS

yum install memcached

源碼安裝

Memcached源碼包安裝的時候需要依賴於libevent

# Ubuntu
apt-get install libevent-dev
# CentOS
yum install libevent-devel

編譯安裝memcached

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支持需要一些可選附加庫。

啓動

我這裏安裝的時候是採用包的方式進行安裝的。

[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文件

設置開機自啓動

[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

[root@anshengme ~]# pkill `cat /tmp/memcached.pid`

測試

先查看11211端口是否啓動

[root@anshengme ~]# netstat -tlnp | grep "11211"
tcp        0      0 0.0.0.0:11211           0.0.0.0:*               LISTEN      4245/memcached

使用telnet查看啓動的11211端口是否可以,可以則測試OK,否則就需要你拍錯了,但願沒有問題。

[root@anshengme ~]# telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.

如果出現以下內容就代表啓動成功!

Memcache使用

安裝Memcache

下載模塊包,目前最新版

[root@anshengme ~]# wget https://pypi.python.org/packages/f7/62/14b2448cfb04427366f24104c9da97cf8ea380d7258a3233f066a951a8d8/python-memcached-1.58.tar.gz#md5=23b258105013d14d899828d334e6b044

解壓並安裝

[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解釋器導入模塊,如果導入成功就表示模塊安裝成功。

[root@anshengme python-memcached-1.58]# python
>>> import memcache
>>>

首次體驗

# 導入memcache模塊
>>> import memcache
# 連接到一臺Memcached服務器
>>> conn = memcache.Client(['192.168.56.100:11211'])
# 設置一個值,如果存在則覆蓋
>>> conn.set('k1', 'v1')
True
# 獲取值的內容
>>> conn.get('k1')
'v1'

更多的使用方法

設置超時時間

>>> conn.set('k', 'v', 1)
True
>>> conn.get('k')

設置值,如果存在就報錯

>>> conn.add('k','hello')
# False設置失敗
False
>>> conn.get('k')
# 原值沒變
'v'

修改值,不存在則返回False

>>> conn.replace('k','helloworld')
# 設置成功
True
>>> conn.get('k')
# 返回修改後的值
'helloworld'
>>> conn.replace('kkkk','hello')
# 修改一個不存在的值
False

設置多個值,值不存在則創建,存在則修改

>>> conn.get('key1')
>>> conn.set_multi({'key1':'value1','key2':'value2'})
[]
>>> conn.get('key1')
'value1'

刪除一個值

>>> conn.get('key1')
'value1'
>>> conn.delete('key1')
1
>>> conn.get('key1')

刪除多個值

>>> conn.set_multi({'key3':'value3','key4':'value4'})
[]
>>> conn.delete_multi(['key3', 'key4'])
1

獲取一個值和獲取多個值

>>> conn.set_multi({'key5':'value5','key6':'value6'})
[]
>>> conn.get('key5')
'value5'
>>> conn.get_multi(['key5','key6'])
{'key5': 'value5', 'key6': 'value6'}

修改指定key的值,在該值後面追加內容

>>> conn.append('key5','after')
True
>>> conn.get('key5')
'value5after'

修改指定key的值,在該值 前面 插入內容

>>> conn.prepend('key5','before')
True
>>> conn.get('key5')
'beforevalue5after'

自增與自減,將Memcached中的某一個值加或減少N(N默認爲1)

>>> conn.set('number','9')
True
# 增加
>>> conn.incr('number')
10
# 增加
>>> conn.incr('number', 10)
20
# 減少
>>> conn.decr('number')
19
# 減少
>>> conn.decr('number', 10)
9

比如設置了這麼一個值:

conn.set('n','10)

現在A用戶和B用戶同時獲取到了這兩個值,如果有其中的任何一個用戶對這個值進行了修改,那麼另外一個用戶在對這個值進行操作的時候就會報錯。

如果要解決以上的問題可以使用getscas,測試代碼如下:

# -- 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)

執行效果如下圖:
wKiom1kZSHKQmL8XAADlmfXsy74466.gif

多節點的操作

首先在服務器上面起四個memcached實例,每個實例都是一個單獨的memcached服務

[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
# _*_ 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. 得出的結果肯定在節點數量之間,餘數是幾,就在那臺節點上面存放數據

如圖所示

wKioL1kZSMfiJvTSAADCeg7AgSU523.png

# 將字符串轉換爲數字模塊
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實例

主程序腳本

# _*_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()

模板文件

<!-- 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的小模塊

# _*_ 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

演示如下:
wKioL1kZSbKBJT2zAAIo_AfrO-U112.gif

#Python標準庫 #Memcache


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章