消息队列学习
来源:互联网 发布:百电通电话软件 编辑:程序博客网 时间:2024/06/06 06:48
在做微信开发的时候,要求服务器响应消息的时间在5秒内回复,如里没有达到5秒时间要求,会连续重发三次,都没有回复,将显示·「该公众号目前无法提供服务」。
在一些耗时的操作无法满足上面的条件。本来是不打算管的,结果连续报警两天,累觉不爱了。
解决这个问题就用到了消息队列机制,个人理解消息队列:就是把目前耗时的操作入队列存起来,先给用户回复,然后再从队列里读出耗时操作慢慢执行。
使用的是RabbitMQ消息队列,其官方地址:http://www.rabbitmq.com/tutorials/tutorial-three-python.html
下面是步骤 :
一、安装 rabbitmq服务
sudo apt-get install rabbitmq-server
二、用的是python 的pika
sudo apt-get install python-pip git-core python-pik
三、参考以下代码
1、方式一
Work Queues
先回复,把耗时工作轮流的分发给C1、C2。
发送端:
new_task.py
#!/usr/bin/env pythonimport pikaimport sysconnection = 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()
运行多个Cn。
接受端
worker.py
#!/usr/bin/env pythonimport pikaimport timeconnection = 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('.') ) 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()
结果 :
shell1$ python worker.py [*] Waiting for messages. To exit press CTRL+C
shell2$ python worker.py [*] Waiting for messages. To exit press CTRL+C
shell3$ python new_task.py First message.shell3$ python new_task.py Second message..shell3$ python new_task.py Third message...shell3$ python new_task.py Fourth message....shell3$ python new_task.py Fifth message.....
shell1$ python worker.py [*] Waiting for messages. To exit press CTRL+C [x] Received 'First message.' [x] Received 'Third message...' [x] Received 'Fifth message.....'
shell2$ python worker.py [*] Waiting for messages. To exit press CTRL+C [x] Received 'Second message..' [x] Received 'Fourth message....'
2、方式二
Publish/Subscribe
P广播消息给每一个C。
在这种方式我们创建了一个Exchanges,其类型是
type='fanout'
发送端代码
emit_log.py
#!/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()接收端代码
receive_logs.py
#!/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()
3、方式三
Routing
这种方式的Exchange的类型为direct。可以根据
routing_key=severity设定的值来过滤消息。比如C1只接受error类的消息。
发送端
emit_log_direct.py
#!/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()
接受端:
receive_logs_direct.py
#!/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: print >> sys.stderr, "Usage: %s [info] [warning] [error]" % \ (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()
4、方式四
Topics
这种方式的Exchange的类型为topics。可以根据
routing_key=severity 多个条件进行过滤。这里的routing_key形式是以.分隔连接的关键字字符串。比如:stock.usd.nyse。最长不能超过255字节。在过滤时, 可以类试正则表达式的匹配。
- * (star) can substitute for exactly one word.(代表一个关键字)
- # (hash) can substitute for zero or more words.(可表示0个,多个关键字)
emit_log_topic.py
#!/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()
接收端
receive_logs_topic.py
#!/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: print >> sys.stderr, "Usage: %s [binding_key]..." % (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()运行结果
python receive_logs_topic.py "#"
python receive_logs_topic.py "kern.*"
python receive_logs_topic.py "kern.*" "*.critical"
python emit_log_topic.py "kern.critical" "A critical kernel error"
5、方式五
Remote procedure call (RPC)
远程过程调用,下面代码是关于在远程服务器上计算斐波那契数并返回。
客户端:
rpc_client.py
#!/usr/bin/env pythonimport pikaimport uuidclass FibonacciRpcClient(object): def __init__(self): self.connection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost')) self.channel = self.connection.channel() result = self.channel.queue_declare(exclusive=True) self.callback_queue = result.method.queue self.channel.basic_consume(self.on_response, no_ack=True, queue=self.callback_queue) def on_response(self, ch, method, props, body): if self.corr_id == props.correlation_id: self.response = body def call(self, n): self.response = None self.corr_id = str(uuid.uuid4()) self.channel.basic_publish(exchange='', routing_key='rpc_queue', properties=pika.BasicProperties( reply_to = self.callback_queue, correlation_id = self.corr_id, ), body=str(n)) while self.response is None: self.connection.process_data_events() return int(self.response)fibonacci_rpc = FibonacciRpcClient()print " [x] Requesting fib(30)"response = fibonacci_rpc.call(30)print " [.] Got %r" % (response,)
服务端
rpc_server.py
#!/usr/bin/env pythonimport pikaconnection = pika.BlockingConnection(pika.ConnectionParameters( host='localhost'))channel = connection.channel()channel.queue_declare(queue='rpc_queue')def fib(n): if n == 0: return 0 elif n == 1: return 1 else: return fib(n-1) + fib(n-2)def on_request(ch, method, props, body): n = int(body) print " [.] fib(%s)" % (n,) response = fib(n) ch.basic_publish(exchange='', routing_key=props.reply_to, properties=pika.BasicProperties(correlation_id = \ props.correlation_id), body=str(response)) ch.basic_ack(delivery_tag = method.delivery_tag)channel.basic_qos(prefetch_count=1)channel.basic_consume(on_request, queue='rpc_queue')print " [x] Awaiting RPC requests"channel.start_consuming()
四、总结
RabbitMQ队列主的有三种方式
1、消息队列:即第一种工作方式,先返回消息后,慢慢处理耗时操作。轮流分发给多个worker
2、广播消息给多个worker。根据Exchange的类型(direct, topic, headers, fanout)及routing_key可以过滤消息。
3、远程过程调用
- 消息队列学习日记
- 消息队列(MSMQ)学习
- 消息队列-Kafka学习
- 消息队列学习
- 消息队列 Kafka学习
- java 消息队列学习
- ActiveMQ消息队列学习
- Linux学习(消息队列)
- 消息队列学习笔记
- 学习消息队列(一)
- 消息队列学习记录
- unix学习笔记------消息队列---发送消息
- .Net消息队列学习(一)
- Net消息队列学习(二)
- .Net消息队列学习(一)
- 学习笔记之消息队列
- Handler和消息队列学习
- 消息队列 rabbitmq 学习 记录
- Git文件操作
- hdu2255 二分图的最佳匹配 KM算法
- 也谈回调
- 解决不能连接远程postgresql
- 21. Dubbo原理解析-通信层之请求响应活动图
- 消息队列学习
- error c2129:静态函数已声明但未定义
- stagefright框架解读(—)音视频Playback流程
- 【瞎搞】 FZU 2147 A-B Game
- 基于网站的用户行为分析
- linux-tar打包|解压缩 命令
- 【原创】《Linux高级程序设计》杨宗德著 - Linux Socket网络编程基础 - 网络通信基础
- vs2010配置opencv2.4.9
- S2SH框架搭建常见的问题