Python标准库系列之Memcache模块

本系列文章来源:https://blog.ansheng.me/article/python-full-stack-way
这个模块是一个Python操作memcached的一个API接口。

安装Memcached

CentOS

yum  install memcached

启动memcache:

[root@wangerxiao ~]# 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@wangerxiao ~]#   chmod +x /etc/rc.d/rc.local
[root@wangerxiao ~]#   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@wangerxiao ~]#   pkill `cat /tmp/memcached.pid`

测试
先查看11211端口是否启动

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

使用telnet查看启动的11211端口是否可以,可以则测试OK。
如果出现以下内容就代表启动成功!

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

Memcache使用

安装Memcache

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

解压并安装

[root@wangerxiao ~]# tar -zxvf python-memcached-1.58.tar.gz 
[root@wangerxiao ~]# cd  python-memcached-1.58/
[root@wangerxiao python-memcached-1.58]# python setup.py   install

进入Python解释器导入模块,如果导入成功就表示模块安装成功。

[root@wangerxiao ~]# ipython 
Python 3.5.2 (default, Aug 25 2016, 14:14:24) 
Type "copyright", "credits" or "license" for more information.

IPython 5.1.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import  memcache

体验一下

# 导入memcache模块
In [4]: import memcache 
# 连接到一台Memcached服务器
In [5]: conn = memcache.Client(['127.0.0.1:11211']) 
# 设置一个值,如果存在则覆盖
In [6]: conn.set('k1','v1') 
Out[6]: True
# 获取值的内容
In [7]: conn.get('k1') 
Out[7]: 'v1'

更多方法

设置超时时间

In [8]: conn.set('k','v',1) 
Out[8]: True
#时间超时,无法获取到值
In [9]: conn.get('k') 

设置值,如果存在就报错

In [10]: conn.add('k','hello') 
Out[10]: True

# False设置失败
In [11]: conn.add('k','world') 
Out[11]: False

# 原值没变
In [12]: conn.get('k') 
Out[12]: 'hello'

修改值,不存在则返回False

In [13]: conn.replace('k','helloworld') 
# 设置成功
Out[13]: True

In [14]: conn.get('k') 
# 返回修改后的值
Out[14]: 'helloworld'

In [15]: conn.replace('kk')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
 in ()
----> 1 conn.replace('kk')

TypeError: replace() missing 1 required positional argument: 'val'

# 修改一个不存在的值
In [16]: conn.replace('kk','hello')
Out[16]: False

设置多个值,值不存在则创建,存在则修改

In [17]: conn.get('key1') 

In [18]: conn.set_multi({'key1':'value1','key2':'value2'}) 
Out[18]: []

In [19]: conn.get('key1') 
Out[19]: 'value1'

删除一个值

In [20]: conn.get('key1') 
Out[20]: 'value1'

In [21]: conn.delete('key1') 
Out[21]: 1

In [22]: conn.get('key1')

删除多个值

In [23]: conn.set_multi({'key3':'value3','key4':'value4'}) 
Out[23]: []

In [24]: conn.delete_multi(['key3','key4']) 
Out[24]: 1

获取一个值和获取多个值

In [26]: conn.set_multi({'key5':'value5','key6':'value6'})
Out[26]: []

In [27]: conn.get('key5') 
Out[27]: 'value5'

In [28]: conn.get_multi(['key5','key6']) 
Out[28]: {'key5': 'value5', 'key6': 'value6'}

修改指定key的值,在该值后面追加内容

In [29]: conn.append('key5','after')
Out[29]: True

In [30]: conn.get('key5') 
Out[30]: 'value5after

修改指定key的值,在该值 前面 插入内容

In [32]: conn.prepend('key5','before') 
Out[32]: True

In [33]: conn.get('key5') 
Out[33]: 'beforevalue5after'

自增与自减,将Memcached中的某一个值加或减少N(N默认为1)

In [34]: conn.set('number',9) 
Out[34]: True

In [35]: conn.incr('number') 
Out[35]: 10

In [36]: conn.incr('number',10) 
Out[36]: 20

In [37]: conn.decr('number') 
Out[37]: 19

In [38]: conn.decr('number',10) 
Out[38]: 9

比如设置了这么一个值:

conn.set('n',10)

现在A用户和B用户同时获取到了这两个值,如果有其中的任何一个用户对这个值进行了修改,那么另外一个用户在对这个值进行操作的时候就会报错。

如果要解决以上的问题可以使用gets与cas,测试代码如下:

# -- 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服务

[root@wangerxiao ~]# netstat -tlnp | grep "memcache" 
tcp        0      0 0.0.0.0:11211           0.0.0.0:*               LISTEN      12423/memcached     
tcp        0      0 0.0.0.0:11212           0.0.0.0:*               LISTEN      17481/memcached     
tcp        0      0 0.0.0.0:11213           0.0.0.0:*               LISTEN      17577/memcached     
tcp        0      0 0.0.0.0:11214           0.0.0.0:*               LISTEN      17808/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.得出的结果肯定在节点数量之间,余数是几,就在那台节点上面存放数据

如图所示

Python标准库系列之Memcache模块_第1张图片
image.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()

模板文件





    



set/del: Key: Val:

设置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

演示链接:https://blog.ansheng.me/article/python-standard-library-memcache#基于memcached的session实例

你可能感兴趣的:(Python标准库系列之Memcache模块)