自己的一个项目里用到Python和Java的通讯,不准备使用Jpype来调用Java类方法的方式来做,于是想到了消息队列。
经过对各种消息队列的比对最终选定了ActiveMQ。RabbitMQ、ZeroMQ、ActiveMQ介绍: http://blog.csdn.net/chszs/article/details/8479072
首先准备需要的环境:
ActiveMQ 5.10.0 Release、Python 2.7、stomp 0.2.9
python连接ActiveMQ的方式官方examples有stomp、amqp这两种方式的例子。stomp协议在python有很多实现模块,本文使用的是stompy。
stompy地址: https://pypi.python.org/pypi/stompy
stompy文档: https://pythonhosted.org/stompy
一、环境准备工作
解压ActiveMQ到任意目录,然后在命令行里执行{activemq目录}\bin\activemq.bat start(注意:不知道是不是我选用高版本的缘故,此处如果按网上其他教程直接activemq.bat不加start参数启动的话不能正常启动)
图:直接执行activemq.bat未加start参数,启动出错
图:执行activemq.bat start,启动成功
安装Python,请看Python安装教程 本文不赘述。
接下来编译安装Python的stomp模块:
将stomp解压到非中文路径下,切换命令行工作目录到解压后的stomp目录下,执行python setup.py install命令。注意:安装python模块可能需要C++编译器,作者机器上已经安装了VS2008。stomp模块的安装需要提前以安装setuptools工具。
图:stomp模块安装成功
二、代码
根据stomp官方文档的教程https://pythonhosted.org/stompy/reference/stompy.simple.html 编写代码之后,执行会有问题。
官方代码:
from stompy.simple import Client # 官方的例子使用的Client(),默认值hostname=localhost,port=61613.因为作者的ActiveMQ服务器与程序不在一台机器所以做了下面的修改。 stomp = Client('192.168.1.166', 61613) stomp.connect() stomp.put("The quick brown fox...", destination="/queue/test") stomp.subscribe("/queue/test") message = stomp.get_nowait() message.body 'The quick brown fox...' stomp.ack(message) stomp.unsubscribe("/queue/test") stomp.disconnect()执行这段程序出现了如下错误:
错误提示在执行stomp.get_nowait();方法时得到的是空回复。此时说明,stomp连接ActiveMQ服务器是成功的,并且stomp.put()方法成功将消息推送到了ActiveMQ服务器。但stomp向服务器订阅信息时发生错误。
根据stomp官方文档的说明,对stomp.get_nowait()方法的解释为:Remove and return an item from the queue without blocking. Only get an item if one is immediately available. Otherwise raise the Empty exception。(如果ActiveMQ服务器有消息存在,则获取消息,如果方法调用时ActiveMQ服务器没有可用消息就会抛出异常。)stomp.get_nowait()方法之前虽然执行了stomp.put()方法向ActiveMQ服务器发送了消息,但可能因为延迟的缘故导致stomp.get_nowait()方法执行时ActiveMQ服务器还没有可用消息导致错误发生。
解决办法:
方法一,在stomp.put()执行之后,stomp.get_nowait()执行之前添加代码time.sleep(0.1)让程序短暂休眠。
方法二,将stomp.get_nowait()方法改用stomp.get()方法。
注意:作者在执行代码过程中遇到了一个奇怪的问题,按以上的代码执行时可能会报:Unexpected ACK received for message-id 的错误。错误的原因请参考:http://blog.csdn.net/kimmking/article/details/10162777
解决这个问题的方法涉及到stomp.subscribe()方法的参数:Client.subscribe(destination, ack='auto', conf=None),将参数ack的默认值"auto"改为ack="client"即可。
三、总结
ActiveMQ支持多种连接方式,网上常说的ActiveMQ默认端口:61616是openwire方式的默认端口。本文使用的是stomp协议连接,对应的是ActiveMQ的61613端口。打开ActiveMQ的配置文件activemq.xml可找到如下的端口对应代码:
<!-- The transport connectors expose ActiveMQ over a given protocol to clients and other brokers. For more information, see: http://activemq.apache.org/configuring-transports.html --> <transportConnectors> <!-- DOS protection, limit concurrent connections to 1000 and frame size to 100MB --> <transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/> <transportConnector name="amqp" uri="amqp://0.0.0.0:5672?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/> <transportConnector name="stomp" uri="stomp://0.0.0.0:61613?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/> <transportConnector name="mqtt" uri="mqtt://0.0.0.0:1883?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/> <transportConnector name="ws" uri="ws://0.0.0.0:61614?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/> </transportConnectors>