RabbitMQ服务之发布/订阅篇

来源:互联网 发布:linux 占用内存 脚本 编辑:程序博客网 时间:2024/05/20 16:01

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(新))讨论,谢谢。

 

 

1 0