在對比了RabbitMQ,apacheMQ之後,發現還是 rocketMQ 好用。為什麼好用,百度查查就都知道了。不說廢話直接說說 Linux安裝:
1、需要軟體(免安裝版)
下載地址 [release 下載地址](https://github.com/apache/incubator-rocketmq/releases) https://github.com/apache/incubator-rocketmq/releases rocketmq-all-4.0.0-incubating-bin-release.zip
2、安裝JDK環境
就不多說了 3、啟動namesrv
進入安裝資料夾的bin 下面
使用命令:nohup sh mqnamesrv &
查看 nohup日誌
發現報錯資訊Caused by: java.net.UnknownHostException: smkzfbweb02: 未知的名稱或服務
這個時候查看檔案/etc/hosts 和 /etc/hostname
發現問題所在了,我的生產伺服器的hostname 被管理員改了,但是沒有加入到 hosts 對應列表中,所以報錯。java.net.UnknownHostException
我們修改 /etc/hosts 檔案
再次啟動
The Name Server boot success. serializeType=JSON 4、啟動broker
使用命令 nohup sh mqbroker -n “1273.0.0.1:9876” &
查看日誌
這裡說的是# Native memory allocation (malloc) failed to allocate 8589934592 bytes for committing reserved memory. 記憶體不足夠了。
查看設定檔
runbroker.sh檔案是 broker 的開機檔案
runserver.sh 檔案是 namesev 開機檔案
查看
這裡發現他們的預設需要的記憶體 都很大,所以根據自己的伺服器配置 減少一些配置
配置好了之後再啟動就OK了。
package com.cat.common.rocketmq;import java.util.Properties;import org.apache.log4j.Logger;import com.alibaba.rocketmq.client.consumer.DefaultMQPushConsumer;import com.alibaba.rocketmq.client.exception.MQClientException;import com.alibaba.rocketmq.client.producer.DefaultMQProducer;import com.alibaba.rocketmq.client.producer.SendResult;import com.alibaba.rocketmq.common.message.Message;import com.alibaba.rocketmq.common.protocol.heartbeat.MessageModel;import com.cat.common.lang.RDate;import com.cat.common.lang.RString;import com.cat.common.lang.RUuid;import com.cat.common.properties.PropertiesUtil;/** * RocketMQ不保證訊息不重複,如果你的業務需要保證嚴格的不重複訊息,需要你自己在業務端去重。 * 線上應該關閉autoCreateTopicEnable,即在設定檔中將其設定為false。。後果就是:以後所有該TOPIC的訊息,都將發送到這台broker上,達不到負載平衡的目的。 * API 3.6.2.Final JAR包是有問題,不推薦使用,目前使用,3.5.9JAR 可以 * 使用3.6.2jar問題是,生產者發送的訊息,消費者消費後,不能把訊息狀態同步給 broken,導致消費者 重啟後,還是會再次受到 訊息,重複消費。 * 叢集消費者= 多個消費者都是 叢集消費者,訂閱了同一個訊息後,broken會把訊息平均分配到 所有叢集消費者 * 不同的叢集消費者(指定了不通的組名),訂閱同一個TOPIC ,都會收到這個topic訊息 * 廣播消費者= 多個廣播消費者 訂閱了同一個訊息後,每個廣播消費者都會 接收到訊息 * 同一個JVM裡面只能存在唯一的 組名+執行個體名 的 生產者,或者是消費者,所以以下的生產者和消費者的執行個體名稱都用uuid 指定 * @author luoyang * */public class RocketMqUtils{ private static Logger _logger = Logger.getLogger(RocketMqUtils.class); private static final Logger _socketLog = Logger.getLogger("SocketMQSendMessage"); private static DefaultMQProducer producer; private static DefaultMQPushConsumer consumer; private static DefaultMQPushConsumer broadConsumer; private static Object obj = null; private static Properties properties = null; private static void initProperties(){ if(null == properties){ properties = new Properties();// String path = RSystemConfig.Home_Path + "socketmq.properties"; String path = "d://socketmq.properties"; properties = PropertiesUtil.getInstance().getProperties(path); _logger.info(" init socketmq.properties success."); } } public static void sendMessage(String topic, String messageContent){ try{ if(RString.isBlank(topic)){ return; } if(null == producer){ getProducer(); } _logger.info("sendMessage topic=" + topic + " messageContent=" + messageContent); _socketLog.info(RString.format("{0}, sendMessage topic={1}, messageContent={2}", RDate.getCurrentTimeStr() + "|", topic, messageContent)); // topic, body Message msg = new Message(topic, messageContent.getBytes()); SendResult sendResult = producer.send(msg); _logger.info("sendMessage sendResult= " + sendResult.toString()); }catch(Exception e){ _socketLog.error(RDate.getCurrentTimeStr() + "| sendMessage topic=" + topic + " messageContent=" + messageContent, e); } } /** * 發送訊息,工具類 * * @param topic * @param messageContent 訊息內容 * @param repeatTime 如果發送失敗,允許重複發送的次數,每次失敗間隔200ms之後繼續發送 */ public static void sendMessage(String topic, String messageContent, int repeatTime){ _logger.info("sendMessage topic=" + topic + " messageContent=" + messageContent); _socketLog.info(RString.format("{0}, sendMessage topic={1}, messageContent={2}", RDate.getCurrentTimeStr() + "|", topic, messageContent)); if(RString.isBlank(topic)){ return; } if(null == producer){ getProducer(); } boolean result = false; for(int i = 1; i <= repeatTime; i++){ try{ Message msg = new Message(topic, messageContent.getBytes()); SendResult sendResult = producer.send(msg); _logger.info("sendMessage sendResult= " + sendResult.toString()); result = true; }catch(Exception e){ e.printStackTrace(); _logger.error("send message have exception" + e + "topic=" + topic + " messageContent=" + messageContent); } _logger.info("sendMessage topic=" + topic + " messageContent=" + messageContent); if(!result){ try{ Thread.sleep(200); }catch(InterruptedException e){ e.printStackTrace(); _logger.error("send message have exception" + e + "topic=" + topic + " messageContent=" + messageContent + "repeat time=" + i); } continue; }else{ break; } } } private static synchronized DefaultMQProducer getProducer(){ if(null != producer){ return producer; } initProperties(); //一個jvm內,具有相同producerGroup名字的生產者執行個體只有一個。 producer = new DefaultMQProducer(RTopicConstants.Product_Group); producer.setNamesrvAddr(properties.getProperty("namesrv_addr")); producer.setInstanceName(RUuid.makeUuid()); try{ // 生產者開啟 producer.start(); // 關閉容器時,關閉生產者 Runtime.getRuntime().addShutdownHook(new Thread(new Runnable(){ public void run(){ _logger.info("getProducer" + ".............producer.shutdown....."); producer.shutdown(); } })); _logger.info("getProducer init product success..data[namesrvAddr={0}]" + properties.getProperty("namesrv_addr")); }catch(MQClientException e){ e.printStackTrace(); _logger.error("getProducer have exception" + e); } return producer; } public static DefaultMQPushConsumer getConsumer(){ if(null != obj){ return consumer; } initConsumer(); return consumer; } public static DefaultMQPushConsumer getBroadConsumer(){ if(null != obj){ return broadConsumer; } initConsumer(); return broadConsumer; } private static synchronized void initConsumer(){ try{ if(null == obj){ initProperties(); consumer = new DefaultMQPushConsumer(RTopicConstants.Consumer_Group); consumer.setNamesrvAddr(properties.getProperty("namesrv_addr")); consumer.setInstanceName(RUuid.makeUuid()); broadConsumer = new DefaultMQPushConsumer(RTopicConstants.Broad_Consumer_Group); broadConsumer.setNamesrvAddr(properties.getProperty("namesrv_addr")); broadConsumer.setMessageModel(MessageModel.BROADCASTING); broadConsumer.setInstanceName(RUuid.makeUuid()); obj = new Object(); _logger.info("intConsumer init consumer success..data[namesrvAddr={0}]" + properties.getProperty("namesrv_addr")); } }catch(Exception e){ e.printStackTrace(); _logger.error("initConsumer have exception" + e); } }}
package com.cat.common.rocketmq;public class RTopicConstants{ /** * 預設叢集消費者 */ public static final String Consumer_Group="ConsumerGroup"; /** * 廣播消費者 */ public static final String Broad_Consumer_Group="BroadConsumerGroup"; /** * 生產者群 */ public static final String Product_Group="ProductGroup"; /** * 預設生產者 */ public static final String Instancename_Producer = "Producer"; /**-------UIC訊息TOPIC-------**/ public static final String luoyang="luoyang";}
生產者
package com.cat.common.rocketmq;import java.util.HashMap;import java.util.Map;import com.cat.common.json.RJson;import com.cat.common.lang.RDate;public class DemoProduct{ public static void main(String[] args){ try{ for(int i = 0; i < 5; i++){ Map<String, Object> m = new HashMap<String, Object>(); m.put("name", i+"1"); m.put("age", 10); m.put("address", i+"杭州市晉江市帶卡"); String msg = RJson.getJson(m); RocketMqUtils.sendMessage(RTopicConstants.luoyang, msg); Thread.sleep(1000); System.out.println(RDate.getCurrentTimeStr()+ ",生產發送訊息"+i); } }catch(Exception e){ // TODO Auto-generated catch block e.printStackTrace(); } }}
消費者
package com.cat.common.rocketmq;import java.util.List;import com.alibaba.rocketmq.client.consumer.DefaultMQPushConsumer;import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;import com.alibaba.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;import com.alibaba.rocketmq.client.consumer.listener.MessageListenerConcurrently;import com.alibaba.rocketmq.client.exception.MQClientException;import com.alibaba.rocketmq.common.message.MessageExt;public class DemoCustom{ public static void initConsumer(){ try{ System.out.println("開啟叢集消費者1:"); DefaultMQPushConsumer consumer = RocketMqUtils.getConsumer(); consumer.subscribe(RTopicConstants.luoyang, "*"); consumer.registerMessageListener(new MessageListenerConcurrently(){ public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context){ try{ MessageExt msg = msgs.get(0); String data = new String(msg.getBody()); if(msg.getTopic().equals(RTopicConstants.luoyang)){ System.out.println("叢集消費者接收到訊息:" + data); } }catch(Exception e){ e.printStackTrace(); //一會之後重新發送,可自行判斷 時候要重試 return ConsumeConcurrentlyStatus.RECONSUME_LATER; } //根據商務邏輯成功處理 返回成功 return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } }); // 開啟消費 consumer.start(); }catch(Exception e){ e.printStackTrace(); } } /** * 註冊廣播模式訊息 * @throws MQClientException */ public static void initBroadConsumer() throws MQClientException{ System.out.println("開啟廣播消費者"); DefaultMQPushConsumer broadConsumer = RocketMqUtils.getBroadConsumer(); broadConsumer.subscribe(RTopicConstants.luoyang, "*"); broadConsumer.registerMessageListener(new MessageListenerConcurrently(){ public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context){ try{ MessageExt msg = msgs.get(0); String data = new String(msg.getBody()); // 資料解析 // 同步配置中心資料 if(msg.getTopic().equals(RTopicConstants.luoyang)){ System.out.println("廣播消費者接收到訊息:" + data); } }catch(Exception e){ e.printStackTrace(); return ConsumeConcurrentlyStatus.RECONSUME_LATER; } return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } }); broadConsumer.start(); } public static void main(String[] args){ try{ initConsumer(); initBroadConsumer(); }catch(Exception e){ e.printStackTrace(); } }}
對於生產者
//一個jvm內,具有相同producerGroup名字的生產者執行個體只有一個。 producer = new DefaultMQProducer(RTopicConstants.Product_Group); producer.setNamesrvAddr(properties.getProperty("namesrv_addr")); producer.setInstanceName(RUuid.makeUuid());
同一個JVM 內 組名+InstanceName 必須唯一。
否則會報錯。所以我這裡設定的instanceName 是uuid
對於消費者
有兩種,一種是廣播,一種是叢集
廣播模式的消費者 訊息不會儲存在broken裡面,直接下發到消費者中,所有的廣播消費者都會消費訊息。
叢集模式,如果一組消費者的groupname 一致,就定義為是同一個消費叢集。必須保證一台JVM 裡面 消費者的instanceName 是唯一的。
同一組的叢集消費者 平均消費 訊息
並且,如果是叢集消費,那麼叢集消費的所有消費者必須訂閱相同的topic,否則會報錯