java rabbitmq 的使用

 四、java操作简单的simple简单队列

  • 模型

P:消息的生产者

红色:队列

C:消费者

  • 获取Mq的链接
 1 import java.io.IOException;
 2 import java.util.concurrent.TimeoutException;
 3 
 4 import com.rabbitmq.client.Connection;
 5 import com.rabbitmq.client.ConnectionFactory;
 6 
 7 public class ConnectionUtils {
 8     
 9     /**
10      * 
11      * 方法功能说明:获取MQ的链接
12      * 参数说明:
13      * @return
14      * 作者:Administrator
15      * 创建时间:2019年1月22日-下午7:51:48
16      * @throws TimeoutException 
17      * @throws IOException 
18      * 
19      *   
20      */
21     public static Connection getConnection() throws IOException, TimeoutException {
22         //定义一个链接工厂
23         ConnectionFactory factory = new ConnectionFactory();
24         //设置服务地址
25         factory.setHost("192.168.0.116");
26         //设定端口
27         factory.setPort(8244);
28         //设定vhost
29         factory.setVirtualHost("vhost");
30         //设定用户名
31         factory.setUsername("admin");
32         //设定密码
33         factory.setPassword("jorudan0407");
34         return factory.newConnection();
35     }
36 }
View Code
  • 生产者生产消息
 1 package com.mmr.rabbitmq.simple;
 2 
 3 import java.io.IOException;
 4 import java.util.concurrent.TimeoutException;
 5 
 6 import com.mmr.rabbitmq.utils.ConnectionUtils;
 7 import com.rabbitmq.client.Channel;
 8 import com.rabbitmq.client.Connection;
 9 
10 public class Send {
11     private static final String QUEUE_NAME="test_simple_queue";
12     
13     public static void main(String[] args) throws IOException, TimeoutException {
14         //获取一个链接
15         Connection connection = ConnectionUtils.getConnection();
16         //获取一个通道
17         Channel channel = connection.createChannel();
18         //创建队列声明
19         channel.queueDeclare(QUEUE_NAME, false, false, false, null);
20         String msg = "hello simple!";
21         channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
22         System.out.println("-- send msg: "+msg);
23         
24         channel.close();
25     }
26 }
View Code
  • 消费者消费消息
 1 package com.mmr.rabbitmq.simple;
 2 
 3 import java.io.IOException;
 4 import java.util.concurrent.TimeoutException;
 5 
 6 import com.mmr.rabbitmq.utils.ConnectionUtils;
 7 import com.rabbitmq.client.Channel;
 8 import com.rabbitmq.client.Connection;
 9 import com.rabbitmq.client.DefaultConsumer;
10 import com.rabbitmq.client.Envelope;
11 import com.rabbitmq.client.AMQP.BasicProperties;
12 
13 /**
14  * 
15 * 公司:若尔丹软件开发公司  
16 * 类描述:获取消息 
17 * 作者:lizongti
18 * 创建时间:2019年1月22日-下午8:09:24
19 * 更新时间:
20  */
21 public class Recv {
22     private static final String QUEUE_NAME="test_simple_queue";
23     public static void main(String[] args) throws IOException, TimeoutException {
24         //获取链接
25         Connection connection = ConnectionUtils.getConnection();
26         //创建链接
27         Channel channel = connection.createChannel();
28         //队列声明
29         channel.queueDeclare(QUEUE_NAME, false, false, false, null);
30         //监听队列
31         channel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(channel) {
32             @Override
33             public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
34                     throws IOException {
35                 // TODO Auto-generated method stub
36                 System.out.println("接收到消息:"+new String(body, "UTF-8"));
37             }
38         });
39     }
40 }
View Code
  • 简单队列不足
    • 耦合性告,生产者意义对应消费者,如果我想有多个消费者,就不行了
    • 对列明变更,这时候的同时变更

五、Work queues工作队列

  • 模型

为什么会出现工作队列

simple队列是意义对应的,而且实际开发,生产者生产消息是很快的,而消费者一般是同业务结合的,处理逻辑慢。可能花费时间较多,这时候队列就会积压很多消息。 

  • 生产者生产消息
 1 package com.mmr.rabbitmq.work;
 2 
 3 import com.mmr.rabbitmq.utils.ConnectionUtils;
 4 import com.rabbitmq.client.Channel;
 5 import com.rabbitmq.client.Connection;
 6 
 7 public class Send {
 8     private static final String QUEUE_NAME="test_work_query";
 9     public static void main(String[] args) throws Exception {
10         Connection connection = ConnectionUtils.getConnection();
11         Channel channel = connection.createChannel();
12         channel.queueDeclare(QUEUE_NAME, false, false, false, null);
13         for(int i=0;i<50;i++) {
14             String msg = "hello"+i;
15             channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
16             Thread.sleep(i*20);
17         }
18         
19         channel.close();
20         connection.close();
21     }
22 }
View Code
  • 消费者1消费消息
 1 package com.mmr.rabbitmq.work;
 2 
 3 import java.io.IOException;
 4 
 5 import com.mmr.rabbitmq.utils.ConnectionUtils;
 6 import com.rabbitmq.client.Channel;
 7 import com.rabbitmq.client.Connection;
 8 import com.rabbitmq.client.DefaultConsumer;
 9 import com.rabbitmq.client.Envelope;
10 import com.rabbitmq.client.AMQP.BasicProperties;
11 
12 public class Recv1 {
13     private static final String QUEUE_NAME="test_work_query";
14     public static void main(String[] args) throws Exception {
15         //获取链接
16         Connection con = ConnectionUtils.getConnection();
17         //获取channel
18         Channel channel = con.createChannel();
19         //声明队列
20         channel.queueDeclare(QUEUE_NAME, false, false, false, null);
21         Boolean autoAck = true;
22         channel.basicConsume(QUEUE_NAME, autoAck, new DefaultConsumer(channel) {
23             @Override
24             public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
25                     throws IOException {
26                 System.out.println("Recv1 触发消息:"+new String(body,"utf-8"));
27                 try {
28                     Thread.sleep(2000);
29                 } catch (InterruptedException e) {
30                     // TODO Auto-generated catch block
31                     e.printStackTrace();
32                 }finally {
33                     System.out.println("Recv1 消息处理完成!");
34                 }
35             }
36         });
37     }
38 }
View Code
  • 消费者2消费消息
 1 package com.mmr.rabbitmq.work;
 2 
 3 import java.io.IOException;
 4 
 5 import com.mmr.rabbitmq.utils.ConnectionUtils;
 6 import com.rabbitmq.client.Channel;
 7 import com.rabbitmq.client.Connection;
 8 import com.rabbitmq.client.DefaultConsumer;
 9 import com.rabbitmq.client.Envelope;
10 import com.rabbitmq.client.AMQP.BasicProperties;
11 
12 public class Recv2 {
13     private static final String QUEUE_NAME="test_work_query";
14     public static void main(String[] args) throws Exception {
15         //获取链接
16         Connection con = ConnectionUtils.getConnection();
17         //获取channel
18         Channel channel = con.createChannel();
19         //声明队列
20         channel.queueDeclare(QUEUE_NAME, false, false, false, null);
21         Boolean autoAck = true;
22         channel.basicConsume(QUEUE_NAME, autoAck, new DefaultConsumer(channel) {
23             @Override
24             public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
25                     throws IOException {
26                 System.out.println("Recv2 触发消息:"+new String(body,"utf-8"));
27                 try {
28                     Thread.sleep(1000);
29                 } catch (InterruptedException e) {
30                     // TODO Auto-generated catch block
31                     e.printStackTrace();
32                 }finally {
33                     System.out.println("Recv2 消息处理完成!");
34                 }
35             }
36         });
37     }
38 }
View Code

现象:

消费者1和消费者2处理的数据消息是一样多的,消费者1都是偶数,消费者2都是基数,这种方式叫做轮询分发(round-robin)结果就不管谁处理快,都不会都给一个消息,任务消息总是你一个我一个。

公平分发:

使用basicQos(perfetch=1),使用自动分发,必需干逼ack,手动应答

生产者

 1 package com.mmr.rabbitmq.work.workfair;
 2 
 3 import com.mmr.rabbitmq.utils.ConnectionUtils;
 4 import com.rabbitmq.client.Channel;
 5 import com.rabbitmq.client.Connection;
 6 
 7 public class Send {
 8     private static final String QUEUE_NAME="test_work_query";
 9     public static void main(String[] args) throws Exception {
10         Connection connection = ConnectionUtils.getConnection();
11         Channel channel = connection.createChannel();
12         channel.queueDeclare(QUEUE_NAME, false, false, false, null);
13         /**
14          * 每个消费者发送确认消息之前,消息队列不发送下一个消息到消费者,一次只能处理一个消息
15          * 限制发送给同一个消费者不超过一条消息
16          */
17         int prefetchCount=1;
18         channel.basicQos(prefetchCount);
19         for(int i=0;i<50;i++) {
20             String msg = "hello"+i;
21             channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
22             Thread.sleep(i*20);
23         }
24         
25         channel.close();
26         connection.close();
27     }
28 }
View Code

消费者1

 1 package com.mmr.rabbitmq.work.workfair;
 2 
 3 import java.io.IOException;
 4 
 5 import com.mmr.rabbitmq.utils.ConnectionUtils;
 6 import com.rabbitmq.client.Channel;
 7 import com.rabbitmq.client.Connection;
 8 import com.rabbitmq.client.DefaultConsumer;
 9 import com.rabbitmq.client.Envelope;
10 import com.rabbitmq.client.AMQP.BasicProperties;
11 
12 public class Recv1 {
13     private static final String QUEUE_NAME="test_work_query";
14     public static void main(String[] args) throws Exception {
15         //获取链接
16         Connection con = ConnectionUtils.getConnection();
17         //获取channel
18         Channel channel = con.createChannel();
19         //声明队列
20         channel.queueDeclare(QUEUE_NAME, false, false, false, null);
21         channel.basicQos(1);//保证一次只发一个
22         Boolean autoAck = false;//自动应答 false
23         channel.basicConsume(QUEUE_NAME, autoAck, new DefaultConsumer(channel) {
24             @Override
25             public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
26                     throws IOException {
27                 System.out.println("Recv1 触发消息:"+new String(body,"utf-8"));
28                 try {
29                     Thread.sleep(2000);
30                 } catch (InterruptedException e) {
31                     // TODO Auto-generated catch block
32                     e.printStackTrace();
33                 }finally {
34                     System.out.println("Recv1 消息处理完成!");
35                     channel.basicAck(envelope.getDeliveryTag(), false);
36                 }
37             }
38         });
39     }
40 }
View Code

消费者2

 1 package com.mmr.rabbitmq.work.workfair;
 2 
 3 import java.io.IOException;
 4 
 5 import com.mmr.rabbitmq.utils.ConnectionUtils;
 6 import com.rabbitmq.client.Channel;
 7 import com.rabbitmq.client.Connection;
 8 import com.rabbitmq.client.DefaultConsumer;
 9 import com.rabbitmq.client.Envelope;
10 import com.rabbitmq.client.AMQP.BasicProperties;
11 
12 public class Recv2 {
13     private static final String QUEUE_NAME="test_work_query";
14     public static void main(String[] args) throws Exception {
15         //获取链接
16         Connection con = ConnectionUtils.getConnection();
17         //获取channel
18         Channel channel = con.createChannel();
19         //声明队列
20         channel.queueDeclare(QUEUE_NAME, false, false, false, null);
21         channel.basicQos(1);//保证一次只发一个
22         Boolean autoAck = false;//自动应答 false
23         channel.basicConsume(QUEUE_NAME, autoAck, new DefaultConsumer(channel) {
24             @Override
25             public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
26                     throws IOException {
27                 System.out.println("Recv2 触发消息:"+new String(body,"utf-8"));
28                 try {
29                     Thread.sleep(1000);
30                 } catch (InterruptedException e) {
31                     // TODO Auto-generated catch block
32                     e.printStackTrace();
33                 }finally {
34                     System.out.println("Recv2 消息处理完成!");
35                     //手动回执一个消息
36                     channel.basicAck(envelope.getDeliveryTag(), false);
37                 }
38             }
39         });
40     }
41 }
View Code

 现象:消费者2比消费者消费的多,也叫能者多劳

 

六、消息应答与消息持久化

boolean autoAck = false;

channel.basicConsume(QUEUE_NAME,autoAck,consumer);

boolean autoAck = true;(自动确认模式),一旦rabbitmq将消息分发给消费者,就会从内存删除,这种情况,如果杀死正在执行的消费者,就会丢失正在处理的消息

boolean autoAck = false;(手动模式),如果有一个消费者挂掉,就会交付给其他的消费者,消息不会丢失,rabbitmq支持消息应答,消费者发送一个消息应答告诉rabbitmq这个消息我已经处理完成,你可以删除了,然后rabbitmq就会删除内存中的消息。

消息应答默认是打开的,即false

消息的持久化:

boolean durable = true;

channel.queueDeclare(QUEUE_NAME, durable , false, false, null);

我们将程序的dureable=false改成true,是不可以的,应为代码景观是正确的,它也不会运行成功,因为我们定义了一个test_work_queue这个queue是为持久化的,rabbitmq不允许重新定义不同参数的一个已存在的队列

解决方法:在控制台将原来的队列删除即可

七、订阅模式

  • 模型

一个生产者、多个消费者

每个消费者都有自己的队列

生产者没有将消息直接发给队列,而是发给交换机(转发器 exchange)里面

每个队列都要绑定到交换机上

生产者发送的消息经过交换机到达多个队列,就能实现一个消息被多个消费者消费 

posted on 2019-01-22 21:23  lizongti  阅读(1848)  评论(0编辑  收藏  举报