本系列文章来源: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.得出的结果肯定在节点数量之间,余数是几,就在那台节点上面存放数据
如图所示
# 将字符串转换为数字模块
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()
模板文件
设置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实例