1. Woker队列结构图

  

  这里表示一个生产者生产了消息发送到队列中,但是确有两个消费者在消费同一个队列中的消息。

2. 创建一个生产者

  Producer如下:

  1. package com.wangx.rabbitmq.worker;
  2.  
  3. import com.rabbitmq.client.Channel;
  4. import com.rabbitmq.client.Connection;
  5. import com.rabbitmq.client.ConnectionFactory;
  6.  
  7. import java.io.IOException;
  8. import java.util.concurrent.TimeoutException;
  9.  
  10. public class Producer {
  11.  
  12. /**
  13. * 队列名字
  14. */
  15. private static final String QUEUE_NAME = "worker-queue";
  16. public static void main(String[] args) throws IOException, TimeoutException {
  17.  
  18. //创建连接工厂
  19. ConnectionFactory factory = new ConnectionFactory();
  20. //设置服务器主机
  21. factory.setHost("127.0.0.1");
  22. //设置用户名
  23. factory.setUsername("wangx");
  24. //设置密码
  25. factory.setPassword("wangx");
  26. //设置VirtualHost
  27. factory.setVirtualHost("/wangx");
  28. Connection connection = null;
  29. Channel channel = null;
  30. try {
  31.  
  32. //创建连接
  33. connection = factory.newConnection();
  34. //创建消息通道
  35. channel = connection.createChannel();
  36. //声明队列
  37. channel.queueDeclare(QUEUE_NAME, false, false, false, null);
  38. String message = "Hello World!";
  39. //发送消息
  40. for (int i = 0; i < 10; i++) {
  41. //发送消息
  42. channel.basicPublish("", QUEUE_NAME, null, (message + i).getBytes());
  43. System.out.println(" [x] Sent '" + message + i + "'");
  44. }
  45. }catch (Exception e) {
  46. e.printStackTrace();
  47. } finally {
  48. channel.close();
  49. connection.close();
  50. }
  51. }
  52. }

  这里同时向队列发送了十条消息。

3. 创建两个消费者

  Consumer1如下:

  1. package com.wangx.rabbitmq.worker;
  2.  
  3. import com.rabbitmq.client.*;
  4.  
  5. import java.io.IOException;
  6. import java.util.concurrent.TimeoutException;
  7.  
  8. public class Consumer1 {
  9. /**
  10. * 队列名字
  11. */
  12. private static final String QUEUE_NAME = "worker-queue";
  13. public static void main(String[] args) throws IOException, TimeoutException {
  14.  
  15. //创建连接工厂
  16. ConnectionFactory factory = new ConnectionFactory();
  17. //设置服务器主机
  18. factory.setHost("localhost");
  19. //设置用户
  20. factory.setUsername("wangx");
  21. //设置密码
  22. factory.setPassword("wangx");
  23. //设置VirtualHost
  24. factory.setVirtualHost("/wangx");
  25. Connection connection = null;
  26. try {
  27. //创建连接
  28. connection = factory.newConnection();
  29. //创建消息通道
  30. final Channel channel = connection.createChannel();
  31. //声明队列
  32. channel.queueDeclare(QUEUE_NAME, false, false, false, null);
  33. // channel.basicQos(1);
  34. Consumer consumer = new DefaultConsumer(channel){
  35. //重写DefaultConsumer中handleDelivery方法,在方法中获取消息
  36. @Override
  37. public void handleDelivery(String consumerTag, Envelope envelope,
  38. AMQP.BasicProperties properties, byte[] body) throws IOException{
  39. try {
  40. //消息沉睡一秒
  41. Thread.sleep(1000);
  42. String message = new String(body, "UTF-8");
  43. System.out.println("consumer1 收到消息 '" + message + "'");
  44. } catch (InterruptedException e) {
  45. e.printStackTrace();
  46. }finally {
  47. System.out.println("consumer1 消息消费完成....");
  48. channel.basicAck(envelope.getDeliveryTag(),false);
  49. }
  50.  
  51. }
  52. };
  53. //监听消息
  54. channel.basicConsume(QUEUE_NAME, false,consumer);
  55. }catch (Exception e) {
  56. e.printStackTrace();
  57. }finally {
  58. }
  59. }
  60. }

  Consumer2

  1. package com.wangx.rabbitmq.worker;
  2.  
  3. import com.rabbitmq.client.*;
  4.  
  5. import java.io.IOException;
  6. import java.util.concurrent.TimeoutException;
  7.  
  8. public class Consumer2 {
  9. /**
  10. * 队列名字
  11. */
  12. private static final String QUEUE_NAME = "worker-queue";
  13. public static void main(String[] args) throws IOException, TimeoutException {
  14.  
  15. //创建连接工厂
  16. ConnectionFactory factory = new ConnectionFactory();
  17. //设置服务器主机
  18. factory.setHost("localhost");
  19. //设置用户
  20. factory.setUsername("wangx");
  21. //设置密码
  22. factory.setPassword("wangx");
  23. //设置VirtualHost
  24. factory.setVirtualHost("/wangx");
  25. Connection connection = null;
  26. try {
  27. //创建连接
  28. connection = factory.newConnection();
  29. //创建消息通道
  30. final Channel channel = connection.createChannel();
  31. // channel.basicQos(1);
  32. //声明队列
  33. channel.queueDeclare(QUEUE_NAME, false, false, false, null);
  34. Consumer consumer = new DefaultConsumer(channel){
  35. //重写DefaultConsumer中handleDelivery方法,在方法中获取消息
  36. @Override
  37. public void handleDelivery(String consumerTag, Envelope envelope,
  38. AMQP.BasicProperties properties, byte[] body) throws IOException{
  39. try {
  40. //消息沉睡100ms
  41. Thread.sleep(100);
  42. String message = new String(body, "UTF-8");
  43. System.out.println("consumer2 收到消息 '" + message + "'");
  44. } catch (InterruptedException e) {
  45. e.printStackTrace();
  46. }finally {
  47. System.out.println("consumer2 消息消费完成....");
  48. channel.basicAck(envelope.getDeliveryTag(),false);
  49. }
  50.  
  51. }
  52. };
  53. //监听消息
  54. channel.basicConsume(QUEUE_NAME, false,consumer);
  55. }catch (Exception e) {
  56. e.printStackTrace();
  57. }finally {
  58. }
  59. }
  60. }

  可以看到consumer1在消息处理的过程中,沉睡了1s,而consumer2沉睡了100ms,以前面的mq的惯性来说,应该是沉睡时间少的消费多一些消息,但是我们来看控制台:

  1. Consumer1:
  2.  
  3. consumer1 收到消息 'Hello World!0'
  4. consumer1 消息消费完成....
  5. consumer1 收到消息 'Hello World!2'
  6. consumer1 消息消费完成....
  7. consumer1 收到消息 'Hello World!4'
  8. consumer1 消息消费完成....
  9. consumer1 收到消息 'Hello World!6'
  10. consumer1 消息消费完成....
  11. consumer1 收到消息 'Hello World!8'
  12. consumer1 消息消费完成....
  13.  
  14. Consumer2:
  15.  
  16. consumer2 收到消息 'Hello World!1'
  17. consumer2 消息消费完成....
  18. consumer2 收到消息 'Hello World!3'
  19. consumer2 消息消费完成....
  20. consumer2 收到消息 'Hello World!5'
  21. consumer2 消息消费完成....
  22. consumer2 收到消息 'Hello World!7'
  23. consumer2 消息消费完成....
  24. consumer2 收到消息 'Hello World!9'
  25. consumer2 消息消费完成....

  可以看消息的消费是平均分发的,一个消费奇数,一个偶数消息。但是有时候我们并不希望说消息平均消费,而是让消费快的多消费,慢的少消费。

4. "能者多劳"模式

  ”能者多劳“即是消费速度快的消费者消费更多的消息,速度慢的消费少的消息。

  使用这种模式只需要设置消费者的channel的basicQos即可。

  如下:

  channel.basicQos(1);表示消息服务器每次只向消费分发一条消息。可以设置多条,只需要在任意的消费者中设置就对所有consumer生效。

控制台打印结果:

  1. Consumer1:
  2.  
  3. consumer1 收到消息 'Hello World!1'
  4. consumer1 消息消费完成....
  5.  
  6. Consumer2:
  7.  
  8. consumer2 收到消息 'Hello World!0'
  9. consumer2 消息消费完成....
  10. consumer2 收到消息 'Hello World!2'
  11. consumer2 消息消费完成....
  12. consumer2 收到消息 'Hello World!3'
  13. consumer2 消息消费完成....
  14. consumer2 收到消息 'Hello World!4'
  15. consumer2 消息消费完成....
  16. consumer2 收到消息 'Hello World!5'
  17. consumer2 消息消费完成....
  18. consumer2 收到消息 'Hello World!6'
  19. consumer2 消息消费完成....
  20. consumer2 收到消息 'Hello World!7'
  21. consumer2 消息消费完成....
  22. consumer2 收到消息 'Hello World!8'
  23. consumer2 消息消费完成....
  24. consumer2 收到消息 'Hello World!9'
  25. consumer2 消息消费完成....

  此时Consumer1才消费了1条,Consumer2消费 了其余的九条,这就是”能者多劳“模式的体现。

5. 消息的确认模式

  消费者从队列中获取消息,服务端是如何知道消息已经被消费完成了呢?

  模式1:自动确认

  只要消息从队列中被获取,无论消费者取到消息后是否成功消费消息,都认为消息已经成功消费。

  使用方式为:将channel.basicConsume();方法的第二个参数设置为true,如下:

  1. channel.basicConsume(QUEUE_NAME, true,consumer);

  模式2: 手动确认模式

  消费者从队列中获取消息之后,服务器会将该消息标记为不可用状态,等待消费者的反馈,如果消费者一直没有反馈,那么该消息将一直处于不可用状态。

  使用方式为:将channel.basicConsume();方法的第二个参数设置为true,如下:

  1. channel.basicConsume(QUEUE_NAME, false,consumer);

  然后在消息的DefaultConsumer.handleDelivery中使用channel.basicAck();方法在消息消费完成时通知服务端消费已经完成。如下:

  1. channel.basicAck(envelope.getDeliveryTag(),false);

RabbitMQ学习笔记(3)----RabbitMQ Worker的使用的更多相关文章

  1. [RabbitMQ学习笔记] - 初识RabbitMQ

    RabbitMQ是一个由erlang开发的AMQP的开源实现. 核心概念 Message 消息,消息是不具名的,它由消息头和消息体组成,消息体是不透明的,而消息头则由 一系列的可选属性组成,这些属性包 ...

  2. RabbitMQ学习笔记(五) Topic

    更多的问题 Direct Exchange帮助我们解决了分类发布与订阅消息的问题,但是Direct Exchange的问题是,它所使用的routingKey是一个简单字符串,这决定了它只能按照一个条件 ...

  3. RabbitMQ学习笔记1-hello world

    安装过程略过,一搜一大把. rabbitmq管理控制台:http://localhost:15672/   默认账户:guest/guest RabbitMQ默认监听端口:5672 JAVA API地 ...

  4. (转) Rabbitmq学习笔记

    详见原文: http://blog.csdn.net/shatty/article/details/9529463 Rabbitmq学习笔记

  5. 官网英文版学习——RabbitMQ学习笔记(十)RabbitMQ集群

    在第二节我们进行了RabbitMQ的安装,现在我们就RabbitMQ进行集群的搭建进行学习,参考官网地址是:http://www.rabbitmq.com/clustering.html 首先我们来看 ...

  6. 官网英文版学习——RabbitMQ学习笔记(一)认识RabbitMQ

    鉴于目前中文的RabbitMQ教程很缺,本博主虽然买了一本rabbitMQ的书,遗憾的是该书的代码用的不是java语言,看起来也有些不爽,且网友们不同人学习所写不同,本博主看的有些地方不太理想,为此本 ...

  7. RabbitMQ学习笔记五:RabbitMQ之优先级消息队列

    RabbitMQ优先级队列注意点: 1.只有当消费者不足,不能及时进行消费的情况下,优先级队列才会生效 2.RabbitMQ3.5以后才支持优先级队列 代码在博客:RabbitMQ学习笔记三:Java ...

  8. 官网英文版学习——RabbitMQ学习笔记(八)Remote procedure call (RPC)

    在第四篇学习笔记中,我们学习了如何使用工作队列在多个工作者之间分配耗时的任务.   但是,如果我们需要在远程计算机上运行一个函数并等待结果呢?这是另一回事.这种模式通常称为远程过程调用或RPC.   ...

  9. 消息队列——RabbitMQ学习笔记

    消息队列--RabbitMQ学习笔记 1. 写在前面 昨天简单学习了一个消息队列项目--RabbitMQ,今天趁热打铁,将学到的东西记录下来. 学习的资料主要是官网给出的6个基本的消息发送/接收模型, ...

  10. RabbitMQ学习笔记(六) RPC

    什么RPC? 这一段是从度娘摘抄的. RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的 ...

随机推荐

  1. win系统如何在桌面显示我的电脑

    如果是在Windows Server 2012本地控制台下,直接按Win(键盘上的微软徽标键)+R,输入: rundll32.exe shell32.dll,Control_RunDLL desk.c ...

  2. 超实用的JavaScript代码段

    1. 判断日期是否有效 JavaScript中自带的日期函数还是太过简单,很难满足真实项目中对不同日期格式进行解析和判断的需要.JQuery也有一些第三方库来使日期相关的处理变得简单,但有时你可能只需 ...

  3. spring cloud(三) config

    spring cloud 配置中心 config 搭建过程 1.搭建config-server 服务端 1.1. 新建boot工程 pom引入依赖 <!-- config配置中心 --> ...

  4. css3小叮当(转载)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  5. python简单的输入与输出

    1 首先利用python完成简单的输出,运行如下: python和c语言类似,但又有所不同,python开发快,语言简洁,我是这样对比学的 输出:print+空格+'要输出的内容',一定要是英文状态下 ...

  6. kernel对NTP的API,系统调用函数

    kenrel API for NTP kernel 提供两个API(即系统调用 system call)给应用程序NTP,去校准kernel system clock Kernel Applicati ...

  7. 用div布局,页面copyright部分始终居于

    <!DOCTYPE HTML><html><head><meta http-equiv="Content-Type" content=&q ...

  8. vue中使用mui的extra icon问题

    1. 元素类名更改 2. mui下的fonts文件夹中添加mui-icons-extra.ttf文件 3. mui下的css文件中添加icons-extra.css文件 4. main.js中导入im ...

  9. HDU1114 - Piggy-Bank

    Before ACM can do anything, a budget must be prepared and the necessary financial support obtained. ...

  10. eclipse debug的时候提示debug Edit Source Lookup path

    原因可能是代码资源包未加载到debug的路径中,解决方法如下: Debug 视图下 ->在调试的线程上 右键单击 ->选择Edit Source Lookup Path ->选择Ad ...