Python标准库系列之Memcache模块

本系列文章来源:<a>https://blog.ansheng.me/article/python-full-stack-way</a>
这个模块是一个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)
<ipython-input-15-2125c72626a0> in <module>()
----> 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.得出的结果肯定在节点数量之间,余数是几,就在那台节点上面存放数据

如图所示

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

模板文件

<!-- 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>



</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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容

  • 一、MemCache简介 session MemCache是一个自由、源码开放、高性能、分布式的分布式内存对象缓存...
    李伟铭MIng阅读 3,783评论 2 13
  • 1、memcache的概念? Memcache是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨...
    桖辶殇阅读 2,224评论 2 12
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 根据情景创造产品
    枫咻阅读 237评论 0 0
  • 新都阅读 360评论 1 1