├── .gitignore
├── LICENSE
├── MQMessage.py
├── MQMessageListener.py
├── MQProducer.py
├── MQPullConsumer.py
├── MQPushConsumer.py
├── README.md
├── __init__.py
├── jpypetest.py
├── settings_MQ.py
└── test
├── Producer.py
├── PullConsumer.py
└── PushConsumer.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 |
27 | # PyInstaller
28 | # Usually these files are written by a python script from a template
29 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
30 | *.manifest
31 | *.spec
32 |
33 | # Installer logs
34 | pip-log.txt
35 | pip-delete-this-directory.txt
36 |
37 | # Unit test / coverage reports
38 | htmlcov/
39 | .tox/
40 | .coverage
41 | .coverage.*
42 | .cache
43 | nosetests.xml
44 | coverage.xml
45 | *,cover
46 | .hypothesis/
47 |
48 | # Translations
49 | *.mo
50 | *.pot
51 |
52 | # Django stuff:
53 | *.log
54 |
55 | # Sphinx documentation
56 | docs/_build/
57 |
58 | # PyBuilder
59 | target/
60 |
61 | #Ipython Notebook
62 | .ipynb_checkpoints
63 |
64 | #idea
65 | .idea/
66 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 GangLuICT
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MQMessage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding:utf-8 -*-
3 |
4 | from jpype import *
5 | import logging
6 | import settings_MQ as settings
7 |
8 | logger = logging.getLogger("MQMessage")
9 |
10 | __all__ = ["MQMessage", "SendStatus", "PullStatus", "ConsumeFromWhere", "ConsumeConcurrentlyStatus", "ConsumeOrderlyStatus",
11 | "MessageModel"]
12 |
13 | Message = JPackage('com.alibaba.rocketmq.common.message').Message
14 | # enum classes:
15 | SENDSTATUS = JPackage('com.alibaba.rocketmq.client.producer').SendStatus
16 | PULLSTATUS = JPackage('com.alibaba.rocketmq.client.consumer').PullStatus
17 | CONSUMEFROMWHERE = JPackage('com.alibaba.rocketmq.common.consumer').ConsumeFromWhere
18 | CONSUMECONCURRENTLYSTATUS = JPackage('com.alibaba.rocketmq.client.consumer.listener').ConsumeConcurrentlyStatus
19 | CONSUMEORDERLYSTATUS = JPackage('com.alibaba.rocketmq.client.consumer.listener').ConsumeOrderlyStatus
20 | MESSAGEMODEL = JPackage('com.alibaba.rocketmq.common.protocol.heartbeat').MessageModel
21 |
22 |
23 | class MQMessage(object):
24 | def __init__(self, topic, tags, keys, body):
25 | self.topic = topic
26 | self.tags = tags
27 | self.keys = keys
28 | self.body = body
29 | self.msg = Message(JString(self.topic), JString(self.tags), JString(self.keys), self.body.encode(encoding = settings.MsgBodyEncoding))
30 | # JAVA prototype of Message
31 | # public Message()
32 | # public Message(String topic, byte[] body)
33 | # public Message(String topic, String tags, String keys, byte[] body)
34 | # public Message(String topic, String tags, String keys, int flag, byte[] body, boolean waitStoreMsgOK)
35 |
36 | def tostr(self):
37 | """
38 | Translate the object into a string
39 | """
40 | return self.topic + "::" + self.tags + "::" + self.keys + "::" + self.body
41 |
42 |
43 | # PullResult的返回结果
44 | PullStatus = {
45 | #'FOUND': 0, # Founded
46 | 'FOUND': PULLSTATUS.FOUND,
47 | #'NO_NEW_MSG': 1, # No new message can be pull
48 | 'NO_NEW_MSG': PULLSTATUS.NO_NEW_MSG,
49 | #'NO_MATCHED_MSG': 2, # Filtering results can not match
50 | 'NO_MATCHED_MSG': PULLSTATUS.NO_MATCHED_MSG,
51 | #'OFFSET_ILLEGAL': 3 # Illegal offset,may be too big or too small
52 | 'OFFSET_ILLEGAL': PULLSTATUS.OFFSET_ILLEGAL
53 | }
54 |
55 | # SendResult的返回结果
56 | SendStatus = {
57 | #'SEND_OK': 0,
58 | 'SEND_OK': SENDSTATUS.SEND_OK,
59 | #'FLUSH_DISK_TIMEOUT': 1,
60 | 'FLUSH_DISK_TIMEOUT': SENDSTATUS.FLUSH_DISK_TIMEOUT,
61 | #'FLUSH_SLAVE_TIMEOUT': 2,
62 | 'FLUSH_SLAVE_TIMEOUT': SENDSTATUS.FLUSH_SLAVE_TIMEOUT,
63 | #'SLAVE_NOT_AVAILABLE': 3
64 | 'SLAVE_NOT_AVAILABLE': SENDSTATUS.SLAVE_NOT_AVAILABLE
65 | }
66 |
67 | # PushConsumer消费时选择第一次订阅时的消费位置
68 | ConsumeFromWhere = {
69 | # 一个新的订阅组第一次启动从队列的最后位置开始消费
70 | # 后续再启动接着上次消费的进度开始消费
71 | #'CONSUME_FROM_LAST_OFFSET': 0,
72 | 'CONSUME_FROM_LAST_OFFSET': CONSUMEFROMWHERE.CONSUME_FROM_LAST_OFFSET,
73 | #@Deprecated
74 | #'CONSUME_FROM_LAST_OFFSET_AND_FROM_MIN_WHEN_BOOT_FIRST': 1,
75 | 'CONSUME_FROM_LAST_OFFSET_AND_FROM_MIN_WHEN_BOOT_FIRST': CONSUMEFROMWHERE.CONSUME_FROM_LAST_OFFSET_AND_FROM_MIN_WHEN_BOOT_FIRST,
76 | #@Deprecated
77 | #'CONSUME_FROM_MIN_OFFSET': 2,
78 | 'CONSUME_FROM_MIN_OFFSET': CONSUMEFROMWHERE.CONSUME_FROM_MIN_OFFSET,
79 | #@Deprecated
80 | #'CONSUME_FROM_MAX_OFFSET': 3,
81 | 'CONSUME_FROM_MAX_OFFSET': CONSUMEFROMWHERE.CONSUME_FROM_MAX_OFFSET,
82 | # 一个新的订阅组第一次启动从队列的最前位置开始消费
83 | # 后续再启动接着上次消费的进度开始消费
84 | #'CONSUME_FROM_FIRST_OFFSET': 4,
85 | 'CONSUME_FROM_FIRST_OFFSET': CONSUMEFROMWHERE.CONSUME_FROM_FIRST_OFFSET,
86 | # 一个新的订阅组第一次启动从指定时间点开始消费,时间点设置参见DefaultMQPushConsumer.consumeTimestamp参数
87 | # 后续再启动接着上次消费的进度开始消费
88 | #'CONSUME_FROM_TIMESTAMP': 5,
89 | 'CONSUME_FROM_TIMESTAMP': CONSUMEFROMWHERE.CONSUME_FROM_TIMESTAMP,
90 | }
91 |
92 | # PushConsumer消费后的返回值(并发消费时)
93 | ConsumeConcurrentlyStatus = {
94 | #'CONSUME_SUCCESS': 0, # Success consumption
95 | 'CONSUME_SUCCESS': CONSUMECONCURRENTLYSTATUS.CONSUME_SUCCESS,
96 | #'RECONSUME_LATER': 1, # Failure consumption,later try to consume
97 | 'RECONSUME_LATER': CONSUMECONCURRENTLYSTATUS.RECONSUME_LATER,
98 | }
99 |
100 | # PushConsumer消费后的返回值(顺序消费时)
101 | ConsumeOrderlyStatus ={
102 | #'SUCCESS': 0, # Success consumption
103 | 'SUCCESS': CONSUMEORDERLYSTATUS.SUCCESS,
104 | #'ROLLBACK': 1, # Rollback consumption(only for binlog consumption)
105 | 'ROLLBACK': CONSUMEORDERLYSTATUS.ROLLBACK,
106 | #'COMMIT': 2, # Commit offset(only for binlog consumption)
107 | 'COMMIT': CONSUMEORDERLYSTATUS.COMMIT,
108 | #'SUSPEND_CURRENT_QUEUE_A_MOMENT': 3 # Suspend current queue a moment
109 | 'SUSPEND_CURRENT_QUEUE_A_MOMENT': CONSUMEORDERLYSTATUS.SUSPEND_CURRENT_QUEUE_A_MOMENT
110 | }
111 |
112 | # PushConsumer的消息model
113 | MessageModel = {
114 | #'BROADCASTING': 0, # broadcast
115 | 'BROADCASTING': MESSAGEMODEL.BROADCASTING,
116 | #'CLUSTERING': 1 # clustering
117 | 'CLUSTERING': MESSAGEMODEL.CLUSTERING
118 | }
119 |
--------------------------------------------------------------------------------
/MQMessageListener.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding:utf-8 -*-
3 |
4 | from jpype import *
5 | import logging
6 | import settings_MQ as settings
7 |
8 | logger = logging.getLogger("MQMessageListener")
9 |
10 | __all__ = ["msgListenerConcurrentlyProxy", "msgListenerOrderlyProxy"]
11 |
12 | from MQMessage import ConsumeConcurrentlyStatus, ConsumeOrderlyStatus
13 |
14 | class MessageListenerConcurrently:
15 | '''接口类MessageListenerConcurrently的实现
16 | public interface MessageListenerConcurrently extends MessageListener {
17 | '''
18 | def consumeMessage(self, msgs, context):
19 | '''
20 | # JAVA prototype
21 | # ConsumeConcurrentlyStatus consumeMessage(final List msgs, final ConsumeConcurrentlyContext context);
22 | '''
23 | logger.debug("Into consumerMessage of MessageListenerConcurrently")
24 | #msg = msgs.get(JInt(0))
25 | for msg in msgs:
26 | topic = msg.getTopic()
27 | tags = msg.getTags()
28 | body = str(msg.getBody()).decode(settings.MsgBodyEncoding)
29 |
30 | logger.debug(msg.toString())
31 | # In Python 2.x, bytes is just an alias for str. 所以bytes解码时要注意了, msg.body.decode会出错(bytes没有decode方法)!
32 | #logger.debug("Message body: " + str(msg.getBody()))
33 | #logger.debug("Message body: " + str(msg.getBody()).decode(settings.MsgBodyEncoding))
34 | logger.debug("Message body: " + body)
35 |
36 | if topic == "RMQTopicTest":
37 | # 执行TopicTest的消费逻辑
38 | if tags == "TagA":
39 | # 执行TagA的消费
40 | logger.debug("Got message with topic " + topic + " and tags " + tags)
41 | elif tags == "TagB":
42 | # 执行TagB的消费
43 | logger.debug("Got message with topic " + topic + " and tags " + tags)
44 | elif tags == "TagC":
45 | # 执行TagC的消费
46 | logger.debug("Got message with topic " + topic + " and tags " + tags)
47 | else:
48 | # 错误的Tag
49 | logger.error("Got message with topic " + topic + " and UNKNOWN tags " + tags)
50 | elif topic == "TopicTest1":
51 | # 执行TopicTest1的消费逻辑
52 | logger.debug("Got message with topic " + topic + " and tags " + tags)
53 | else:
54 | logger.debug("Got message with UNKNOWN topic " + topic )
55 |
56 | return ConsumeConcurrentlyStatus['CONSUME_SUCCESS']
57 |
58 | class MessageListenerOrderly:
59 | '''接口类MessageListenerOrderly的实现
60 | public interface MessageListenerOrderly extends MessageListener {
61 | '''
62 | def __init__(self):
63 | #JAVA原子类
64 | self.consumeTimes = java.util.concurrent.atomic.AtomicLong(0)
65 |
66 | def consumeMessage(self, msgs, context):
67 | '''
68 | # JAVA prototype
69 | # ConsumeOrderlyStatus consumeMessage(final List msgs, final ConsumeOrderlyContext context);
70 | '''
71 | context.setAutoCommit(False)
72 | logger.debug(java.lang.Thread.currentThread().getName() + " Receive New Messages: " + msgs.toString())
73 | #TODO: msgs.toString()可能需要改成for msg in msgs: msg.toString()
74 |
75 | self.consumeTimes.incrementAndGet()
76 | consumeTimes = self.consumeTimes.get()
77 | #print consumeTimes
78 | #print type(consumeTimes)
79 |
80 | if (consumeTimes % 2) == 0:
81 | logger.debug("consumeTimes % 2 = 0, return SUCCESS")
82 | return ConsumeOrderlyStatus['SUCCESS']
83 | elif (consumeTimes % 3) == 0:
84 | logger.debug("consumeTimes % 3 = 0, return ROLLBACK")
85 | return ConsumeOrderlyStatus['ROLLBACK']
86 | elif (consumeTimes % 4) == 0:
87 | logger.debug("consumeTimes % 4 = 0, return COMMIT")
88 | return ConsumeOrderlyStatus['COMMIT']
89 | elif (consumeTimes % 5) == 0:
90 | logger.debug("consumeTimes % 5 = 0, return SUSPEND_CURRENT_QUEUE_A_MOMENT")
91 | context.setSuspendCurrentQueueTimeMillis(3000)
92 | return ConsumeOrderlyStatus['SUSPEND_CURRENT_QUEUE_A_MOMENT']
93 | else:
94 | logger.debug("consumeTimes is not times of 2, 3, 4, 5, return SUCCESS")
95 | return ConsumeOrderlyStatus['SUCCESS']
96 |
97 |
98 | #实现 与 类代理
99 | msgListenerConcurrently = MessageListenerConcurrently()
100 | #JProxy("MessageListenerConcurrently", inst = msgListenerConcurrently)
101 | msgListenerConcurrentlyProxy = JProxy("com.alibaba.rocketmq.client.consumer.listener.MessageListenerConcurrently", inst = msgListenerConcurrently)
102 |
103 |
104 | #实现 与 类代理
105 | msgListenerOrderly = MessageListenerOrderly()
106 | #JProxy("MessageListenerOrderly", inst = msgListenerOrderly)
107 | msgListenerOrderlyProxy = JProxy("com.alibaba.rocketmq.client.consumer.listener.MessageListenerOrderly", inst = msgListenerOrderly)
108 |
109 |
--------------------------------------------------------------------------------
/MQProducer.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding:utf-8 -*-
3 |
4 | from jpype import *
5 | import logging
6 | import time
7 |
8 | logger = logging.getLogger("MQProducer")
9 |
10 | __all__ = ["MQProducer"]
11 |
12 | DefaultMQProducer = JPackage('com.alibaba.rocketmq.client.producer').DefaultMQProducer
13 | MQClientException = JPackage('com.alibaba.rocketmq.client.exception').MQClientException
14 | SendResult = JPackage('com.alibaba.rocketmq.client.producer').SendResult
15 |
16 | class MQProducer(object):
17 | def __init__(self, groupName, namesrvAddr):
18 | """
19 | :param groupName:
20 | :param namesrvAddr:
21 | :return:
22 | # JAVA prototype(实现的是第2个,见init函数 实例化)
23 | # public DefaultMQProducer() {
24 | # public DefaultMQProducer(final String producerGroup) {
25 | # public DefaultMQProducer(RPCHook rpcHook) {
26 | # public DefaultMQProducer(final String producerGroup, RPCHook rpcHook) {
27 | """
28 | self.producer = None #初始化放在了init函数中
29 | self.groupName = groupName
30 | self.namesrvAddr = namesrvAddr
31 | self.instanceName = str(int(time.time()*1000)) #毫秒值作为instance name
32 | self.compressMsgBodyOverHowmuch = 4096 #消息压缩阈值
33 |
34 | def init(self):
35 | """批量设置一些基本项(为了尽可能少实现这些API接口,如以后有需要,可以逐个移出init)"""
36 | logger.info('Initializing producer ' + self.instanceName + ' ...')
37 | self.producer = DefaultMQProducer(JString(self.groupName)) #创建实例
38 | self.producer.setNamesrvAddr(JString(self.namesrvAddr))
39 | self.producer.setInstanceName(JString(self.instanceName))
40 | self.producer.setCompressMsgBodyOverHowmuch(JInt(self.compressMsgBodyOverHowmuch))
41 |
42 | def start(self):
43 | """
44 | # JAVA prototype
45 | # public void start() throws MQClientException {
46 | """
47 | logger.info('Starting producer ' + self.instanceName + ' ...')
48 | self.producer.start()
49 |
50 | def shutdown(self):
51 | """
52 | # JAVA prototype
53 | # public void shutdown() {
54 | """
55 | logger.info('Shutting down producer ' + self.instanceName + ' ...')
56 | self.producer.shutdown()
57 |
58 | def send(self, MQMsg):
59 | """
60 | # JAVA prototype
61 | # public SendResult send(Message msg, long timeout) throws MQClientException, RemotingException,
62 | # MQBrokerException, InterruptedException {
63 | # public void send(Message msg, SendCallback sendCallback) throws MQClientException, RemotingException,
64 | # InterruptedException {
65 | # public void send(Message msg, SendCallback sendCallback, long timeout) throws MQClientException,
66 | # RemotingException, InterruptedException {
67 | # public void sendOneway(Message msg) throws MQClientException, RemotingException, InterruptedException {
68 | # public SendResult send(Message msg, MessageQueue mq) throws MQClientException, RemotingException,
69 | # MQBrokerException, InterruptedException {
70 | # public SendResult send(Message msg, MessageQueue mq, long timeout) throws MQClientException,
71 | # RemotingException, MQBrokerException, InterruptedException {
72 | # public void send(Message msg, MessageQueue mq, SendCallback sendCallback) throws MQClientException,
73 | # RemotingException, InterruptedException {
74 | # public void send(Message msg, MessageQueue mq, SendCallback sendCallback, long timeout)
75 | # throws MQClientException, RemotingException, InterruptedException {
76 | # public SendResult send(Message msg, MessageQueueSelector selector, Object arg) throws MQClientException,
77 | # RemotingException, MQBrokerException, InterruptedException {
78 | # public SendResult send(Message msg, MessageQueueSelector selector, Object arg, long timeout)
79 | # throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
80 | # public void send(Message msg, MessageQueueSelector selector, Object arg, SendCallback sendCallback)
81 | # throws MQClientException, RemotingException, InterruptedException {
82 | # public void send(Message msg, MessageQueueSelector selector, Object arg, SendCallback sendCallback,
83 | # long timeout) throws MQClientException, RemotingException, InterruptedException {
84 | """
85 | logger.debug('Producer ' + self.instanceName + ' sending message: ' + MQMsg.tostr())
86 | return self.producer.send(MQMsg.msg)
87 |
88 | def sendOneway(self, MQMsg):
89 | """
90 | # JAVA prototype
91 | # public void sendOneway(Message msg) throws MQClientException, RemotingException, InterruptedException {
92 | # public void sendOneway(Message msg, MessageQueue mq) throws MQClientException, RemotingException,
93 | # InterruptedException {
94 | # public void sendOneway(Message msg, MessageQueueSelector selector, Object arg) throws MQClientException,
95 | # RemotingException, InterruptedException {
96 | """
97 | logger.debug('Producer ' + self.instanceName + ' sending one-way message: ' + MQMsg.tostr())
98 | return self.producer.sendOneway(MQMsg.msg)
99 |
--------------------------------------------------------------------------------
/MQPullConsumer.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding:utf-8 -*-
3 |
4 | from jpype import *
5 | import logging
6 | import time
7 |
8 | logger = logging.getLogger("MQPullConsumer")
9 |
10 | __all__ = ["MQPullConsumer"]
11 |
12 | DefaultMQPullConsumer= JPackage('com.alibaba.rocketmq.client.consumer').DefaultMQPullConsumer
13 | MQClientException = JPackage('com.alibaba.rocketmq.client.exception').MQClientException
14 | PullResult = JPackage('com.alibaba.rocketmq.client.consumer').PullResult
15 | MessageQueue = JPackage('com.alibaba.rocketmq.common.message').MessageQueue
16 |
17 |
18 | class MQPullConsumer(object):
19 |
20 | def __init__(self, groupName, namesrvAddr):
21 | """
22 | :param groupName:
23 | :param namesrvAddr:
24 | :return:
25 | """
26 | self.consumer = None #初始化放在了init函数中
27 | self.groupName = groupName
28 | self.namesrvAddr = namesrvAddr
29 | self.instanceName = str(int(time.time()*1000)) #毫秒值作为instance name
30 |
31 | self.mqs = None
32 | self.offseTable = {} # map of message queue id to queue offset
33 |
34 | def init(self):
35 | """批量设置一些基本项(为了尽可能少实现这些API接口,如以后有需要,可以逐个移出init)"""
36 | logger.info('Initializing consumer ' + self.instanceName + ' ...')
37 | self.consumer = DefaultMQPullConsumer(JString(self.groupName)) #创建实例
38 | self.consumer.setNamesrvAddr(JString(self.namesrvAddr))
39 | self.consumer.setInstanceName(JString(self.instanceName))
40 |
41 | def start(self):
42 | """
43 | # JAVA prototype
44 | # public void start() throws MQClientException {
45 | """
46 | logger.info('Starting consumer ' + self.instanceName + ' ...')
47 | self.consumer.start()
48 |
49 | def shutdown(self):
50 | """
51 | # JAVA prototype
52 | # public void shutdown() {
53 | """
54 | logger.info('Shutting down consumer ' + self.instanceName + ' ...')
55 | self.consumer.shutdown()
56 |
57 | def pullBlockIfNotFound(self, mq, subExpression, offset, maxNums):
58 | """
59 | # JAVA prototype
60 | # public PullResult pullBlockIfNotFound(MessageQueue mq, String subExpression, long offset, int maxNums)
61 | # throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
62 | # public void pullBlockIfNotFound(MessageQueue mq, String subExpression, long offset, int maxNums,
63 | # PullCallback pullCallback) throws MQClientException, RemotingException, InterruptedException {
64 | """
65 | pullResult = self.consumer.pullBlockIfNotFound(mq, subExpression, self.getMessageQueueOffset(mq), maxNums)
66 | return pullResult
67 |
68 | def fetchSubscribeMessageQueues(self, topic):
69 | """
70 | # JAVA prototype
71 | # public Set fetchSubscribeMessageQueues(String topic) throws MQClientException {
72 | """
73 | self.mqs = self.consumer.fetchSubscribeMessageQueues(JString(topic))
74 |
75 | def getMessageQueueOffset(self, mq):
76 | """
77 | 获取某个MQ中的当前消息的offset
78 | :param mq:
79 | :return:
80 | """
81 | haskey = self.offseTable.has_key(mq.queueId)
82 | if haskey:
83 | return self.offseTable[mq.queueId]
84 | else:
85 | return 0
86 |
87 | def putMessageQueueOffset(self, mq, offset):
88 | """
89 | 设置某个MQ中的当前消息的offset(更新后的值)
90 | :param mq:
91 | :param offset:
92 | :return:
93 | """
94 | self.offseTable[mq.queueId] = offset
95 |
--------------------------------------------------------------------------------
/MQPushConsumer.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding:utf-8 -*-
3 |
4 | from jpype import *
5 | import logging
6 | import time
7 | import settings_MQ as settings
8 |
9 | logger = logging.getLogger("MQPullConsumer")
10 |
11 | __all__ = ["MQPushConsumer"]
12 |
13 | DefaultMQPushConsumer= JPackage('com.alibaba.rocketmq.client.consumer').DefaultMQPushConsumer
14 | MQClientException = JPackage('com.alibaba.rocketmq.client.exception').MQClientException
15 | #MessageExt = JPackage('com.alibaba.rocketmq.common.message').MessageExt
16 | #ConsumeConsurrentlyContext = JPackage('com.alibaba.rocketmq.client.consumer.listener').ConsumeConsurrentlyContext
17 | #ConsumeConsurrentlyStatus = JPackage('com.alibaba.rocketmq.client.consumer.listener').ConsumeConsurrentlyStatus
18 | #MessageListenerConcurrently = JPackage('com.alibaba.rocketmq.client.consumer.listener').MessageListenerConcurrently
19 |
20 | #from MQMessage import ConsumeConcurrentlyStatus, ConsumeOrderlyStatus
21 |
22 | class MQPushConsumer(object):
23 | '''实现类
24 | public class DefaultMQPushConsumer extends ClientConfig implements MQPushConsumer {
25 | '''
26 |
27 | def __init__(self, groupName, namesrvAddr):
28 | """
29 | :param groupName:
30 | :param namesrvAddr:
31 | :return:
32 | """
33 | self.consumer = None #初始化放在了init函数中
34 | self.groupName = groupName
35 | self.namesrvAddr = namesrvAddr
36 | self.instanceName = str(int(time.time()*1000)) #毫秒值作为instance name
37 |
38 | def init(self):
39 | """批量设置一些基本项(为了尽可能少实现这些API接口,如以后有需要,可以逐个移出init)"""
40 | logger.info('Initializing consumer ' + self.instanceName + ' ...')
41 | self.consumer = DefaultMQPushConsumer(JString(self.groupName)) #创建实例
42 | self.consumer.setNamesrvAddr(JString(self.namesrvAddr))
43 | self.consumer.setInstanceName(JString(self.instanceName))
44 |
45 | def start(self):
46 | """
47 | # JAVA prototype
48 | # public void start() throws MQClientException {
49 | """
50 | logger.info('Starting consumer ' + self.instanceName + ' ...')
51 | self.consumer.start()
52 |
53 | def shutdown(self):
54 | """
55 | # JAVA prototype
56 | # public void shutdown() {
57 | """
58 | logger.info('Shutting down consumer ' + self.instanceName + ' ...')
59 | self.consumer.shutdown()
60 |
61 | def setMessageModel(self, messageModel):
62 | """
63 | # JAVA prototype
64 | # public void setMessageModel(MessageModel messageModel)
65 | """
66 | logger.info('Setting message model of instance ' + self.instanceName + ' to ' + str(messageModel))
67 | #self.consumer.setMessageModel(JObject(messageModel, "com.alibaba.rocketmq.common.protocol.heartbeat.MessageModel"))
68 | self.consumer.setMessageModel(messageModel)
69 |
70 | def subscribe(self, topic, subExpression):
71 | # JAVA prototype
72 | # public void subscribe(String topic, String subExpression) throws MQClientException {
73 | # public void subscribe(String topic, String fullClassName, String filterClassSource) throws MQClientException {
74 | self.consumer.subscribe(JString(topic), JString(subExpression))
75 |
76 | def unsubscribe(self, topic):
77 | # JAVA prototype
78 | # public void unsubscribe(String topic) {
79 | self.consumer.unsubscribe(JString(topic))
80 |
81 | def setConsumeFromWhere(self, fromwhere):
82 | # JAVA prototype
83 | # public void setConsumeFromWhere(ConsumeFromWhere consumeFromWhere) {
84 | #self.consumer.setConsumeFromWhere(JObject(fromwhere, "com.alibaba.rocketmq.common.consumer.ConsumeFromWhere"))
85 | self.consumer.setConsumeFromWhere(fromwhere)
86 |
87 | def registerMessageListener(self, listener):
88 | # JAVA prototype
89 | # public void registerMessageListener(MessageListenerConcurrently messageListener) {
90 | # public void registerMessageListener(MessageListenerOrderly messageListener) {
91 | self.consumer.registerMessageListener(listener)
92 |
93 | # 下面内容移除到MQMesasgeListener.py中
94 | #
95 | # class MessageListenerConcurrently:
96 | # '''接口类MessageListenerConcurrently的实现
97 | # public interface MessageListenerConcurrently extends MessageListener {
98 | # '''
99 | # def consumeMessage(self, msgs, context):
100 | # '''
101 | # # JAVA prototype
102 | # # ConsumeConcurrentlyStatus consumeMessage(final List msgs, final ConsumeConcurrentlyContext context);
103 | # '''
104 | # logger.debug("Into consumerMessage of MessageListenerConcurrently")
105 | # #msg = msgs.get(JInt(0))
106 | # for msg in msgs:
107 | # topic = msg.getTopic()
108 | # tags = msg.getTags()
109 | # body = str(msg.getBody()).decode(settings.MsgBodyEncoding)
110 | #
111 | # logger.debug(msg.toString())
112 | # # In Python 2.x, bytes is just an alias for str. 所以bytes解码时要注意了, msg.body.decode会出错(bytes没有decode方法)!
113 | # #logger.debug("Message body: " + str(msg.getBody()))
114 | # #logger.debug("Message body: " + str(msg.getBody()).decode(settings.MsgBodyEncoding))
115 | # logger.debug("Message body: " + body)
116 | #
117 | # if topic == "RMQTopicTest":
118 | # # 执行TopicTest的消费逻辑
119 | # if tags == "TagA":
120 | # # 执行TagA的消费
121 | # logger.debug("Got message with topic " + topic + " and tags " + tags)
122 | # elif tags == "TagB":
123 | # # 执行TagB的消费
124 | # logger.debug("Got message with topic " + topic + " and tags " + tags)
125 | # elif tags == "TagC":
126 | # # 执行TagC的消费
127 | # logger.debug("Got message with topic " + topic + " and tags " + tags)
128 | # else:
129 | # # 错误的Tag
130 | # logger.error("Got message with topic " + topic + " and UNKNOWN tags " + tags)
131 | # elif topic == "TopicTest1":
132 | # # 执行TopicTest1的消费逻辑
133 | # logger.debug("Got message with topic " + topic + " and tags " + tags)
134 | # else:
135 | # logger.debug("Got message with UNKNOWN topic " + topic )
136 | #
137 | # return ConsumeConcurrentlyStatus['CONSUME_SUCCESS']
138 | #
139 | # #实现
140 | # msgListenerConcurrently = MessageListenerConcurrently()
141 | # #JProxy("MessageListenerConcurrently", inst = msgListenerConcurrently)
142 | # msgListenerConcurrentlyProxy = JProxy("com.alibaba.rocketmq.client.consumer.listener.MessageListenerConcurrently", inst = msgListenerConcurrently)
143 | #
144 | # class MessageListenerOrderly:
145 | # '''接口类MessageListenerOrderly的实现
146 | # public interface MessageListenerOrderly extends MessageListener {
147 | # '''
148 | # def __init__(self):
149 | # #JAVA原子类
150 | # self.consumeTimes = java.util.concurrent.atomic.AtomicLong(0)
151 | #
152 | # def consumeMessage(self, msgs, context):
153 | # # JAVA prototype
154 | # # ConsumeOrderlyStatus consumeMessage(final List msgs, final ConsumeOrderlyContext context);
155 | # context.setAutoCommit(False)
156 | # logger.debug(java.lang.Thread.currentThread().getName() + " Receive New Messages: " + msgs.toString())
157 | # #TODO: msgs.toString()可能需要改成for msg in msgs: msg.toString()
158 | #
159 | # self.consumeTimes.incrementAndGet()
160 | # consumeTimes = self.consumeTimes.get()
161 | # # print consumeTimes
162 | # # print type(consumeTimes)
163 | #
164 | # if (consumeTimes % 2) == 0:
165 | # logger.debug("consumeTimes % 2 = 0, return SUCCESS")
166 | # return ConsumeOrderlyStatus['SUCCESS']
167 | # elif (consumeTimes % 3) == 0:
168 | # logger.debug("consumeTimes % 3 = 0, return ROLLBACK")
169 | # return ConsumeOrderlyStatus['ROLLBACK']
170 | # elif (consumeTimes % 4) == 0:
171 | # logger.debug("consumeTimes % 4 = 0, return COMMIT")
172 | # return ConsumeOrderlyStatus['COMMIT']
173 | # elif (consumeTimes % 5) == 0:
174 | # logger.debug("consumeTimes % 5 = 0, return SUSPEND_CURRENT_QUEUE_A_MOMENT")
175 | # context.setSuspendCurrentQueueTimeMillis(3000)
176 | # return ConsumeOrderlyStatus['SUSPEND_CURRENT_QUEUE_A_MOMENT']
177 | # else:
178 | # logger.debug("consumeTimes is not times of 2, 3, 4, 5, return SUCCESS")
179 | # return ConsumeOrderlyStatus['SUCCESS']
180 | #
181 | # #实现
182 | # msgListenerOrderly = MessageListenerOrderly()
183 | # #JProxy("MessageListenerOrderly", inst = msgListenerOrderly)
184 | # msgListenerOrderlyProxy = JProxy("com.alibaba.rocketmq.client.consumer.listener.MessageListenerOrderly", inst = msgListenerOrderly)
185 | #
186 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RMQ-Client4Python
2 | Python client module for RocketMQ
3 |
4 |
5 | ## Dependencies
6 | JAVA libraries:
7 | RocketMQ libs
8 | Python libraries:
9 | jpype
10 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GangLuICT/RMQ-Client4Python/5a7cfe2efc1e6c009090b891d0a4fb0dc03ce3e4/__init__.py
--------------------------------------------------------------------------------
/jpypetest.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding:utf-8 -*-
3 |
4 | import os
5 | import logging
6 | import jpype
7 |
8 | # 为了支持中文输入,要显式设置编码
9 | import sys
10 | if sys.getdefaultencoding() != 'utf-8':
11 | reload(sys)
12 | sys.setdefaultencoding('utf-8');
13 |
14 | if __name__ == "__main__":
15 | jvmPath = jpype.getDefaultJVMPath()
16 | print jvmPath
17 | jpype.startJVM(jvmPath)
18 | #jpype.startJVM(jvmPath, "-Xms32m", "-Xmx256m", "-mx256m", "-Djava.class.path=/Users/tan9le/temp/some-lib.jar:")
19 | jpype.java.lang.System.out.println( " hello world! " )
20 | map = jpype.JClass("java.util.HashMap")()
21 | map.put("123","123124测试中文")
22 | print map.get("123")
23 | jpype.shutdownJVM()
24 |
25 |
--------------------------------------------------------------------------------
/settings_MQ.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding:utf-8 -*-
3 |
4 | import logging
5 | logging.basicConfig(level=logging.DEBUG,
6 | format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
7 | datefmt='%a, %d %b %Y %H:%M:%S',
8 | filename='rmq.log',
9 | filemode='a')
10 |
11 | RMQClientJAR = '/home/deploy/rocketmq/alibaba-rocketmq/lib/'
12 | JAVA_EXT_DIRS = RMQClientJAR
13 |
14 | #startJVM中的options参数不能包含空格!只能一项一项填写
15 | #JVM_OPTIONS = '-Xms32m -Xmx256m -mx256m'
16 | JVM_OPTIONS = '-mx256m'
17 |
18 | pullMaxNums = 32
19 | MsgBodyEncoding = 'utf-8'
20 |
--------------------------------------------------------------------------------
/test/Producer.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding:utf-8 -*-
3 |
4 | import logging
5 | logger = logging.getLogger("Producer")
6 |
7 | #导入上级目录模块
8 | import sys
9 | sys.path.append("..")
10 | import settings_MQ as settings
11 |
12 | #启动JVM
13 | from jpype import *
14 | jvmPath = getDefaultJVMPath()
15 | startJVM(jvmPath, settings.JVM_OPTIONS, "-Djava.ext.dirs="+settings.JAVA_EXT_DIRS)
16 | #startJVM(jvmPath, "-Djava.class.path=" + settings.RMQClientJAR + ":")
17 | logger.info(java.lang.System.getProperty("java.class.path"))
18 | logger.info(java.lang.System.getProperty("java.ext.dirs"))
19 |
20 | #启动JVM之后才能调用JPackage,否则找不到相关的jar包
21 | from MQProducer import *
22 | from MQMessage import MQMessage
23 |
24 | # 为了支持文本中文输入,要显式设置编码;该编码不影响Message的Body的编码
25 | import sys
26 | if sys.getdefaultencoding() != 'utf-8':
27 | reload(sys)
28 | sys.setdefaultencoding('utf-8');
29 |
30 | ###############################################################################
31 | if __name__ == '__main__':
32 | #jpype.startJVM(jvmPath, "-Xms32m", "-Xmx256m", "-mx256m", "-Djava.class.path=/Users/tan9le/temp/some-lib.jar:")
33 | java.lang.System.out.println("Java runs correct!")
34 |
35 | producer = MQProducer('MQClient4Python-Producer', 'jfxr-7:9876;jfxr-6:9876')
36 | producer.init()
37 | producer.start()
38 | MQMsg = MQMessage('RMQTopicTest', # topic
39 | 'TagB', # tag
40 | 'OrderID001', # key
41 | 'Hello, RocketMQ!') # body
42 | sendResult = producer.send(MQMsg)
43 | MQMsg = MQMessage('RMQTopicTest', # topic
44 | 'TagC', # tag
45 | 'OrderID001', # key
46 | 'Hello, RocketMQ! I am 陆钢') # body
47 | sendResult = producer.send(MQMsg)
48 |
49 | producer.shutdown()
50 |
51 | shutdownJVM()
52 |
--------------------------------------------------------------------------------
/test/PullConsumer.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding:utf-8 -*-
3 |
4 | import logging
5 | logger = logging.getLogger("PullConsumer")
6 |
7 | #导入上级目录模块
8 | import sys
9 | sys.path.append("..")
10 | import settings_MQ as settings
11 |
12 | #启动JVM
13 | from jpype import *
14 | jvmPath = getDefaultJVMPath()
15 | startJVM(jvmPath, "-ea", "-Djava.ext.dirs=" + settings.JAVA_EXT_DIRS)
16 | #startJVM(jvmPath, "-Djava.class.path=" + settings.RMQClientJAR + ":")
17 | print settings.JAVA_EXT_DIRS
18 | logger.info(java.lang.System.getProperty("java.class.path"))
19 | logger.info(java.lang.System.getProperty("java.ext.dirs"))
20 |
21 | #启动JVM之后才能调用JPackage,否则找不到相关的jar包
22 | from MQPullConsumer import *
23 | from MQMessage import MQMessage, PullStatus
24 |
25 | # 为了支持文本中文输入,要显式设置编码;该编码不影响Message的Body的编码
26 | import sys
27 | if sys.getdefaultencoding() != 'utf-8':
28 | reload(sys)
29 | sys.setdefaultencoding('utf-8');
30 |
31 | if __name__ == '__main__':
32 | consumer = MQPullConsumer('MQClient4Python-Consumer', 'jfxr-7:9876;jfxr-6:9876')
33 | consumer.init()
34 | consumer.start()
35 |
36 | consumer.fetchSubscribeMessageQueues("RMQTopicTest") #获取所有消息队列,返回值存储到consumer.mqs
37 | #TODO:
38 | # 1. fetchSubscribeMessageQueues可能返回异常:Can not find Message Queue for this topic
39 | # 如果不存在Topic,则创建topic:createTopic(String key, String newTopic, int queueNum)
40 | # 2. MessageQueue的数目,如何确定的!
41 | # 3. PullConsumer没有ConsumeFromWhere这项设置
42 |
43 | while True:
44 | for mq in consumer.mqs:
45 | logger.debug("Pulling from message queue: " + str(mq.queueId))
46 | while True:
47 | try:
48 | pullResult = consumer.pullBlockIfNotFound(mq, '', consumer.getMessageQueueOffset(mq), settings.pullMaxNums) # brokerSuspendMaxTimeMillis默认值是20s
49 | consumer.putMessageQueueOffset(mq, pullResult.getNextBeginOffset())
50 | pullStatus = PullStatus[str(pullResult.getPullStatus())] # JAVA中的enum对应到Python中没有转换为Int,enum对象转换为string的时候是其枚举值的名字,而不是enum的值(0,1...)!
51 | #print pullStatus
52 | #print type(pullStatus)
53 | #print type(PullStatus['NO_MATCHED_MSG'])
54 | #print pullStatus == PullStatus['NO_MATCHED_MSG'] # 比较对象时先将其转换成了string
55 | if pullStatus == PullStatus['FOUND']:
56 | logger.debug('Found')
57 | logger.debug(pullResult.toString())
58 | msgList = pullResult.getMsgFoundList()
59 | for msg in msgList:
60 | logger.debug(msg.toString())
61 | # In Python 2.x, bytes is just an alias for str. 所以bytes解码时要注意了, msg.body.decode会出错!
62 | #logger.debug("Message body: " + str(msg.body))
63 | logger.debug("Message body: " + str(msg.body).decode(settings.MsgBodyEncoding))
64 | #TODO: 进一步分析pull下来的result
65 | elif pullStatus == PullStatus['NO_NEW_MSG']:
66 | logger.debug('NO_NEW_MSG')
67 | break
68 | elif pullStatus == PullStatus['NO_MATCHED_MSG']:
69 | logger.debug('NO_MATCHED_MSG')
70 | elif pullStatus == PullStatus['OFFSET_ILLEGAL']:
71 | logger.debug('OFFSET_ILLEGAL')
72 | else:
73 | logger.error('Wrong pull status: ' + str(pullStatus))
74 | except JavaException, ex:
75 | logger.error(str(ex.javaClass()) + str(ex.message()))
76 | logger.error(str(ex.stacktrace()))
77 |
78 | consumer.shutdown()
79 |
80 | shutdownJVM()
81 |
--------------------------------------------------------------------------------
/test/PushConsumer.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding:utf-8 -*-
3 |
4 | import logging
5 | logger = logging.getLogger("PushConsumer")
6 |
7 | #导入上级目录模块
8 | import sys
9 | sys.path.append("..")
10 | import settings_MQ as settings
11 |
12 | #启动JVM
13 | from jpype import *
14 | jvmPath = getDefaultJVMPath()
15 | startJVM(jvmPath, settings.JVM_OPTIONS, "-Djava.ext.dirs="+settings.JAVA_EXT_DIRS)
16 | #startJVM(jvmPath, "-Djava.class.path=" + settings.RMQClientJAR + ":")
17 | logger.info(java.lang.System.getProperty("java.class.path"))
18 | logger.info(java.lang.System.getProperty("java.ext.dirs"))
19 |
20 | #启动JVM之后才能调用JPackage,否则找不到相关的jar包
21 | from MQPushConsumer import MQPushConsumer
22 | from MQMessageListener import msgListenerConcurrentlyProxy, msgListenerOrderlyProxy
23 | from MQMessage import ConsumeFromWhere, MessageModel
24 |
25 | # 为了支持文本中文输入,要显式设置编码;该编码不影响Message的Body的编码
26 | import sys
27 | if sys.getdefaultencoding() != 'utf-8':
28 | reload(sys)
29 | sys.setdefaultencoding('utf-8');
30 |
31 | import time
32 |
33 | if __name__ == '__main__':
34 | consumer = MQPushConsumer('MQClient4Python-Consumer', 'jfxr-7:9876;jfxr-6:9876')
35 | consumer.init()
36 |
37 | consumer.setMessageModel(MessageModel['CLUSTERING']) # 默认是CLUSTERING
38 | #consumer.setMessageModel(MessageModel.CLUSTERING) # 默认是CLUSTERING
39 |
40 | consumer.subscribe("RMQTopicTest", "TagB")
41 |
42 | consumer.setConsumeFromWhere(ConsumeFromWhere['CONSUME_FROM_LAST_OFFSET'])
43 | #consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET)
44 |
45 | #consumer.registerMessageListener(msgListenerConcurrentlyProxy)
46 | consumer.registerMessageListener(msgListenerOrderlyProxy)
47 |
48 | consumer.start()
49 |
50 | while True:
51 | time.sleep(1)
52 |
53 | #监听状态时不需要shutdown,除非真实想退出!
54 | #consumer.shutdown()
55 | #监听状态时JVM也不能退出,除非真实想退出!
56 | #shutdownJVM()
57 |
--------------------------------------------------------------------------------