本节内容

  1. RabbitMQ——消息队列
  2. Memcached & Redis使用

1、RabbitMQ——消息队列  

RabbitMQ与Queue的关系

1、做的事情是一样的,两者都是队列。

2、Queue分两种,线程Q和进程Q,线程Q是用于同一进程下的多线程交互的,进程Q只能用于父进程与子进程之间的交互,或者同属于同一父进程下的多个子进程进行交互。

3、当遇到不同语言的进程交互或同是python下但不相关的进程之间交互时,python自带的Queue就不能使用了,此时需要一个中间代理,即RabbitMQ。

windows下两个独立的进程之间通讯方式:

1、走硬盘,json,pickle

2、走网卡,socket

3、走broker,比如RabbitMQ,每个软件连接RabbitMQ,也是使用的封装好的socket

RabbitMQ这类的消息队列broker,不仅可以为2个独立进程服务,也可以为任意的,任意数量的进程服务。

安装 http://www.rabbitmq.com/install-standalone-mac.html

安装python rabbitMQ module

1
2
3
4
5
6
7
pip install pika
or
easy_install pika
or
源码
  
https://pypi.python.org/pypi/pika

实现最简单的队列通信

send端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python
import pika
 
connection = pika.BlockingConnection(pika.ConnectionParameters(
               'localhost'))  #这里相当于建立一个最基本的socket
channel = connection.channel()  #声明一个管道
 
#声明queue  
channel.queue_declare(queue='hello')  #在管道中,再声明一个队列
 
#n RabbitMQ a message can never be sent directly to the queue, it always needs to go through an exchange.
channel.basic_publish(exchange='',
                      routing_key='hello',  #routing_key要被传入队列名称 
                      body='Hello World!')  #body是要发送的消息内容
print(" [x] Sent 'Hello World!'")
connection.close()  #关闭连接

receive端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#_*_coding:utf-8_*_
__author__ = 'Alex Li'
import pika
 
connection = pika.BlockingConnection(pika.ConnectionParameters(  
               'localhost'))  #建立连接
channel = connection.channel()  #建立管道
 
 
#You may ask why we declare the queue again ‒ we have already declared it in our previous code.
# We could avoid that if we were sure that the queue already exists. For example if send.py program
#was run before. But we're not yet sure which program to run first. In such cases it's a good
# practice to repeat declaring the queue in both programs.
channel.queue_declare(queue='hello')  #声明队列,声明从哪个队列里收消息
 
def callback(ch, method, properties, body):  #这是callback函数的标准格式。
    print(" [x] Received %r" % body)
 
channel.basic_consume(callback,    #如果收到消息,就调用callback函数来处理消息     
              queue='hello',  #这是要从哪个队列里接受消息。
              no_ack=True)  #这句的语法就是声明了要消费消息
 
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()  #这里是正式接收消费消息。启动后,一直运行,没有消息就阻塞。

channel.queue_declare(queue='hello')  #声明队列,声明从哪个队列里收消息

在我们确定‘hello’这个队列确实存在的情况下,这句语法可以不写,因为在生产者代码中已经声明过了。此处写的原因是,我们不确定是生产者还是消费者先运行。

def callback(ch, method, properties, body):  #这是callback函数的标准格式。

默认ch是管道的内存对象;method包含了把信息发给哪个queue,使用哪种分发模式;

channel.basic_consume(callback,queue='hello',no_ack=True),这里no_ack=no_acknowledgement,意思是客户端无论处理完了还是没处理完,都不用给服务端发确认消息。这个no_ack默认值为False。这种情况下,RabbitMQ会自动检测socket连接,只要正常处理消息的客户端宕机,socket连接也会断开。此时RMQ会把刚刚正在处理的消息发给下一个客户端,重新处理。这种机制可以保证消息一定被处理掉。
 

Work Queues

在这种模式下,RabbitMQ会默认把p发的消息依次分发给各个消费者(c),跟负载均衡差不多

消息提供者代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pika
import time
connection = pika.BlockingConnection(pika.ConnectionParameters(
    'localhost'))
channel = connection.channel()
 
# 声明queue
channel.queue_declare(queue='task_queue')
 
# n RabbitMQ a message can never be sent directly to the queue, it always needs to go through an exchange.
import sys
 
message = ' '.join(sys.argv[1:]) or "Hello World! %s" % time.time()
channel.basic_publish(exchange='',
                      routing_key='task_queue',
                      body=message,
                      properties=pika.BasicProperties(
                          delivery_mode=2,  # make message persistent
                      )
                      )
print(" [x] Sent %r" % message)
connection.close()

消费者代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#_*_coding:utf-8_*_
 
import pika, time
 
connection = pika.BlockingConnection(pika.ConnectionParameters(
    'localhost'))
channel = connection.channel()
 
 
def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
    time.sleep(20)
    print(" [x] Done")
    print("method.delivery_tag",method.delivery_tag)
    ch.basic_ack(delivery_tag=method.delivery_tag)
 
 
channel.basic_consume(callback,
                      queue='task_queue',
                      no_ack=True
                      )
 
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()

  此时,先启动消息生产者,然后再分别启动3个消费者,通过生产者多发送几条消息,就会发现,这几条消息会被依次分配到各个消费者身上

 

消息公平分发

如果Rabbit只管按顺序把消息发到各个消费者身上,不考虑消费者负载的话,很可能出现,一个机器配置不高的消费者那里堆积了很多消息处理不完,同时配置高的消费者却一直很轻松。为解决此问题,可以在各个消费者端,配置perfetch=1,意思就是告诉RabbitMQ在我这个消费者当前消息还没处理完的时候就不要再给我发新消息了。

1
channel.basic_qos(prefetch_count=1)

带消息持久化+公平分发的完整代码

生产者端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python
import pika
import sys
 
connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
channel = connection.channel()
 
channel.queue_declare(queue='task_queue', durable=True)
 
message = ' '.join(sys.argv[1:]) or "Hello World!"
channel.basic_publish(exchange='',
                      routing_key='task_queue',
                      body=message,
                      properties=pika.BasicProperties(
                         delivery_mode = 2# make message persistent
                      ))
print(" [x] Sent %r" % message)
connection.close()

消费者端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env python
import pika
import time
 
connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
channel = connection.channel()
 
channel.queue_declare(queue='task_queue', durable=True)
print(' [*] Waiting for messages. To exit press CTRL+C')
 
def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
    time.sleep(body.count(b'.'))
    print(" [x] Done")
    ch.basic_ack(delivery_tag = method.delivery_tag)
 
channel.basic_qos(prefetch_count=1)
channel.basic_consume(callback,
                      queue='task_queue')
 
channel.start_consuming()

  

Publish\Subscribe(消息发布\订阅) 

之前的例子都基本是1对1的消息发送和接收,即消息只能发送到指定的queue里,但有些时候你想让你的消息被所有的Queue收到,类似广播的效果,这时候就要用到exchange了,

Exchange在定义的时候是有类型的,以决定到底是哪些Queue符合条件,可以接收消息

fanout: 所有bind到此exchange的queue都可以接收消息
direct: 通过routingKey和exchange决定的那个唯一的queue可以接收消息
topic:所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息

   表达式符号说明:#代表一个或多个字符,*代表任何字符
      例:#.a会匹配a.a,aa.a,aaa.a等
          *.a会匹配a.a,b.a,c.a等
     注:使用RoutingKey为#,Exchange Type为topic的时候相当于使用fanout 

headers: 通过headers 来决定把消息发给哪些queue

消息publisher

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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[1:]) or "info: Hello World!"
channel.basic_publish(exchange='logs',
                      routing_key='',
                      body=message)
print(" [x] Sent %r" % message)
connection.close()

消息subscriber

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#_*_coding:utf-8_*_
__author__ = 'Alex Li'
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名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
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(" [x] %r" % body)
 
channel.basic_consume(callback,
                      queue=queue_name,
                      no_ack=True)
 
channel.start_consuming()

  

有选择的接收消息(exchange type=direct) 

RabbitMQ还支持根据关键字发送,即:队列绑定关键字,发送者将数据根据关键字发送到消息exchange,exchange根据 关键字 判定应该将数据发送至指定队列。

publisher

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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[1if 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()

subscriber 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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[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()

  

2、Memcached & Redis使用 

Memcached

http://www.cnblogs.com/wupeiqi/articles/5132791.html  

它相对简单,具体使用方法参见上述文档,这里不赘述了。

Redis

http://www.cnblogs.com/alex3714/articles/6217453.html  

RabbitMQ实现的是进程间的数据传递。
redis实现的是进程间的数据共享。
由于disk速度慢,我们把要共享的数据存在内存中,但不同进程间无法共享数据,所以我们又需要一个中间商broker。但同样,中间商与其他进程之间也不能直接互相访问内存。所以broker与其他的进程间用socket连接,大家对数据的处理都在broker内完成,就实现了数据的共享。
上文所说的broker学名叫做缓存。
 
现在主流的缓存系统:
  MongoDB  最早的,目前用的人逐渐变少,特点是直接持久化,即同时存在于disk and memery中
  redis  目前用的最多的缓存系统,默认存在内存,可以配置存在硬盘
  memcache  是轻量级的缓存,它只能存在于memery中
 

介绍

NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库。

redis是业界主流的key-value nosql 数据库之一。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis优点

  • 异常快速 : Redis是非常快的,每秒可以执行大约110000设置操作,81000个/每秒的读取操作。

  • 支持丰富的数据类型 : Redis支持最大多数开发人员已经知道如列表,集合,可排序集合,哈希等数据类型。

    这使得在应用中很容易解决的各种问题,因为我们知道哪些问题处理使用哪种数据类型更好解决。
  • 操作都是原子的 : 所有 Redis 的操作都是原子,从而确保当两个客户同时访问 Redis 服务器得到的是更新后的值(最新值)。

  • MultiUtility工具:Redis是一个多功能实用工具,可以在很多如:缓存,消息传递队列中使用(Redis原生支持发布/订阅),在应用程序中,如:Web应用程序会话,网站页面点击数等任何短暂的数据;
redis是单线程,使用IO多路复用中的epoll模式实现了高并发。
 

Redis API使用

redis-py 的API的使用可以分类为:

  • 连接方式
  • 连接池
  • 操作
    • String 操作
    • Hash 操作
    • List 操作
    • Set 操作
    • Sort Set 操作
  • 管道
  • 发布订阅

连接方式

1、操作模式

redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令,Redis是StrictRedis的子类,用于向后兼容旧版本的redis-py。

1
2
3
4
5
import redis
  
= redis.Redis(host='10.211.55.4', port=6379)
r.set('foo''Bar')
print r.get('foo')

2、连接池

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

 
3、操作、管道、订阅发布
  详见redis开头处的网址。
 

Python全栈开发-Day11-RabbitMQ/Redis的更多相关文章

  1. Python全栈开发:RabbitMQ/Redis/Memcache/SQLAlchemy

    Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度 ...

  2. python 全栈开发,Day101(redis操作,购物车,DRF解析器)

    昨日内容回顾 1. django请求生命周期? - 当用户在浏览器中输入url时,浏览器会生成请求头和请求体发给服务端 请求头和请求体中会包含浏览器的动作(action),这个动作通常为get或者po ...

  3. python全栈开发-Day11 迭代器、生成器、面向过程编程

    一. 迭代器 一 .迭代的概念 迭代器即迭代的工具,那什么是迭代呢? 迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值 while True: #只是单纯地重复,因而 ...

  4. Python 全栈开发【第0篇】:目录

    Python 全栈开发[第0篇]:目录   第一阶段:Python 开发入门 Python 全栈开发[第一篇]:计算机原理&Linux系统入门 Python 全栈开发[第二篇]:Python基 ...

  5. Win10构建Python全栈开发环境With WSL

    目录 Win10构建Python全栈开发环境With WSL 启动WSL 总结 对<Dev on Windows with WSL>的补充 Win10构建Python全栈开发环境With ...

  6. python全栈开发目录

    python全栈开发目录 Linux系列 python基础 前端~HTML~CSS~JavaScript~JQuery~Vue web框架们~Django~Flask~Tornado 数据库们~MyS ...

  7. Python全栈开发【面向对象进阶】

    Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...

  8. Python全栈开发【面向对象】

    Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...

  9. Python全栈开发【模块】

    Python全栈开发[模块] 本节内容: 模块介绍 time random os sys json & picle shelve XML hashlib ConfigParser loggin ...

  10. Python全栈开发【基础四】

    Python全栈开发[基础四] 本节内容: 匿名函数(lambda) 函数式编程(map,filter,reduce) 文件处理 迭代器 三元表达式 列表解析与生成器表达式 生成器 匿名函数 lamb ...

随机推荐

  1. Lyft高管的技术团队管理实战

    Lyft 的技术总监沈思维分享了他对于管理技术团队和打造工程文化的经验,也欢迎添加他的微信公众号"人家的屋顶"了解更多(微信公众号ID: othersroof).沈思维毕业于密歇根 ...

  2. 【RMAN】使用RMAN的 Compressed Backupsets备份压缩技术 (转载)

    1.Oracle参考文档中关于RMAN备份压缩的描述1)关于如何通过调整RMAN参数启用取消备份压缩功能http://download.oracle.com/docs/cd/B19306_01/bac ...

  3. Codeforces 903G Yet Another Maxflow Problem - 线段树

    题目传送门 传送门I 传送门II 传送门III 题目大意 给定一个网络.网络分为$A$,$B$两个部分,每边各有$n$个点.对于$A_{i} \ (1\leqslant i < n)$会向$A_ ...

  4. Codeforces Round #439 (Div. 2) Problem A (Codeforces 869A) - 暴力

    Rock... Paper! After Karen have found the deterministic winning (losing?) strategy for rock-paper-sc ...

  5. 值类型之间的相互转化,运算符,if条件判断,循环,函数

    值类型之间的相互转化 number | string | boolean 一.转换为boolean=>Boolean(a); var num = 10; var s = '123'; var b ...

  6. 使用SSH框架遇到的错误总结

    1.org.hibernate.exception.ConstraintViolationException: could not insert: 如果是主键是自增序列,映射文件 指定主键生成器< ...

  7. Windows常用的CMD命令

    mspaint 打开画图 write 打开写字板 explorer 打开文件资源管理器 notepad 打开记事本 devmgmt.msc 打开设备管理器 regedit 打开注册表编辑器 Mscon ...

  8. HDU 6191 Query on A Tree(可持久化Trie)

    题意 \(n\) 个点的有根树,根为 \(1\) .每个点有点权,有 \(q\) 个询问,每次询问以 \(u\) 为根的子树的点的点权中异或 \(x\) 所得的最大值是多少. 思路 求出整棵树的 \( ...

  9. Python实现机器学习算法:EM算法

    ''' 数据集:伪造数据集(两个高斯分布混合) 数据集长度:1000 ------------------------------ 运行结果: ---------------------------- ...

  10. Java创建对象的几种方式。

    Java创建对象的几种方式(重要): (1) 用new语句创建对象,这是最常见的创建对象的方法. (2) 运用反射手段,调用java.lang.Class或者java.lang.reflect.Con ...