chunjihong 发表于 2017-7-3 08:44:20

python16_day11【MQ、Redis、Memcache】

一、RabbitMQ
  是一个在AMQP基础上完整的,可复用的企业消息系统。他遵循Mozilla Public License开源协议。
  MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们。消 息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。排队指的是应用程序通过 队列来通信。队列的使用除去了接收和发送应用程序同时执行的要求。
  1.RabbitMQ install



安装配置epel源
    $ rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm

安装erlang
    $ yum -y install erlang

安装RabbitMQ
    $ yum -y install rabbitmq-server
  
  注意:service rabbitmq-server start/stop
  2. Python API install  



pip install pika
or
easy_install pika
or
源码
https://pypi.python.org/pypi/pika
  3.基于QUEUE实现生产消费模型



import Queue
import threading

message = Queue.Queue(10)

def producer(i):
   while True:
         message.put(i)

def consumer(i):
   while True:
         msg = message.get()

for i in range(12):
   t = threading.Thread(target=producer, args=(i,))
   t.start()

for i in range(10):
   t = threading.Thread(target=consumer, args=(i,))
   t.start()
  4.基于RabbitMQ
  对于RabbitMQ来说,生产和消费不再针对内存里的一个Queue对象,而是某台服务器上的RabbitMQ Server实现的消息队列。



import 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(" Sent 'Hello World!'")
connection.close()


# ########################## 消费者 ##########################
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(
         host='localhost'))
channel = connection.channel()

channel.queue_declare(queue='hello')

def callback(ch, method, properties, body):
   print(" Received %r" % body)

channel.basic_consume(callback,
                     queue='hello',
                     no_ack=True)

print('
[*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()
  5.消费者ack





import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost', port=5672))
channel = connection.channel()
channel.queue_declare(queue='hello')

def callback(ch, method, properties, body):
   print(" Received %r" % body)

channel.basic_consume(callback, queue='hello', no_ack=False)
# no_ack: acknowledgment 消息不丢失,MQ判读出现异常,没有消费,没有ack,则把消息放回队列.
channel.start_consuming()
消息ack  6.durable消息持久化





import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(
         host='127.0.0.1', port=5672))
channel = connection.channel()

channel.queue_declare(queue='hello1', durable=True)      # 创建通道, 持久化修改1:durable=True
channel.basic_publish(exchange='',
                     routing_key='hello',
                     body='Hello World!',
                     properties=pika.BasicProperties(delivery_mode=2)# 持久化修改2
                     )
connection.close()
生产者




import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost', port=5672))
channel = connection.channel()

def callback(ch, method, properties, body):
   print(" Received %r" % body)
   import time
   time.sleep(10)
   print('ok')
   ch.basic_ack(delivery_tag=method.delivery_tag)# 持久化:修改2
channel.basic_consume(callback,
                     queue='hello',
                     no_ack=False)         # 持久化:修改1
channel.start_consuming()
消费者    7.消息获取顺序





import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost', port=5672))
channel = connection.channel()

def callback(ch, method, properties, body):
   print(" Received %r" % body)
   import time
   time.sleep(10)
   print('ok')
   ch.basic_ack(delivery_tag=method.delivery_tag)
channel.basic_qos(prefetch_count=1)         # 默认消息队列里的数据是按照顺序被消费者拿走,
                                             # 例如:消费者1 去队列中获取 奇数 序列的任务,消费者1去队列中获取 偶数 序列的任务。
                                             # 表示谁来谁取,不再按照奇偶数排列
channel.basic_consume(callback,
                     queue='hello',
                     no_ack=False)

channel.start_consuming()
消费者  8.发布订阅
  exchange type = fanout





import pika
import sys

connection = pika.BlockingConnection(pika.ConnectionParameters(
         host='localhost'))
channel = connection.channel()

channel.exchange_declare(exchange='logs',
                        type='fanout')

message = ' '.join(sys.argv) or "info: Hello World!"
channel.basic_publish(exchange='logs',
                     routing_key='',
                     body=message)
print(" Sent %r" % message)
connection.close()
发布者




import pika

connection = 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.queue

channel.queue_bind(exchange='logs',
                  queue=queue_name)

print('
[*] Waiting for logs. To exit press CTRL+C')

def callback(ch, method, properties, body):
   print(" %r" % body)

channel.basic_consume(callback,
                     queue=queue_name,
                     no_ack=True)

channel.start_consuming()
订阅者  9.发布订阅(关键字)
  exchange type = direct





import pika
import sys

connection = pika.BlockingConnection(pika.ConnectionParameters(
         host='localhost'))
channel = connection.channel()

channel.exchange_declare(exchange='direct_logs',
                        type='direct')

severity = sys.argv if len(sys.argv) > 1 else 'info'
message = ' '.join(sys.argv) or 'Hello World!'
channel.basic_publish(exchange='direct_logs',
                     routing_key=severity,
                     body=message)
print(" Sent %r:%r" % (severity, message))
connection.close()
发布者




import pika
import sys

connection = 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.queue

severities = sys.argv
if not severities:
   sys.stderr.write("Usage: %s \n" % sys.argv)
   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(" %r:%r" % (method.routing_key, body))

channel.basic_consume(callback,
                     queue=queue_name,
                     no_ack=True)

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


[*]# 表示可以匹配 0 个 或 多个 单词
[*]*表示只能匹配 一个 单词



发送者路由值            队列中
old.boy.python          old.*-- 不匹配
old.boy.python          old.#-- 匹配




import pika
import sys

connection = pika.BlockingConnection(pika.ConnectionParameters(
         host='localhost'))
channel = connection.channel()

channel.exchange_declare(exchange='topic_logs',
                        type='topic')

routing_key = sys.argv if len(sys.argv) > 1 else 'anonymous.info'
message = ' '.join(sys.argv) or 'Hello World!'
channel.basic_publish(exchange='topic_logs',
                     routing_key=routing_key,
                     body=message)
print(" Sent %r:%r" % (routing_key, message))
connection.close()
发布者




import pika
import sys

connection = 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.queue

binding_keys = sys.argv
if not binding_keys:
   sys.stderr.write("Usage: %s ...\n" % sys.argv)
   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(" %r:%r" % (method.routing_key, body))

channel.basic_consume(callback,
                     queue=queue_name,
                     no_ack=True)

channel.start_consuming()
订阅者二、Memcached
  1.安装API
  pip3 install python-memcached
  2.基本使用



import memcache

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

  3.支持集群


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



mc = memcache.Client([('1.1.1.1:12000', 1), ('1.1.1.2:12000', 2), ('1.1.1.3:12000', 1)], debug=True)

mc.set('k1', 'v1')

  4.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重复添加,失败!!!

  5.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')

  6.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'})

  7.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'])

  8.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"])

  9.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"

  10.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

  11.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 memcache
mc = 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")

  Ps:本质上每次执行gets时,会从memcache中获取一个自增的数字,通过cas去修改gets的值时,会携带之前获取的自增值和memcache中的自增值进行比较,如果相等,则可以提交,如果不想等,那表示在gets和cas执行之间,又有其他人执行了gets(获取了缓冲的指定值), 如此一来有可能出现非正常数据,则不允许修改。
三、Redis
  1.安装API
  pip3 install redis
  2.功能介绍


[*]连接方式
[*]连接池
[*]操作

[*]String 操作
[*]Hash 操作
[*]List 操作
[*]Set 操作
[*]Sort Set 操作


[*]管道
[*]发布订阅
  3.基本操作



import redis

r = redis.Redis(host='10.211.55.4', port=6379)
r.set('foo', 'Bar')
print r.get('foo')

  4.连接池
  redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池。



import redis

pool = redis.ConnectionPool(host='10.211.55.4', port=6379)

r = redis.Redis(connection_pool=pool)
r.set('foo', 'Bar')
print r.get('foo')

  5.操作
  参考:http://www.cnblogs.com/wupeiqi/articles/5132791.html
页: [1]
查看完整版本: python16_day11【MQ、Redis、Memcache】