博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python 缓存
阅读量:4876 次
发布时间:2019-06-11

本文共 12747 字,大约阅读时间需要 42 分钟。

Memcached

Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。Memcached基于一个存储键/值对的。其(daemon )是用写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信。

Python操作Memcached

安装API

python操作Memcached使用Python-memcached模块

下载安装:https://pypi.python.org/pypi/python-memcached

连接操作

import memcache mc = memcache.Client(['10.211.55.4:12000'], debug=True)mc.set("foo", "bar")ret = mc.get('foo')print ret

支持集群

python-memcached模块原生支持集群操作,其原理是在内存维护一个主机列表,且集群中主机的权重值和主机在列表中重复出现的次数成正比

主机    权重    1.1.1.1   1    1.1.1.2   2    1.1.1.3   1 那么在内存中主机列表为:    host_list = ["1.1.1.1", "1.1.1.2", "1.1.1.2", "1.1.1.3", ]

如果用户根据如果要在内存中创建一个键值对(如:k1 = "v1"),那么要执行一下步骤:

  • 根据算法将 k1 转换成一个数字
  • 将数字和主机列表长度求余数,得到一个值 N( 0 <= N < 列表长度 )
  • 在主机列表中根据 第2步得到的值为索引获取主机,例如:host_list[N]
  • 连接 将第3步中获取的主机,将 k1 = "v1" 放置在该服务器的内存中

add

添加一条键值对,如果已经存在的 key,重复执行add操作异常

#!/usr/bin/env python# -*- coding:utf-8 -*-import memcache mc = memcache.Client(['10.211.55.4:12000'], debug=True)mc.add('k1', 'v1')# mc.add('k1', 'v2') # 报错,对已经存在的key重复添加,失败!!!

replace

replace 修改某个key的值,如果key不存在,则异常

#!/usr/bin/env python# -*- coding:utf-8 -*-import memcache mc = memcache.Client(['10.211.55.4:12000'], debug=True)# 如果memcache中存在kkkk,则替换成功,否则一场mc.replace('kkkk','999')

set set_multi

set            设置一个键值对,如果key不存在,则创建,如果key存在,则修改

set_multi   设置多个键值对,如果key不存在,则创建,如果key存在,则修改

#!/usr/bin/env python# -*- coding:utf-8 -*-import memcache mc = memcache.Client(['10.211.55.4:12000'], debug=True) mc.set('key0', 'wupeiqi') mc.set_multi({
'key1': 'val1', 'key2': 'val2'})

delete delete_multi

delete             在Memcached中删除指定的一个键值对

delete_multi    在Memcached中删除指定的多个键值对

#!/usr/bin/env python# -*- coding:utf-8 -*-import memcache mc = memcache.Client(['10.211.55.4:12000'], debug=True) mc.delete('key0')mc.delete_multi(['key1', 'key2'])

get get_multi

get            获取一个键值对

get_multi   获取多一个键值对

#!/usr/bin/env python# -*- coding:utf-8 -*-import memcache mc = memcache.Client(['10.211.55.4:12000'], debug=True) val = mc.get('key0')item_dict = mc.get_multi(["key1", "key2", "key3"])

append prepend

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

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

#!/usr/bin/env python# -*- coding:utf-8 -*-import memcache mc = memcache.Client(['10.211.55.4:12000'], debug=True)# k1 = "v1" mc.append('k1', 'after')# k1 = "v1after" mc.prepend('k1', 'before')# k1 = "beforev1after"

decr incr  

incr  自增,将Memcached中的某一个值增加 N ( N默认为1 )

decr 自减,将Memcached中的某一个值减少 N ( N默认为1 )

#!/usr/bin/env python# -*- coding:utf-8 -*-import memcache mc = memcache.Client(['10.211.55.4:12000'], debug=True)mc.set('k1', '777') mc.incr('k1')# k1 = 778 mc.incr('k1', 10)# k1 = 788 mc.decr('k1')# k1 = 787 mc.decr('k1', 10)# k1 = 777

gets  cas

如商城商品剩余个数,假设改值保存在memcache中,product_count = 900

A用户刷新页面从memcache中读取到product_count = 900
B用户刷新页面从memcache中读取到product_count = 900

如果A、B用户均购买商品

A用户修改商品剩余个数 product_count=899

B用户修改商品剩余个数 product_count=899

如此一来缓存内的数据便不在正确,两个用户购买商品后,商品剩余还是 899

如果使用python的set和get来操作以上过程,那么程序就会如上述所示情况!

如果想要避免此情况的发生,只要使用 gets 和 cas 即可,如:

#!/usr/bin/env python# -*- coding:utf-8 -*-import memcachemc = memcache.Client(['10.211.55.4:12000'], debug=True, cache_cas=True) v = mc.gets('product_count')# ...# 如果有人在gets之后和cas之前修改了product_count,那么,下面的设置将会执行失败,剖出异常,从而避免非正常数据的产生mc.cas('product_count', "899")

 

 

RabbitMQ

RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统。他遵循Mozilla Public License开源协议。

MQ全称为Message Queue, (MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消 息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。

安装API

pip install pika

对于RabbitMQ来说,生产和消费不再针对内存里的一个Queue对象,而是某台服务器上的RabbitMQ Server实现的消息队列。

#!/usr/bin/env pythonimport pika # ######################### 生产者 ######################### connection = pika.BlockingConnection(pika.ConnectionParameters(        host='localhost'))channel = connection.channel() channel.queue_declare(queue='hello') channel.basic_publish(exchange='',                      routing_key='hello',                      body='Hello World!')print(" [x] Sent 'Hello World!'")connection.close()
#!/usr/bin/env pythonimport pika # ########################## 消费者 ########################## connection = pika.BlockingConnection(pika.ConnectionParameters(        host='localhost'))channel = connection.channel() channel.queue_declare(queue='hello') def callback(ch, method, properties, body):    print(" [x] Received %r" % body) channel.basic_consume(callback,                      queue='hello',                      no_ack=True) print(' [*] Waiting for messages. To exit press CTRL+C')channel.start_consuming()

durable   消息不丢失

#生产者 #!/usr/bin/env pythonimport pikaconnection = pika.BlockingConnection(pika.ConnectionParameters(host='10.211.55.4'))channel = connection.channel()# make message persistentchannel.queue_declare(queue='hello', durable=True)channel.basic_publish(exchange='',                      routing_key='hello',                      body='Hello World!',                      properties=pika.BasicProperties(                          delivery_mode=2, # make message persistent                      ))print(" [x] Sent 'Hello World!'")connection.close()
# 消费者#!/usr/bin/env python# -*- coding:utf-8 -*-import pikaconnection = pika.BlockingConnection(pika.ConnectionParameters(host='10.211.55.4'))channel = connection.channel()# make message persistentchannel.queue_declare(queue='hello', durable=True)def callback(ch, method, properties, body):    print(" [x] Received %r" % body)    import time    time.sleep(10)    print 'ok'    ch.basic_ack(delivery_tag = method.delivery_tag)channel.basic_consume(callback,                      queue='hello',                      no_ack=False)print(' [*] Waiting for messages. To exit press CTRL+C')channel.start_consuming()

消息获取顺序

默认消息队列里的数据是按照顺序被消费者拿走,例如:消费者1 去队列中获取 奇数 序列的任务,消费者1去队列中获取 偶数 序列的任务。

#!/usr/bin/env python# -*- coding:utf-8 -*-import pikaconnection = pika.BlockingConnection(pika.ConnectionParameters(host='10.211.55.4'))channel = connection.channel()# make message persistentchannel.queue_declare(queue='hello')def callback(ch, method, properties, body):    print(" [x] Received %r" % body)    import time    time.sleep(10)    print 'ok'    ch.basic_ack(delivery_tag = method.delivery_tag)channel.basic_qos(prefetch_count=1)channel.basic_consume(callback,                      queue='hello',                      no_ack=False)print(' [*] Waiting for messages. To exit press CTRL+C')channel.start_consuming()

发布订阅

发布订阅和简单的消息队列区别在于,发布订阅会将消息发送给所有的订阅者,而消息队列中的数据被消费一次便消失。所以,RabbitMQ实现发布和订阅时,会为每一个订阅者创建一个队列,而发布者发布消息时,会将消息放置在所有相关队列中。

 exchange type = fanout

#生产者#!/usr/bin/env pythonimport pikaimport sysconnection = pika.BlockingConnection(pika.ConnectionParameters(        host='localhost'))channel = connection.channel()channel.exchange_declare(exchange='logs',                         type='fanout')message = ' '.join(sys.argv[1:]) or "info: Hello World!"channel.basic_publish(exchange='logs',                      routing_key='',                      body=message)print(" [x] Sent %r" % message)connection.close()
#消费者#!/usr/bin/env pythonimport pikaconnection = pika.BlockingConnection(pika.ConnectionParameters(        host='localhost'))channel = connection.channel()channel.exchange_declare(exchange='logs',                         type='fanout')result = channel.queue_declare(exclusive=True)queue_name = result.method.queuechannel.queue_bind(exchange='logs',                   queue=queue_name)print(' [*] Waiting for logs. To exit press CTRL+C')def callback(ch, method, properties, body):    print(" [x] %r" % body)channel.basic_consume(callback,                      queue=queue_name,                      no_ack=True)channel.start_consuming()

关键字发送

 exchange type = direct

之前事例,发送消息时明确指定某个队列并向其中发送消息,RabbitMQ还支持根据关键字发送,即:队列绑定关键字,发送者将数据根据关键字发送到消息exchange,exchange根据 关键字 判定应该将数据发送至指定队列。

#消费者#!/usr/bin/env pythonimport pikaimport sysconnection = pika.BlockingConnection(pika.ConnectionParameters(        host='localhost'))channel = connection.channel()channel.exchange_declare(exchange='direct_logs',                         type='direct')result = channel.queue_declare(exclusive=True)queue_name = result.method.queueseverities = sys.argv[1:]if not severities:    sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])    sys.exit(1)for severity in severities:    channel.queue_bind(exchange='direct_logs',                       queue=queue_name,                       routing_key=severity)print(' [*] Waiting for logs. To exit press CTRL+C')def callback(ch, method, properties, body):    print(" [x] %r:%r" % (method.routing_key, body))channel.basic_consume(callback,                      queue=queue_name,                      no_ack=True)channel.start_consuming()
#生产者#!/usr/bin/env pythonimport pikaimport sysconnection = pika.BlockingConnection(pika.ConnectionParameters(        host='localhost'))channel = connection.channel()channel.exchange_declare(exchange='direct_logs',                         type='direct')severity = sys.argv[1] if len(sys.argv) > 1 else 'info'message = ' '.join(sys.argv[2:]) or 'Hello World!'channel.basic_publish(exchange='direct_logs',                      routing_key=severity,                      body=message)print(" [x] Sent %r:%r" % (severity, message))connection.close()

模糊匹配

 exchange type = topic

在topic类型下,可以让队列绑定几个模糊的关键字,之后发送者将数据发送到exchange,exchange将传入”路由值“和 ”关键字“进行匹配,匹配成功,则将数据发送到指定队列。

  • # 表示可以匹配 0 个 或 多个 单词
  • *  表示只能匹配 一个 单词
发送者路由值              队列中old.boy.python          old.*  -- 不匹配old.boy.python          old.#  -- 匹配
#消费者#!/usr/bin/env pythonimport pikaimport sysconnection = pika.BlockingConnection(pika.ConnectionParameters(        host='localhost'))channel = connection.channel()channel.exchange_declare(exchange='topic_logs',                         type='topic')result = channel.queue_declare(exclusive=True)queue_name = result.method.queuebinding_keys = sys.argv[1:]if not binding_keys:    sys.stderr.write("Usage: %s [binding_key]...\n" % sys.argv[0])    sys.exit(1)for binding_key in binding_keys:    channel.queue_bind(exchange='topic_logs',                       queue=queue_name,                       routing_key=binding_key)print(' [*] Waiting for logs. To exit press CTRL+C')def callback(ch, method, properties, body):    print(" [x] %r:%r" % (method.routing_key, body))channel.basic_consume(callback,                      queue=queue_name,                      no_ack=True)channel.start_consuming()
#生产者#!/usr/bin/env pythonimport pikaimport sysconnection = pika.BlockingConnection(pika.ConnectionParameters(        host='localhost'))channel = connection.channel()channel.exchange_declare(exchange='topic_logs',                         type='topic')routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'message = ' '.join(sys.argv[2:]) or 'Hello World!'channel.basic_publish(exchange='topic_logs',                      routing_key=routing_key,                      body=message)print(" [x] Sent %r:%r" % (routing_key, message))connection.close()

RabbitMQ rpc模式

#server , 生产者#!/usr/bin/env python# -*- coding:utf-8 -*-import pikaimport uuidimport jsonconn = pika.BlockingConnection(pika.ConnectionParameters(host='10.37.129.5'))channel = conn.channel()def callback(ch, method, properties, body):    print(body)    ch.queue_delete(method.routing_key)def send_msg(hostname, cmd):    """    向队列中发送命令,并等待命令在客户端执行完成后获取结果    :param hostname:    :param cmd:    :return:    """    # 创建临时队列,用于存放客户端执行命令后的返回值    queue_name = str(uuid.uuid4())    channel.queue_declare(queue=queue_name)    # 向客户端队列中发送命令:封装了命令以及执行结果存放的队列名称    body = {
'uuid': queue_name, 'content': cmd} channel.basic_publish(exchange='', routing_key=hostname, body=json.dumps(body)) # 等待客户端想队列中发送执行结果,超时时间10s v = channel.consume(queue_name, inactivity_timeout=10) try: for method, properties, body in v: # 执行指定回调函数 callback(channel, method, properties, body) except TypeError as e: # 如果超时,则删除临时队列,不再获取数据 channel.queue_delete(queue_name)if __name__ == '__main__': while True: hostname = input('hostname( c1.com 或 c2.com ):') cmd = input('cmd:') if cmd == 'exit': break send_msg(hostname, cmd) conn.close()

 

#agent 消费者#!/usr/bin/env python# -*- coding:utf-8 -*-import pikaimport jsonimport subprocessconn = pika.BlockingConnection(pika.ConnectionParameters(host='10.37.129.5'))channel = conn.channel()channel.queue_declare(queue='c2.com')def callback(ch, method, properties, body):    body = json.loads(str(body, encoding='utf-8'))    result = subprocess.getoutput(body['content'])    result = 'c2.com:%s' % result    ch.basic_publish(exchange='', routing_key=body['uuid'], body=result)channel.basic_consume(callback, queue='c2.com', no_ack=True)channel.start_consuming()

 

程序练习:

基于把ssh换成rpc的连接方式

github:

转载于:https://www.cnblogs.com/wangyufu/p/6679117.html

你可能感兴趣的文章
马婕 2014年MBA,mpacc备考 报刊宣读1 中国的电子商务(转)
查看>>
微软挫败:OA等办公软件不再需要下载,其桌面办公软件无立足之地
查看>>
2019年上半年收集到的人工智能开源框架介绍文章
查看>>
ubuntu修改系统运行级别,去除图形化界面
查看>>
C++学习的方法以及四大名著(转)
查看>>
Life is short, you need Python【目录】
查看>>
开发环境、生产环境、测试环境的基本理解和区别
查看>>
LintCode: Count and Say
查看>>
cmake Qt
查看>>
从抖音关闭评论,看服务治理的重要性
查看>>
MIUI10系统怎么样刷成开发版获得ROOT权限
查看>>
前端内容思维导图
查看>>
Linux下装VirtualBox
查看>>
ORA-12514: TNS:listener does not currently know of service requested in connect descriptor
查看>>
linux下面安装配置LAMP环境
查看>>
libco之协程分析
查看>>
java人机猜拳游戏002user
查看>>
iOS UIButton的使用详解
查看>>
Spring Boot 2(一):Spring Boot 2.0新特性
查看>>
排序问题
查看>>