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