1,消息推送类

  1. import pika
  2.  
  3. # 同步消息推送类
  4. class RabbitPublisher(object):
  5.  
  6. # 传入RabbitMQ的ip,用户名,密码,实例化一个管道
  7. def __init__(self, host, user, password):
  8. self.host = host
  9. self.user = user
  10. self.password = password
  11. self.connection = pika.BlockingConnection(pika.ConnectionParameters(host=self.host, credentials=pika.PlainCredentials(self.user, self.password)))
  12. self.channel = self.connection.channel()
  13.  
  14. # 发送消息在队列中
  15. def send(self, queue_name, body):
  16. self.channel.queue_declare(queue=queue_name, durable=True) # 声明一个持久化队列
  17. self.channel.basic_publish(exchange='',
  18. routing_key=queue_name, # 队列名字
  19. body=body, # 消息内容
  20. properties=pika.BasicProperties(
  21. delivery_mode=2, # 消息持久化
  22. ))
  23.  
  24. # 清除指定队列的所有的消息
  25. def purge(self, queue_name):
  26. self.channel.queue_purge(queue_name)
  27.  
  28. # 删除指定队列
  29. def delete(self, queue_name, if_unused=False, if_empty=False):
  30. self.channel.queue_delete(queue_name, if_unused=if_unused, if_empty=if_empty)
  31.  
  32. # 断开连接
  33. def stop(self):
  34. self.connection.close()

2.消息消费类

(1)同步消息消费

在同步消息消费的时候可能会出现pika库断开的情况,原因是因为pika客户端没有及时发送心跳,连接就被server端断开了。解决方案就是做一个心跳线程来维护连接。

心跳线程类

  1. class Heartbeat(threading.Thread):
  2.  
  3. def __init__(self, connection):
  4. super(Heartbeat, self).__init__()
  5. self.lock = threading.Lock() # 线程锁
  6. self.connection = connection # rabbit连接
  7. self.quitflag = False # 退出标志
  8. self.stopflag = True # 暂停标志
  9. self.setDaemon(True) # 设置为守护线程,当消息处理完,自动清除
  10.  
  11. # 间隔10s发送心跳
  12. def run(self):
  13. while not self.quitflag:
  14. time.sleep(10) # 睡10s发一次心跳
  15. self.lock.acquire() # 加线程锁
  16. if self.stopflag:
  17. self.lock.release()
  18. continue
  19. try:
  20. self.connection.process_data_events() # 一直等待服务段发来的消息
  21. except Exception as e:
  22. print "Error format: %s" % (str(e))
  23. self.lock.release()
  24. return
  25. self.lock.release()
  26.  
  27. # 开启心跳保护
  28. def startheartbeat(self):
  29. self.lock.acquire()
  30. if self.quitflag:
  31. self.lock.release()
  32. return
  33. self.stopflag = False
  34. self.lock.release()

消息消费类

  1. # 同步消息消费类
  2. class RabbitConsumer(object):
  3.  
  4. # 传入RabbitMQ的ip,用户名,密码,实例化一个管道
  5. def __init__(self, host, user, password):
  6. self.host = host
  7. self.user = user
  8. self.password = password
  9. self.connection = pika.BlockingConnection(pika.ConnectionParameters(host=self.host, credentials=pika.PlainCredentials(self.user, self.password)))
  10. self.channel = self.connection.channel()
  11.  
  12. # 进行消费
  13. def receive(self, queue_name, callback_worker, prefetch_count=1): # callback_worker为消费的回调函数
  14. self.channel.queue_declare(queue=queue_name, durable=True)
  15. self.channel.basic_qos(prefetch_count=prefetch_count) # 设置预取的数量,如果为0则不预取,消费者处理越快,可以将这个这设置的越高
  16. self.channel.basic_consume(callback_worker, queue=queue_name) # callback_worker为消费的回调函数
  17. heartbeat = Heartbeat(self.connection) # 实例化一个心跳类
  18. heartbeat.start() # 开启一个心跳线程,不传target的值默认运行run函数
  19. heartbeat.startheartbeat() # 开启心跳保护
  20. self.channel.start_consuming() # 开始消费

调用方法

  1. # 消费回调函数
  2. def callback(ch, method, properties, body):
  3. print(" [x] Received %r" % body)
  4. ch.basic_ack(delivery_tag=method.delivery_tag) # 告诉生产者处理完成
  5.  
  6. consumer = RabbitConsumer(host="12.12.12.12", user="test", password="")
  7. consumer.receive(queue_name="queue1", callback_worker=callback)

(2)异步消息消费(推荐)

pika提供了支持异步发送模式的selectconnection方法支持异步发送接收(通过回调的方式)

在连接的时候stop_ioloop_on_close=False需要低版本的pika,比如0.13.1,安装方式 pip install pika==0.13.1

connectioon建立时回调建立channel, channel建立时一次回调各种declare方法,declare建立时依次回调publish。

同使用blockconnection方法相比,通过wireshark抓包来看,使用 异步的方式会对发包进行一些优化,会将几个包合并成一个大包,然后做一次ack应答从而提高效率,与之相反使用blockconnection时将会做至少两次ack,head一次content一次等

因此再试用异步的方式时会获得一定的优化

异步消息消费类

  1. # 异步消息消费类
  2. class RabbitConsumerAsync(object):
  3. EXCHANGE = 'amq.direct'
  4. EXCHANGE_TYPE = 'direct'
  5.  
  6. def __init__(self, host, user, password, queue_name="fish_test", callback_worker=None, prefetch_count=1):
  7. self.host = host
  8. self.user = user
  9. self.password = password
  10. self._connection = None
  11. self._channel = None
  12. self._closing = False
  13. self._consumer_tag = None
  14. self.QUEUE = queue_name
  15. self.callbackworker = callback_worker
  16. self.prefetch_count = prefetch_count
  17.  
  18. def connect(self):
  19. return pika.SelectConnection(pika.ConnectionParameters(host=self.host, credentials=pika.PlainCredentials(self.user, self.password)), self.on_connection_open,
  20. stop_ioloop_on_close=False)
  21.  
  22. def on_connection_open(self, unused_connection):
  23. self.add_on_connection_close_callback()
  24. self.open_channel()
  25.  
  26. def add_on_connection_close_callback(self):
  27. self._connection.add_on_close_callback(self.on_connection_closed)
  28.  
  29. def on_connection_closed(self, connection, reply_code, reply_text):
  30. self._channel = None
  31. if self._closing:
  32. self._connection.ioloop.stop()
  33. else:
  34. self._connection.add_timeout(5, self.reconnect)
  35.  
  36. def reconnect(self):
  37. self._connection.ioloop.stop()
  38. if not self._closing:
  39. self._connection = self.connect()
  40. self._connection.ioloop.start()
  41.  
  42. def open_channel(self):
  43. self._connection.channel(on_open_callback=self.on_channel_open)
  44.  
  45. def on_channel_open(self, channel):
  46. self._channel = channel
  47. self._channel.basic_qos(prefetch_count=self.prefetch_count)
  48. self.add_on_channel_close_callback()
  49. self.setup_exchange(self.EXCHANGE)
  50.  
  51. def add_on_channel_close_callback(self):
  52. self._channel.add_on_close_callback(self.on_channel_closed)
  53.  
  54. def on_channel_closed(self, channel, reply_code, reply_text):
  55. print reply_text
  56. self._connection.close()
  57.  
  58. def setup_exchange(self, exchange_name):
  59. self._channel.exchange_declare(self.on_exchange_declareok, exchange_name, self.EXCHANGE_TYPE, durable=True)
  60.  
  61. def on_exchange_declareok(self, unused_frame):
  62. self.setup_queue()
  63.  
  64. def setup_queue(self):
  65. self._channel.queue_declare(self.on_queue_declareok, self.QUEUE, durable=True)
  66.  
  67. def on_queue_declareok(self, method_frame):
  68. self._channel.queue_bind(self.on_bindok, self.QUEUE, self.EXCHANGE, self.QUEUE)
  69.  
  70. def on_bindok(self, unused_frame):
  71. self.start_consuming()
  72.  
  73. def start_consuming(self):
  74. self.add_on_cancel_callback()
  75. self._consumer_tag = self._channel.basic_consume(self.on_message, self.QUEUE)
  76.  
  77. def add_on_cancel_callback(self):
  78. self._channel.add_on_cancel_callback(self.on_consumer_cancelled)
  79.  
  80. def on_consumer_cancelled(self, method_frame):
  81. if self._channel:
  82. self._channel.close()
  83.  
  84. def on_message(self, unused_channel, basic_deliver, properties, body):
  85. self.callbackworker(body)
  86. self.acknowledge_message(basic_deliver.delivery_tag)
  87.  
  88. def acknowledge_message(self, delivery_tag):
  89. self._channel.basic_ack(delivery_tag)
  90.  
  91. def stop_consuming(self):
  92. if self._channel:
  93. self._channel.basic_cancel(self.on_cancelok, self._consumer_tag)
  94.  
  95. def on_cancelok(self, unused_frame):
  96. self.close_channel()
  97.  
  98. def close_channel(self):
  99. self._channel.close()
  100.  
  101. def run(self):
  102. self._connection = self.connect()
  103. self._connection.ioloop.start()
  104.  
  105. def stop(self):
  106. self._closing = True
  107. self.stop_consuming()
  108. self._connection.ioloop.start()
  109.  
  110. def close_connection(self):
  111. self._connection.close()

调用方法

  1. # 消费回调函数
  2. def callback(body):
  3. print(" [x] Received %r" % body)
  4.  
  5. consumer = RabbitConsumerAsync(host="12.12.12.12", user="test", password="", queue_name="fish_test", callback_worker=callback, prefetch_count=2)
  6. consumer.run()

(后面这两个可不加入)守护进程类(保证消费运行)

  1. class CDaemon(object):
  2. """
  3. a generic daemon class.
  4. usage: subclass the CDaemon class and override the run() method
  5. stderr 表示错误日志文件绝对路径, 收集启动过程中的错误日志
  6. verbose 表示将启动运行过程中的异常错误信息打印到终端,便于调试,建议非调试模式下关闭, 默认为1, 表示开启
  7. save_path 表示守护进程pid文件的绝对路径
  8. """
  9.  
  10. def __init__(self, save_path, stdin=os.devnull, stdout=os.devnull, stderr=os.devnull, home_dir='.', umask=022, verbose=1):
  11. self.stdin = stdin
  12. self.stdout = stdout
  13. self.stderr = stderr
  14. self.pidfile = save_path # pid文件绝对路径
  15. self.home_dir = home_dir
  16. self.verbose = verbose # 调试开关
  17. self.umask = umask
  18. self.daemon_alive = True
  19.  
  20. def daemonize(self):
  21. try:
  22. pid = os.fork()
  23. if pid > 0:
  24. sys.exit(0)
  25. except OSError, e:
  26. sys.stderr.write('fork #1 failed: %d (%s)\n' % (e.errno, e.strerror))
  27. sys.exit(1)
  28.  
  29. os.chdir(self.home_dir)
  30. os.setsid()
  31. os.umask(self.umask)
  32.  
  33. try:
  34. pid = os.fork()
  35. if pid > 0:
  36. sys.exit(0)
  37. except OSError, e:
  38. sys.stderr.write('fork #2 failed: %d (%s)\n' % (e.errno, e.strerror))
  39. sys.exit(1)
  40.  
  41. sys.stdout.flush()
  42. sys.stderr.flush()
  43.  
  44. si = file(self.stdin, 'r')
  45. so = file(self.stdout, 'a+')
  46. if self.stderr:
  47. se = file(self.stderr, 'a+', 0)
  48. else:
  49. se = so
  50.  
  51. os.dup2(si.fileno(), sys.stdin.fileno())
  52. os.dup2(so.fileno(), sys.stdout.fileno())
  53. os.dup2(se.fileno(), sys.stderr.fileno())
  54.  
  55. def sig_handler(signum, frame):
  56. self.daemon_alive = False
  57.  
  58. signal.signal(signal.SIGTERM, sig_handler)
  59. signal.signal(signal.SIGINT, sig_handler)
  60.  
  61. if self.verbose >= 1:
  62. print 'daemon process started ...'
  63.  
  64. atexit.register(self.del_pid)
  65. pid = str(os.getpid())
  66. file(self.pidfile, 'w+').write('%s\n' % pid)
  67.  
  68. def get_pid(self):
  69. try:
  70. pf = file(self.pidfile, 'r')
  71. pid = int(pf.read().strip())
  72. pf.close()
  73. except IOError:
  74. pid = None
  75. except SystemExit:
  76. pid = None
  77. return pid
  78.  
  79. def del_pid(self):
  80. if os.path.exists(self.pidfile):
  81. os.remove(self.pidfile)
  82.  
  83. def start(self, *args, **kwargs):
  84. if self.verbose >= 1:
  85. print 'ready to starting ......'
  86. # check for a pid file to see if the daemon already runs
  87. pid = self.get_pid()
  88. if pid:
  89. msg = 'pid file %s already exists, is it already running?\n'
  90. sys.stderr.write(msg % self.pidfile)
  91. sys.exit(0)
  92. # start the daemon
  93. self.daemonize()
  94. self.run(*args, **kwargs)
  95.  
  96. def stop(self):
  97. if self.verbose >= 1:
  98. print 'stopping ...'
  99. pid = self.get_pid()
  100. if not pid:
  101. msg = 'pid file [%s] does not exist. Not running?\n' % self.pidfile
  102. sys.stderr.write(msg)
  103. if os.path.exists(self.pidfile):
  104. os.remove(self.pidfile)
  105. return
  106. # try to kill the daemon process
  107. try:
  108. i = 0
  109. while 1:
  110. os.kill(pid, signal.SIGTERM)
  111. time.sleep(0.1)
  112. i = i + 1
  113. if i % 10 == 0:
  114. os.kill(pid, signal.SIGHUP)
  115. except OSError, err:
  116. err = str(err)
  117. if err.find('No such process') > 0:
  118. if os.path.exists(self.pidfile):
  119. os.remove(self.pidfile)
  120. else:
  121. print str(err)
  122. sys.exit(1)
  123. if self.verbose >= 1:
  124. print 'Stopped!'
  125.  
  126. def restart(self, *args, **kwargs):
  127. self.stop()
  128. self.start(*args, **kwargs)
  129.  
  130. def is_running(self):
  131. pid = self.get_pid()
  132. # print(pid)
  133. return pid and os.path.exists('/proc/%d' % pid)
  134.  
  135. def run(self, *args, **kwargs):
  136. # NOTE: override the method in subclass
  137. print 'base class run()'

调用

  1. class RabbitDaemon(CDaemon):
  2. def __init__(self, name, save_path, stdin=os.devnull, stdout=os.devnull, stderr=os.devnull, home_dir='.', umask=022, verbose=1):
  3. CDaemon.__init__(self, save_path, stdin, stdout, stderr, home_dir, umask, verbose)
  4. self.name = name # 派生守护进程类的名称
  5.  
  6. def run(self, **kwargs):
  7. # 新建一个队列链接
  8. rab_con = RabbitConsumerAysnc(queuename="test", callbackworker=liando_sf_consumer, prefetch_count=2)
  9. # 开启消费者进程
  10. rab_con.run()
  11.  
  12. p_name = 'test' # 守护进程名称
  13. pid_fn = '/www/rabbit/test.pid' # 守护进程pid文件的绝对路径
  14. err_fn = '/www/rabbit/test_err.log' # 守护进程启动过程中的错误日志,内部出错能从这里看到
  15. cD = RabbitDaemon(p_name, pid_fn, stderr=err_fn, verbose=1)

参考链接

https://pika.readthedocs.io/en/0.10.0/examples.html

https://www.jianshu.com/p/a4671c59351a

源码下载地址:https://github.com/sy159/RabbiyMQ.git

python实现RabbitMQ同步跟异步消费模型的更多相关文章

  1. python下载mp4 同步和异步下载支持断点续下

    Range 用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格式: Range:(unit=first byte pos)-[last byte pos] Range 头部的格式有以下几种 ...

  2. python操作rabbitmq,实现生产消费者模型

    更多详情参考官方文档:https://www.rabbitmq.com/tutorials/tutorial-six-python.html 参考博客:https://blog.csdn.net/we ...

  3. Python番外之 阻塞非阻塞,同步与异步,i/o模型

    1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步/异步主要针对C端: 同步:      所谓同步,就 ...

  4. Python之协程、异步IO、redis缓存、rabbitMQ队列

    本节内容 Gevent协程 Select\Poll\Epoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitMQ队列 Redis\Memcached缓存 Paramiko SS ...

  5. ActiveMQ( 一) 同步,异步,阻塞 JMS 消息模型

    同步请求:浏览器 向服务器 发送一个登录请求,如果服务器 没有及时响应,则浏览器则会一直等待状态,直至服务器响应或者超时. 异步请求:浏览器 向服务器 发送一个登录请求,不管服务器是否立即响应,浏览器 ...

  6. python 全栈开发,Day44(IO模型介绍,阻塞IO,非阻塞IO,多路复用IO,异步IO,IO模型比较分析,selectors模块,垃圾回收机制)

    昨日内容回顾 协程实际上是一个线程,执行了多个任务,遇到IO就切换 切换,可以使用yield,greenlet 遇到IO gevent: 检测到IO,能够使用greenlet实现自动切换,规避了IO阻 ...

  7. python 全栈开发,Day43(引子,协程介绍,Greenlet模块,Gevent模块,Gevent之同步与异步)

    昨日内容回顾 I/O模型,面试会问到I/O操作,不占用CPU.它内部有一个专门的处理I/O模块.print和写log 属于I/O操作,它不占用CPU 线程GIL保证一个进程中的多个线程在同一时刻只有一 ...

  8. python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)

    python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程 并行与并发 同步与异步 阻塞与非阻塞 CPU密集型与IO密集型 线程与进程 进 ...

  9. python全栈开发,Day43(引子,协程介绍,Greenlet模块,Gevent模块,Gevent之同步与异步)

    昨日内容回顾 I/O模型,面试会问道 I/O操作,不占用CPU,它内部有一个专门的处理I/O模块 print和写log属于I/O操作,它不占用CPU 线程 GIL保证一个进程中的多个线程在同一时刻只有 ...

随机推荐

  1. vijos 1002 简单压缩+DP

    描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数轴上 ...

  2. 归并排序Merge sort2

    原理,把原始数组分成若干子数组,对每一个子数组进行排序, 继续把子数组与子数组合并,合并后仍然有序,直到全部合并完,形成有序的数组 举例 无序数组[6 2 4 1 5 9] 先看一下每个步骤下的状态, ...

  3. 【洛谷 P5110】 块速递推(矩阵加速,分块打表)

    题目链接 掌握了分块打表法了.原来以前一直想错了... 块的大小\(size=\sqrt n\),每隔\(size\)个数打一个表,还要在\(0\text{~}size-1\)每个数打一个表. 然后就 ...

  4. java 深度拷贝 复制 深度复制

    1.深度拷贝.复制代码实现 最近需要用到比较两个对象属性的变化,其中一个是oldObj,另外一个是newObj,oldObj是newObj的前一个状态,所以需要在newObj的某个状态时,复制一个一样 ...

  5. 函数getopt()及其参数optind -- (转)

    getopt被用来解析命令行选项参数 #include <unistd.h>       extern char *optarg;  //选项的参数指针       extern int ...

  6. io多路复用-select()

    参照<Unix网络编程>相关章节内容,实现了一个简单的单线程IO多路复用服务器与客户端. 普通迭代服务器,由于执行recvfrom则会发生阻塞,直到客户端发送数据并正确接收后才能够返回,一 ...

  7. write-ups

    https://github.com/MarioVilas/write-ups https://github.com/Deplorable-Mountaineer/Robot_Dynamite htt ...

  8. sicily 1172. Queens, Knights and Pawns

    Description You all are familiar with the famous 8-queens problem which asks you to place 8 queens o ...

  9. 运输层和TCP/IP协议

    0. 基本要点 运输层是为相互通信的应用进程提供逻辑通信. 端口和套接字的意义 什么是无连接UDP 什么是面向连接的TCP 在不可靠的网络上实现可靠传输的工作原理,停止等待协议和ARQ协议 TCP的滑 ...

  10. jquery - 实例1

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="text2.aspx.cs& ...