RabbitMQ服務之發佈/訂閱篇
相關聲明:
1、轉載請標明出處:http://blog.csdn.net/why_2012_gogo/article/details/53995557
2、本系列文章均來自於實際項目、官網、網絡資源整理而來,並自己進行修改、優化及調試,內容僅供參考;
在上一篇文章中,我們實現了RabbitMQ的應答、任務轉發數及隊列和消息的持久化操作,也就是一個消息只能發送給一個消費者,除非該消費者因意外被停掉後,纔會轉發給其它工作者,不清楚的同學可以回頭看下《RabbitMQ服務之任務分發篇》;而在這裏要介紹的是將消息發送給多個消費者,有點類似廣播機制(觀察者模式),也就是在第一篇文章《RabbitMQ入門篇》中介紹的轉發器,不清楚的讀者可以去瀏覽。我們這裏要說的是Fanout機制轉發器,具體請往下俯看!
l 轉發器
l 例子
1、轉發器
在RabbitMQ中,消息發送首先是發送到轉發器,然後通過相應的轉發器機制將消息轉發到指定隊列,最後接收者直接從所綁定的隊列中獲取符合條件的消息。有人會問,前面的文章中沒有使用轉發器,怎麼也可以發送消息?答案是,它們也使用了轉發器。在RabbitMQ中,有匿名轉發器的概念存在,也就是””所代表的轉發器,具體如下:
channel.basicPublish("","queue_name",
MessageProperties.PERSISTENT_TEXT_PLAIN,"message".getBytes());
如上所示,第一個參數爲轉發器的名字,如果第二個參數(routingKey)存在,就由其決定將消息轉發到哪個隊列;如果不存在,那麼就使用臨時隊列(routingKey),此時消息轉發到哪個臨時隊列,由轉發器決定。
2、例子
這裏我們以用戶的操作記錄錄入爲例,來演示說明Fanout轉發器的使用。例子是這樣的,我們開啓兩個消息接受者,一個消息發送者發送消息,而兩個消息接受者都可以接收到發送的消息,一個接受者負責記錄文件內容到記錄文件中,另一個接受者則負責顯示操作記錄到控制檯。
A、基類
BaseObject.java:
publicclassBaseObject {
staticLogger log = LogManager.getLogger(BaseObject.class.getSimpleName());
publicstatic void debug(String debug) {
log.debug(debug);
}
publicstatic void debug(Object obj) {
if(null!= obj)
log.debug(obj.toString());
}
publicstatic void error(String error) {
log.error(error);
}
}
該類爲所有對象繼承的基類,其中LogManager爲Java中的Log4j的使用,這裏不做相關介紹,請讀者查閱相關資料。
BaseConnector.java:
publicclass BaseConnector extends BaseObject {
protectedChannel channel;
protectedConnection connection;
publicBaseConnector()throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory(); //新建鏈接工廠並綁定主機地址
factory.setHost("127.0.0.1"); //設置MabbitMQ所在主機ip或者主機名
connection = factory.newConnection(); //創建連接
channel = connection.createChannel(); //創建頻道
}
}
protectedvoid close() { //關閉頻道及鏈接
try {
channel.close();
connection.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
B、發送端
PublisherHandle.java:
publicclass PublisherHandler extends BaseConnector {
public PublisherHandler()throws IOException,TimeoutException {
super();
}
publicvoid sendMessage(MessageInfo messageInfo,String exchangeName,String exchangeType) {
try{
//聲明轉發
channel.exchangeDeclare(exchangeName,exchangeType);
//給轉發器,發送消息
channel.basicPublish(exchangeName,"",null, SerializationUtils.serialize(messageInfo));
}catch (IOException e) {
debug("RabbitMQSend Message Error:"+e.getMessage());
}
}
publicvoid close() {
super.close();
}
}
C、接收端
ReceiverHandle.java:
publicclass ReceiverHandler extends BaseConnector implements Runnable,Consumer {
private MessageInfo messageInfo =new MessageInfo();
private int hashCode;
private String exchangeName; // 轉發器名字
private Stirng exchangeType; // 轉發器類型
privatevolatile Thread thread;
public ReceiverHandler(String exchangeName,String exchangeType)throwsIOException, TimeoutException {
this.exchangeName = exchangeName;
this. exchangeType = exchangeType;
super();
}
publicvoid receiveMessage() {
hashCode = Thread.currentThread().hashCode(); //區分不同工作進程的輸出
try{
debug(hashCode+ " [*] Waiting for messages Receiving...");
//不存在隊列時創建臨時隊列此時隊列須要爲非持久化類型(含消息)
String queueName = channel.queueDeclare().getQueue();
//聲明轉發器
channel.exchangeDeclare(exchangeName,exchangeType);
// fanout
channel.queueBind(queueName,exchangeName, "");
//指定消費隊列(是否開啓應答模式:默認關閉)
Stringop_result = channel.basicConsume(queueName, true,this);
if("".equals(op_result)){
debug("BasicConsumeConfig Consumer Queue Error!");
}
}catch (IOException e) {
debug("ConsumerDelivery Error,Msg info:" + e.getMessage());
}catch (Exception e) {
debug("ErrorIs Opening,Msg info:" + e.getMessage());
}
}
@Override
publicvoid handleCancel(String arg0)throws IOException {
debug("===handleCancel==="+arg0);
}
@Override
publicvoid handleCancelOk(String arg0) {
debug("===handleCancelOk==="+arg0);
}
@Override
publicvoid handleConsumeOk(String consumeOk) {
}
@Override
publicvoid handleDelivery(String consumerTag, Envelope env,
BasicPropertiesprops, byte[] body) throws IOException {
messageInfo= (MessageInfo) SerializationUtils.deserialize(body);
messageInfo.setHashCode(hashCode);
// 下面兩個在單個接收端只出現一次,這裏都羅列是因爲兩個接收端只有此處代碼不同,其它地方均相同,只是爲了簡略代碼而已
// 記錄操作信息到文件或數據庫
recordToFile(msgInfo.getContent());
//顯示操作信息到控制檯或系統
recordToConsole(msgInfo.getContent());
}
@Override
publicvoid handleRecoverOk(String arg0) {
debug("===handleRecoverOk==="+arg0);
}
@Override
publicvoid handleShutdownSignal(String arg0, ShutdownSignalException arg1) {
debug("===handleShutdownSignal==="+arg0+"===ShutdownSignalException==="+arg1.getMessage());
}
@Override
publicvoid run() {
receiveMessage();
}
publicvoid start() {
if(thread == null){
thread = new Thread(this);
thread.start();
}
}
}
recordToFile方法:
//記錄操作到文件
voidrecordToFile(String msg) {
FileOutputStream out = null;
try{
StringlocalDir = AppUnit.class.getClassLoader().getResource("").getPath();
StringopFileName = "操作記錄【" +new SimpleDateFormat("yyyy-MM-dd").format(newDate())+"】"+".txt";
Filefile = new File(localDir,opFileName);
out= new FileOutputStream(file,true);
out.write((msg+"\r\n").getBytes());
out.flush();
out.close();
}catch(Exception e) {
e.printStackTrace();
}finally {
try{
out.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
recordToConsole方法:
//記錄顯示到控制檯
voidrecordToConsole(Stringmsg) {
debug("【記錄操作內容】" + msg);
}
D、測試入口
publicstatic void main(String[] args){
PublisherHandler publisher = null;
ReceiverHandler receiver = null;
ReceiverHandler receiver2 = null;
try{
receiver= new ReceiverHandler("exchange_fanout","fanout"); //接收者1
ThreadreceiverThread = new Thread(receiver);
receiverThread.start();
receiver2= new ReceiverHandler("exchanage_fanout","fanout"); //接收者2
ThreadreceiverThread2 = new Thread(receiver2);
receiverThread2.start();
publisher= new PublisherHandler(); //發送者
for(inti=0;i<5;i++) {
MessageInfo msgInfo = new MessageInfo();
String message = "【記錄操作】" + "新增訂單【記錄編號】"+ (i+1) + " 【記錄日期】"+new SimpleDateFormat("yyyy-MM-dd").format(newDate());
msgInfo.setConsumerTag("demo2_tag"); //回調標誌
msgInfo.setChannel("demo2"); //頻道
msgInfo.setContent(message); //消息內容
publisher.sendMessage(msgInfo,"exchange_fanout","fanout");
}
}catch (IOException | TimeoutException e) {
e.printStackTrace();
}finally {
publisher.close();
}
}
E、結果顯示
保存記錄接收端:
打印控制接收端:
消息隊列RabbitMQ的發佈訂閱就介紹到這裏,由於作者水平有限,如有問題請在評論中發言或是QQ羣(245389109(新))討論,謝謝。