如果要在amq上使用mqtt協議,有一個很大的問題是mqtt協議無法使用amq的消息選擇器功能,這樣就會造成一個很麻煩的情況:
- 如果要用mqtt協議點對點發送消息,那么就需要為每個mqtt連接單獨建立一個queue或者topic,可amq對大量destination的支持并不好,測試中發現無法支撐超過9000個queue。也就是說如果要用amq實現mqtt連接的點對點發送,連接數就不能超過9000個。
在網上找了一圈,只有一篇類似的解決方法,是大神kimmking寫的:
使用ActiveMQ+MQTT實現Android點對點消息通知
但是這個方法太過麻煩,而且隨著mq版本升級,需要每次都改一下broker的代碼。
趁著今晚有空,根據之前的思路研究了一下這個問題的解決辦法。
消息選擇器的功能有兩個要素:
- 消息生產者在消息屬性里帶上篩選的字段
- 消費者在建立消費者的時候,注明消息選擇器
難點是mqtt協議既不支持消息屬性,也不支持帶上選擇器建立消費者。
我的項目里由于發送方可以不用mqtt協議,所以我主要考慮解決第二個難點,其實第一個難點的解決方法也類似,也可以通過插件實現。
第二個難點的解決方法是通過編寫插件,在broker執行addConsumer的時候,為每一個consumer自動加上一個ClientID='客戶端clientID'這樣的消息選擇器。插件的編寫方法見我上一篇文章
ActiveMQ插件開發
代碼如下:
package com.icbc.amqs;
import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerPlugin;
import org.apache.activemq.plugin.StatisticsBrokerPlugin;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class MqttSelectorPlugin implements BrokerPlugin {
private Log log = LogFactory.getLog(StatisticsBrokerPlugin.class);
public Broker installPlugin(Broker broker) throws Exception {
log.info("install MqttSelectorPlugin ");
return new MqttSelector(broker);
}
}
package com.icbc.amqs;
import org.apache.activemq.broker.Broker;
import org.apache.activemq.broker.BrokerFilter;
import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.broker.region.Subscription;
import org.apache.activemq.command.ConsumerInfo;
import org.apache.activemq.plugin.StatisticsBrokerPlugin;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class MqttSelector extends BrokerFilter{
private Log log;
public MqttSelector(Broker next) {
super(next);
log = LogFactory.getLog(StatisticsBrokerPlugin.class);
log.info("initialize Message Log plugin");
}
//需要注意的是mqtt協議建立消費者的時候。consumerInfo里不會帶上clientID,只能從ConnectionContext中取。
@Override
public Subscription addConsumer(ConnectionContext context, ConsumerInfo info) throws Exception {
if (info.getDestination().toString().contains("test")){
info.setSelector("ClientID='"+context.getClientId()+"'");
log.info("[Consumer] Adding consumer : "+ info);
}
return super.addConsumer(context, info);
}
}
經測試,可以讓mqtt協議的消費者正常接收消息。