RabbitMQ与SpringMVC集成并实现发送消息和接收消息(持久化)方案一

RabbitMQ是用于应用程序之间或者程序的不同组件之间的消息通信,进行了异步处理,而这种异步处理的方式大大的节省了服务器的请求响应时间,从而提高了系统的吞吐量,也就是生产-消费模型,一端往消息队列中不断写入消息,而另一端则可以读取或者订阅队列中的消息。

 RabbitMQ   设置持久化, 如果生产端发送消息,消费端突然挂掉了,消息还存在队列,等消费端重启了,消费端能获取到消息。

RabbitMQ的两大核心组件是Exchange和Queue。

 

说明:

  Exchange又称交换器,它接受消息和路由信息,然后将消息发送给消息队

列。

 

 Queue是一个具名缓冲区,它们代表一组消费者应用程序保存消息。

 

 接下来介绍Producer 和 Consumer 两种类型

1.生产者

   第一步:实现消息类,主要是保存调用哪个路由key和交换器(也是走哪条线)、要传的数据

[java]  view plain  copy
  1. /** * 消息 * */  
  2. public class RabbitMessage implements Serializable {  
  3.     private static final long serialVersionUID = -6487839157908352120L;  
  4.     private Class<?>[] paramTypes;// 参数类型  
  5.     private String exchange;// 交换器  
  6.     private Object[] params;  
  7.     private String routeKey;// 路由key  
  8.   
  9.     public RabbitMessage() {  
  10.     }  
  11.   
  12.     public RabbitMessage(String exchange, String routeKey, Object... params) {  
  13.         this.params = params;  
  14.         this.exchange = exchange;  
  15.         this.routeKey = routeKey;  
  16.     }  
  17.   
  18.     @SuppressWarnings("rawtypes")  
  19.     public RabbitMessage(String exchange, String routeKey, String methodName,  
  20.             Object... params) {  
  21.         this.params = params;  
  22.         this.exchange = exchange;  
  23.         this.routeKey = routeKey;  
  24.         int len = params.length;  
  25.         Class[] clazzArray = new Class[len];  
  26.         for (int i = 0; i < len; i++)  
  27.             clazzArray[i] = params[i].getClass();  
  28.         this.paramTypes = clazzArray;  
  29.     }  
  30.   
  31.     public byte[] getSerialBytes() {  
  32.         byte[] res = new byte[0];  
  33.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  34.         ObjectOutputStream oos;  
  35.         try {  
  36.             oos = new ObjectOutputStream(baos);  
  37.             oos.writeObject(this);  
  38.             oos.close();  
  39.             res = baos.toByteArray();  
  40.         } catch (IOException e) {  
  41.             e.printStackTrace();  
  42.         }  
  43.         return res;  
  44.     }  
  45.   
  46.     public String getRouteKey() {  
  47.         return routeKey;  
  48.     }  
  49.   
  50.     public String getExchange() {  
  51.         return exchange;  
  52.     }  
  53.   
  54.     public void setExchange(String exchange) {  
  55.         this.exchange = exchange;  
  56.     }  
  57.   
  58.     public void setRouteKey(String routeKey) {  
  59.         this.routeKey = routeKey;  
  60.     }  
  61.   
  62.     public Class<?>[] getParamTypes() {  
  63.         return paramTypes;  
  64.     }  
  65.   
  66.     public Object[] getParams() {  
  67.         return params;  
  68.     }  
  69. }  

 第二步:实现生产者前提,是要设置调用安装RabbitMQ的IP、端口、线程数、交换器类型等

                配置一个global.properties文件

    

   通过SpringMvcglobal.properties文件读进来

  

[java]  view plain  copy
  1. <!-- 注入属性文件 -->  
  2.     <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
  3.         <property name="locations">  
  4.             <list>  
  5.                 <value>classpath:global.properties</value>  
  6.             </list>  
  7.         </property>  
  8.     </bean>  

第三步:实现生产者类,这里面主要用到的技术有java.util.concurrent.Executors(上一篇有介绍过)实现线程执行

     1)实现连接管理

[java]  view plain  copy
  1. /** * 连接管理 * */  
  2. public class ConnectionManage {  
  3.   
  4.     private volatile Connection connection;  
  5.   
  6.     public ConnectionManage(String rmqServerIP, int rmqServerPort)  
  7.             throws IOException {  
  8.         ConnectionFactory cf = new ConnectionFactory();  
  9.         cf.setHost(rmqServerIP);  
  10.         cf.setPort(rmqServerPort);  
  11.         connection = cf.newConnection();  
  12.     }  
  13.   
  14.     @SuppressWarnings("finally")  
  15.     public Channel createChannel() {  
  16.         Channel channel = null;  
  17.         try {  
  18.             channel = connection.createChannel();  
  19.         } catch (ShutdownSignalException e1) {  
  20.         } catch (IOException e) {  
  21.         }  
  22.         return channel;  
  23.     }  
  24.   
  25.     public void shutdown() throws IOException {  
  26.         if (connection != null)  
  27.             connection.close();  
  28.     }  
  29. <pre name="code" class="java">  
 
 

这边可以设置监听,是否连接断掉connection.addShutdownListener(shutdoenListner);//如果断掉,可以继续连接

  2)实现生产者

     在SpringMVC配置文件XML中加入,把global.properties文件读出来并设置值

[java]  view plain  copy
  1. <bean id="rmqProducer" class="cn.test.rabbitmq.RmqProducer">  
  2.     <property name="rmqServerIP" value="${rmq.ip}" />  
  3.     <property name="rmqServerPort" value="${rmq.port}" />  
  4.     <property name="threadPoolNum" value="${rmq.producer.num}" />  
  5.     <property name="exchange" value="testExchange" />  
  6.     <property name="exchangeType" value="topic" />  
  7. </bean>  

[java]  view plain  copy
  1. /** 
  2.  * 生产着 
  3.  * 
  4.  */  
  5. public class RmqProducer implements InitializingBean,DisposableBean  
  6. {  
  7.       
  8.     private String rmqServerIP;//ip地址  
  9.     private int rmqServerPort;//端口    
  10.     private int threadPoolNum;//线程数  
  11.     private String exchangeType;//类型  
  12.     private String exchange;//交换器     
  13.     private ConnectionManage connectManage;  
  14.     private Channel channel;                  
  15.   
  16.       
  17.       
  18.   
  19.       
  20.     /** 
  21.      * 初始化 
  22.      */  
  23.     @Override  
  24.     public void afterPropertiesSet() throws Exception   
  25.     {  
  26.         //创建连接管理器  
  27.         connectManage=new ConnectionManage(rmqServerIP,rmqServerPort);  
  28.         boolean durable=true;//是否持久化  
  29.         boolean autoDelete=false;//是否自动删除  
  30.         Channel channel=connectManage.createChannel();  
  31.         channel.exchangeDeclare(exchange, exchangeType, durable,autoDelete,null);  
  32.           
  33.     }  
  34.       
  35.       
  36.       
  37.       
  38.       
  39.       
  40.     /** 
  41.      * 发送信息 
  42.      * @param msg 
  43.      */  
  44.     public void sendMessage(final RabbitMessage  msg)  
  45.     {  
  46.           
  47.           
  48.         channel.basicPublish(msg.getExchange(),msg.getRouteKey(),MessageProperties.PERSISTENT_TEXT_PLAIN,msg.getSerialBytes());  
  49.   
  50.     }  
  51.   
  52.     /** 
  53.      *  
  54.      * @throws Exception 
  55.      */  
  56.     @Override  
  57.     public void destroy() throws Exception   
  58.     {  
  59.         connectManage.shutdown();  
  60.     }  
  61.   
  62.     public String getRmqServerIP() {  
  63.         return rmqServerIP;  
  64.     }  
  65.   
  66.     public void setRmqServerIP(String rmqServerIP) {  
  67.         this.rmqServerIP = rmqServerIP;  
  68.     }  
  69.   
  70.   
  71.       
  72.   
  73.     public String getExchangeType() {  
  74.         return exchangeType;  
  75.     }  
  76.   
  77.     public void setExchangeType(String exchangeType) {  
  78.         this.exchangeType = exchangeType;  
  79.     }  
  80.   
  81.     public int getRmqServerPort() {  
  82.         return rmqServerPort;  
  83.     }  
  84.   
  85.     public void setRmqServerPort(int rmqServerPort) {  
  86.         this.rmqServerPort = rmqServerPort;  
  87.     }  
  88.   
  89.   
  90.   
  91.   
  92.     public String getExchange() {  
  93.         return exchange;  
  94.     }  
  95.   
  96.   
  97.   
  98.   
  99.     public void setExchange(String exchange) {  
  100.         this.exchange = exchange;  
  101.     }  
  102.   
  103.       
  104. }  

说明:

  1). channel.exchangeDeclare(exchange, exchangeType, durable,autoDelete,null);

    exchange:交换机名字 

    exchangeType类型 

    durable是否持久化 

    autoDelete不使用时是否自动删除

  2).channel.basicPublish(msg.getExchange(),msg.getRouteKey(),MessageProperties.PERSISTENT_TEXT_PLAIN,msg.getSerialBytes());

   

     exchange:交换机名字 

     routeKey:路由关键字 

     msg.getSerialBytes() :消息主体  

 

   Channel是线程好全的,但是最好是每个线程里用自己的Channel,因为在单个Channel里排队是有可能慢一些的。所以我们可以采用多线程处理,每个线程对应Channel,这样速度会比较快,具体实现:

  

    java.util.concurrent.ExecutorService多线程的管理和实现,上一篇有介绍

    ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术

   

          //每个线程对应Channel
[java]  view plain  copy
  1.              //启动线程池  
  2. channelManager=new ConcurrentHashMap<Thread, Channel>();  
  3. threadPool=Executors.newFixedThreadPool(threadPoolNum, new ThreadFactory(){  
  4.     @Override  
  5.     public Thread newThread(Runnable r)   
  6.     {  
  7.         Thread thread=new Thread(r);  
  8.         Channel channel = connectManage.createChannel();  
  9.         if(channel!=null)  
  10.             channelManager.put(thread, channel);//创建线程和channel对应起来  
  11.         return thread;  
  12.     }             
  13. });  

       //采用自己的Channel来发送消息

[java]  view plain  copy
  1.                Runnable runnable=new Runnable() {  
  2.     @Override  
  3.     public void run()   
  4.     {  
  5.         Thread thread=Thread.currentThread();  
  6.         Channel channel=channelManager.get(thread);  
  7.         if(channel!=null)  
  8.             channelManager.put(thread, channel);  
  9.         try {  
  10.             channel.basicPublish(msg.getExchange(),msg.getRouteKey(),MessageProperties.PERSISTENT_TEXT_PLAIN,msg.getSerialBytes());  
  11.         } catch (IOException e) {  
  12.             e.printStackTrace();  
  13.         }  
  14.     }  
  15. };  
  16.   
  17. threadPool.execute(runnable);  

测试类:

   

[java]  view plain  copy
  1. @Test  
  2. public void test() throws IOException  
  3. {  
  4.   
  5.         String exchange="testExchange";交换器  
  6.         String routeKey="testQueue";//队列  
  7.         //参数  
  8.         Map<String,Object> param=new HashMap<String, Object>();  
  9.         param.put("data","hello");  
  10.           
  11.         RabbitMessage  msg=new RabbitMessage(exchange,routeKey, param);  
  12.         //发送消息  
  13.         rmqProducer.sendMessage(msg);  
  14.           
  15. }  

     





 2.消费者

    采用多线程进行处理消息,这样每个线程对应Channel,处理速度会比较快。

     在SpringMVC配置文件XML中加入,把global.properties文件读出来并设置值

            

[java]  view plain  copy
  1. <bean id="consumer" class="cn.test.rabbitmq.RmqConsumerSerial">  
  2.     <property name="rmqServerIp" value="${rmq.ip}"></property>  
  3.     <property name="rmqServerPort" value="${rmq.port}"/>  
  4.     <property name="exchange" value="testExchange"></property>  
  5.     <property name="threadPoolNum" value="${rmq.producer.num}"/>  
  6.     <property name="queueName" value="testQueue"></property>  
  7.     <property name="exchangeType" value="topic"/>  
  8.     <property name="qos" value="1"></property>  
  9. </bean>  

   实现消费者

     

[java]  view plain  copy
  1. @Override  
  2.     public void afterPropertiesSet() throws Exception   
  3.     {  
  4.         start();  
  5.     }  
  6.       
  7.     @Override  
  8.     public void destroy() throws Exception   
  9.     {  
  10.         stop();  
  11.     }  
  12.       
  13.     public void start() throws IOException  
  14.     {  
  15.         connectManage=new ConnectionManage(rmqServerIp,rmqServerPort,threadPoolNum);      
  16.                   
  17.         //向rmq创建exchange,queue  
  18.         boolean durable=true,exclusive=false,autoDelete=false;  
  19.         Channel channel=connectManage.createChannel();  
  20.         channel.exchangeDeclare(exchange, exchangeType, durable,autoDelete,null);  
  21.         channel.queueDeclare(queueName, durable, exclusive, autoDelete, null);  
  22.         channel.queueBind(queueName, exchange, routeKey);  
  23.         channel.close();  
  24.                   
  25.         //启动线程池  
  26.         channelManager=new HashMap<Thread, Channel>();  
  27.         threadPool=Executors.newFixedThreadPool(threadPoolNum, new ThreadFactory(){  
  28.             @Override  
  29.             public Thread newThread(Runnable r)   
  30.             {  
  31.                 Thread thread=new Thread(r);  
  32.                 try {  
  33.                     Channel channel = connectManage.createChannel();  
  34.                     if(channel!=null)  
  35.                     {  
  36.                         channelManager.put(thread, channel);  
  37.                         channel.basicQos(qos);  
  38.                     }  
  39.                 } catch (IOException e) {  
  40.                     logger.warn(e.getMessage());                      
  41.                 }  
  42.                 return thread;  
  43.             }             
  44.         });  
  45.   
  46.         for(int i=0;i<threadPoolNum;i++)  
  47.             threadPool.execute(getRunable());  
  48.     }  
  49.       
  50.     protected  Runnable getRunable(){  
  51.           
  52.         return new Runnable() {  
  53.             @Override  
  54.             public void run()   
  55.             {  
  56.                 Thread thread=Thread.currentThread();  
  57.                 final Channel channel=channelManager.get(thread);  
  58.                 boolean autoAck=false;  
  59.                 DefaultConsumer consumer =  new DefaultConsumer(channel) {  
  60.                      @Override  
  61.                      public void handleDelivery(String consumerTag,  
  62.                                                 Envelope envelope,  
  63.                                                 AMQP.BasicProperties properties,  
  64.                                                 byte[] body)  
  65.                          throws IOException  
  66.                          {  
  67.                             long deliveryTag = envelope.getDeliveryTag();  
  68.                             boolean suc=false;  
  69.                             ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(body));                              
  70.                             try {  
  71.                                 Object obj=ois.readObject();  
  72.                                 RabbitMessage rmqMsg = RabbitMessage.class.cast(obj);         
  73.                                 Object[] objs=rmqMsg.getParams();  
  74.                                 System.out.println("rmqMsg.getParams()=="+rmqMsg.getParams()[0].toString());  
  75.                                 suc=true;  
  76.   
  77.                             } catch (Exception e) {  
  78.                             }              
  79.                             if(suc)  
  80.                                 channel.basicAck(deliveryTag, false);  
  81.                             else  
  82.                                 channel.basicNack(deliveryTag, false,true);  
  83.                          }  
  84.                      };           
  85.                 try {  
  86.                     channel.basicConsume(queueName, autoAck, consumer);               
  87.                 }catch (IOException e) {  
  88.                     logger.warn(e.getMessage());  
  89.                 }   
  90.             }  
  91.         };  
  92.     }  


说明:

   1)channel.queueDeclare().getQueue() 得到的是一个随机queue,断开连接后即删除。

   2)channel.basicQos(qos) 设置最大的投送字节数

   3)channel.basicNack(deliveryTag, false,true);false代表失败,true是要重新发送,

结果:


可以通过反射机制进行调用具体的类,来根据不同的队列来处理不同的信息。

总结:

   这边RabbitMQ与SpringMVC配置,没用到SpringMVC里的RabbitMQ,下一篇会介绍。

  




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值