RabbitMQ学习之发布/订阅/广播-实现多消费者(java)

模拟发布订阅模式,一个消息发给多个消费者。实现一个发送日志,一个接收者将接收到的数据写到硬盘上,与此同时,另一个接收者把接收到的消息展现在屏幕上。

转发器类型使用:fanout。fanout类型转发器特别简单,把所有它介绍到的消息,广播到所有它所知道的队列。

直接上代码了:


首先配置好ribbit

package com.cnjy.ecampus.ribbitmq;
import java.io.IOException;


import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;


import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.QueueingConsumer;


@Configuration
public class RabbitConfig {


    @Bean(name="firstConnectionFactory")
    @Primary
    public ConnectionFactory firstConnectionFactory(
        @Value("${spring.rabbitmq.first.host}") String host, 
        @Value("${spring.rabbitmq.first.port}") int port,
        @Value("${spring.rabbitmq.first.username}") String username,
        @Value("${spring.rabbitmq.first.password}") String password
        ) throws IOException{
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost(host);
        connectionFactory.setPort(port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        org.springframework.amqp.rabbit.connection.Connection connection = connectionFactory.createConnection();
        Channel channel = connection.createChannel(false);
        /** 
         * 声明转发器和类型 可用的转发器类型Direct Topic Headers Fanout 参考:http://melin.iteye.com/blog/691265 
         * Direct Exchange – 处理路由键。需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。 
         * Fanout Exchange – 不处理路由键。你只需要简单的将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。 
         * 很像子网广播,每台子网内的主机都获得了一份复制的消息。Fanout交换机转发消息是最快的。  
         * Topic Exchange – 将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。 
         * 因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。 
         */  
        channel.exchangeDeclare("schoolClassMsg", "fanout");  
        //channel.queueBind("schoolClassMsg", "schoolClassMsg", "");
        
        //channel.queueDeclare("schoolClassMsg", true, false, false, null);
        System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
        //channel.basicQos(2);
//        QueueingConsumer consumer = new QueueingConsumer(channel);
//        channel.basicConsume("", false, consumer);
        return connectionFactory;
    }


//    @Bean(name="secondConnectionFactory")
//    public ConnectionFactory secondConnectionFactory(
//        @Value("${spring.rabbitmq.second.host}") String host, 
//        @Value("${spring.rabbitmq.second.port}") int port,
//        @Value("${spring.rabbitmq.second.username}") String username,
//        @Value("${spring.rabbitmq.second.password}") String password
//        ){
//        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
//        connectionFactory.setHost(host);
//        connectionFactory.setPort(port);
//        connectionFactory.setUsername(username);
//        connectionFactory.setPassword(password);
//        return connectionFactory;
//    }


    @Bean(name="firstRabbitTemplate")
    @Primary
    public RabbitTemplate firstRabbitTemplate(
@Qualifier("firstConnectionFactory") ConnectionFactory connectionFactory
){
        RabbitTemplate firstRabbitTemplate = new RabbitTemplate(connectionFactory);
        return firstRabbitTemplate;
    }


//    @Bean(name="secondRabbitTemplate")
//    public RabbitTemplate secondRabbitTemplate(
//        @Qualifier("secondConnectionFactory") ConnectionFactory connectionFactory
//        ){
//        RabbitTemplate secondRabbitTemplate = new RabbitTemplate(connectionFactory);
//        return secondRabbitTemplate;
//    }


    @Bean(name="firstFactory")
    public SimpleRabbitListenerContainerFactory firstFactory(
        SimpleRabbitListenerContainerFactoryConfigurer configurer,
        @Qualifier("firstConnectionFactory") ConnectionFactory connectionFactory        
        ) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setPrefetchCount(10);  
        factory.setConcurrentConsumers(50);  
        factory.setChannelTransacted(true);//
        configurer.configure(factory, connectionFactory);
        return factory;
    }


//    @Bean(name="secondFactory")
//    public SimpleRabbitListenerContainerFactory secondFactory(
//        SimpleRabbitListenerContainerFactoryConfigurer configurer,
//        @Qualifier("secondConnectionFactory") ConnectionFactory connectionFactory           
//        ) {
//        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
//        configurer.configure(factory, connectionFactory);
//        return factory;
//    }
//
//    @Bean
//    public Queue firstQueue() {
//        System.out.println("configuration firstQueue ........................");
//        return new Queue("hello1");
//    }
//
//    @Bean
//    public Object secondQueue() {
//        System.out.println("configuration secondQueue ........................");
//        return new Queue("hello2");
//    }

}


发送消息(此处发送权限可忽略,应为个人项目需求):

package com.cnjy.ecampus.ribbitmq;


import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;


import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;


import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;


import com.cnjy.ecampus.framework.util.LogUtils;
import com.cnjy.ecampus.framework.util.PropertiesUtil;
import com.cnjy.ecampus.modules.um.domain.AuthClient;
import com.cnjy.ecampus.modules.um.domain.SchoolApp;
import com.cnjy.ecampus.modules.um.domain.User;
import com.cnjy.ecampus.modules.um.mapper.AuthClientMapper;
import com.cnjy.ecampus.modules.um.mapper.SchoolAppMapper;
import com.google.gson.Gson;


/**
 * 消息管理
 *
 * @author zj
 * 
 * 2017年10月18日
 */
@RestController
public class RibbitManage  implements RabbitTemplate.ConfirmCallback  {
// @Autowired
// private AmqpTemplate template;

@Resource(name = "firstRabbitTemplate")
private RabbitTemplate firstTemplate;
@Resource
private AuthClientMapper authClientMapper;
@Resource
private HttpServletRequest request;
@Resource
private SchoolAppMapper schoolAppMapper;
// private static final String QUEUE = "queue";
// public static String queueName;

// public static Sender sender;
// static {
// try {
// sender = new Sender("schoolClassMsg");//给默认queue
// } catch (Exception e) {
//
// }
// }

/**
* sendMap  需要发送的map ,msg发送的消息提示  ,sendtype => add 添加 ,update 修改 ,delete 删除 ,queue 队列名
* @return data 改变的数据 ,message 改变的提示信息 , sendType 类型  =>1 添加 2 修改 3 删除  ,type=> 1(说明修改或添加的是班级) ,2 (说明是学生),3 teacher...
* zj
* 2017年10月18日
*/
public boolean sendMsg(Map map,String msg, String sendType, String type, String queueName) {
    Map returnMap = new HashMap();
    try {
//    String oldQueueName = ((sender.queueName == null)?"":sender.queueName);
//    if (!queueName.equals(oldQueueName)) {
//    sender = new Sender(queueName);
//    }
//    LogUtils.debug("oldQueueName" + oldQueueName);
//    System.out.println(oldQueueName);
    boolean isSend = validateSendAuth();//是否有发送消息权限
    if (!isSend) {
    LogUtils.debug("ribbitMesg不存在发送消息权限!");
    //System.out.println("不存在发送消息权限!");
    return false;
   
returnMap.put("code", 0);
returnMap.put("data", map);
returnMap.put("message", msg);
String json = new Gson().toJson(returnMap);
LogUtils.debug("同步的json: " + json);
//System.out.println("同步的json: " + json);
// sender.sendMessageStr(json);
// firstTemplate.convertAndSend("schoolClassMsg", json);//转发至指定队列
firstTemplate.convertAndSend("schoolClassMsg","", json);//转发至交换器
// template.convertAndSend(queueName, json);
//sender.sendMessageStr( "{\"code\":0,\"data\":\"aaaaa\"}");
} catch (Exception e) {
e.printStackTrace();

LogUtils.debug("发送消息 捕获到的异常" + e);

扫描二维码关注公众号,回复: 1890054 查看本文章

                        //此处发送消息失败,可将 失败的信息存储起来添加日志处理

return false;
}
        return true;
    }

// @RabbitListener(queues = "schoolClassMsg", containerFactory = "rabbitListenerContainerFactory")
//   @RabbitListener(queues="schoolClassMsg")
// @RabbitListener(queues = "schoolClassMsg", containerFactory = "firstFactory")
// public void processC(String str) {
// System.out.println("ReceiveC:"+str);
// }
  
/**
* 验证发送消息权限
* zj
* 2017年11月1日
* @return true 说明 能够发送消息
*/
public boolean validateSendAuth() {
User user = getLoginUser();
if (user == null) {
return false;
}
//获取 认证所有认证
List<AuthClient> authClient = authClientMapper.getAuthAll();
if (PropertiesUtil.isEmptyList(authClient) ) {
return false;
}
//获取所有学校所有有开通的 app
List<SchoolApp> listSA = schoolAppMapper.queryAppList(user.getSchoolId());
if (PropertiesUtil.isEmptyList(listSA) ) {
return false;
}

//执行 看是否存在 发送消息 权限
for (int i = 0; i < authClient.size(); i++) {
AuthClient AC = authClient.get(i);
if (AC == null) {
continue ;
}
String clientType = AC.getClientType();
for (int j = 0; j < listSA.size(); j++) {
SchoolApp SA = listSA.get(j);
if (SA == null) {
continue ;
}
String appId = SA.getAppId();
if (appId.equals(clientType)) {
return true;
}
}
}

return false;
}

public User getLoginUser() {
User loginUser = null;
HttpSession session = request.getSession();
Object objUser = session.getAttribute("loginUser");
    if(objUser==null){
    throw new NullPointerException("登录已过期,请重新登录!");
    }else{
    loginUser = (User)objUser;
    }
    return loginUser;
}


@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println(" 回调id:" + correlationData);  
        if (ack) {  
            System.out.println("消息成功消费");  
        } else {  
            System.out.println("消息消费失败:" + cause);  
        }  
}
}


消费者1:

public static void main(String[] args) throws Exception {  
  String EXCHANGE_NAME = "schoolClassMsg";
      // 创建连接和频道  
      ConnectionFactory factory = new ConnectionFactory();  
      factory.setHost("192.168.2.46");  
      // 指定用户 密码  
      factory.setUsername("administrator");  
      factory.setPassword("123456");  
      // 指定端口  
      factory.setPort(AMQP.PROTOCOL.PORT);  
      Connection connection = factory.newConnection();  
      Channel channel = connection.createChannel();  
      /** 
       * 声明转发器和类型 可用的转发器类型Direct Topic Headers Fanout 
       * Direct Exchange – 处理路由键。需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。 
       * Fanout Exchange – 不处理路由键。你只需要简单的将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。 
       * 很像子网广播,每台子网内的主机都获得了一份复制的消息。Fanout交换机转发消息是最快的。  
       * Topic Exchange – 将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。 
       * 因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。 
       */  
      channel.exchangeDeclare(EXCHANGE_NAME, "fanout");  
      //创建一个非持久的、唯一的且自动删除的队列且队列名称由服务器随机产生一般情况这个名称与amq.gen-JzTY20BRgKO-HjmUJj0wLg 类似。  
      String queueName = channel.queueDeclare().getQueue();  
      System.out.println("====queueName===" + queueName);
//      String queueName = "schoolClassMsg";  
      // 为转发器指定队列,设置binding  
      channel.queueBind(queueName, EXCHANGE_NAME, "");  


      System.out.println(" [*] Waiting for messages. To exit press CTRL+C");  


      QueueingConsumer consumer = new QueueingConsumer(channel);  
      // 指定接收者,第二个参数为自动应答,无需手动应答  
      channel.basicConsume(queueName, true, consumer);  


      while (true) {  
          QueueingConsumer.Delivery delivery = consumer.nextDelivery();  
          String message = new String(delivery.getBody());  


          System.out.println("=======" + message);
      }  

  }  


消费者2:

public static void main(String[] args) throws Exception {  
  String EXCHANGE_NAME = "schoolClassMsg";
  // 创建连接和频道  
      ConnectionFactory factory = new ConnectionFactory();  
      factory.setHost("192.168.2.46");  
      // 指定用户 密码  
      factory.setUsername("administrator");  
      factory.setPassword("123456");
      // 指定端口  
      factory.setPort(AMQP.PROTOCOL.PORT);  
      Connection connection = factory.newConnection();    
      Channel channel = connection.createChannel();    
  
      channel.exchangeDeclare(EXCHANGE_NAME, "fanout");    
      // 创建一个非持久的、唯一的且自动删除的队列且队列名称由服务器随机产生一般情况这个名称与amq.gen-JzTY20BRgKO-HjmUJj0wLg 类似。  
      String queueName = channel.queueDeclare().getQueue();  
//      String queueName = "schoolClassMsg";  
      System.out.println("====queueName===" + queueName);
      // 为转发器指定队列,设置binding    
      channel.queueBind(queueName, EXCHANGE_NAME, "");    
  
      System.out.println(" [*] Waiting for messages. To exit press CTRL+C");    
  
      QueueingConsumer consumer = new QueueingConsumer(channel);    
      // 指定接收者,第二个参数为自动应答,无需手动应答    
      channel.basicConsume(queueName, true, consumer);    
  
      while (true)    
      {    
          QueueingConsumer.Delivery delivery = consumer.nextDelivery();    
          String message = new String(delivery.getBody());    
          System.out.println(" [x] Received '" + message + "'");    
  
      }    
  }  


java 技术交流群:317628801




猜你喜欢

转载自blog.csdn.net/qq_33545491/article/details/80841123