1. Woker队列结构图

  

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

2. 创建一个生产者

  Producer如下:

package com.wangx.rabbitmq.worker;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory; import java.io.IOException;
import java.util.concurrent.TimeoutException; public class Producer { /**
* 队列名字
*/
private static final String QUEUE_NAME = "worker-queue";
public static void main(String[] args) throws IOException, TimeoutException { //创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务器主机
factory.setHost("127.0.0.1");
//设置用户名
factory.setUsername("wangx");
//设置密码
factory.setPassword("wangx");
//设置VirtualHost
factory.setVirtualHost("/wangx");
Connection connection = null;
Channel channel = null;
try { //创建连接
connection = factory.newConnection();
//创建消息通道
channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "Hello World!";
//发送消息
for (int i = 0; i < 10; i++) {
//发送消息
channel.basicPublish("", QUEUE_NAME, null, (message + i).getBytes());
System.out.println(" [x] Sent '" + message + i + "'");
}
}catch (Exception e) {
e.printStackTrace();
} finally {
channel.close();
connection.close();
}
}
}

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

3. 创建两个消费者

  Consumer1如下:

package com.wangx.rabbitmq.worker;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException; public class Consumer1 {
/**
* 队列名字
*/
private static final String QUEUE_NAME = "worker-queue";
public static void main(String[] args) throws IOException, TimeoutException { //创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务器主机
factory.setHost("localhost");
//设置用户
factory.setUsername("wangx");
//设置密码
factory.setPassword("wangx");
//设置VirtualHost
factory.setVirtualHost("/wangx");
Connection connection = null;
try {
//创建连接
connection = factory.newConnection();
//创建消息通道
final Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel){
//重写DefaultConsumer中handleDelivery方法,在方法中获取消息
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException{
try {
//消息沉睡一秒
Thread.sleep(1000);
String message = new String(body, "UTF-8");
System.out.println("consumer1 收到消息 '" + message + "'");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("consumer1 消息消费完成....");
channel.basicAck(envelope.getDeliveryTag(),false);
} }
};
//监听消息
channel.basicConsume(QUEUE_NAME, false,consumer);
}catch (Exception e) {
e.printStackTrace();
}finally {
}
}
}

  Consumer2

package com.wangx.rabbitmq.worker;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException; public class Consumer2 {
/**
* 队列名字
*/
private static final String QUEUE_NAME = "worker-queue";
public static void main(String[] args) throws IOException, TimeoutException { //创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务器主机
factory.setHost("localhost");
//设置用户
factory.setUsername("wangx");
//设置密码
factory.setPassword("wangx");
//设置VirtualHost
factory.setVirtualHost("/wangx");
Connection connection = null;
try {
//创建连接
connection = factory.newConnection();
//创建消息通道
final Channel channel = connection.createChannel();
// channel.basicQos(1);
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
Consumer consumer = new DefaultConsumer(channel){
//重写DefaultConsumer中handleDelivery方法,在方法中获取消息
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException{
try {
//消息沉睡100ms
Thread.sleep(100);
String message = new String(body, "UTF-8");
System.out.println("consumer2 收到消息 '" + message + "'");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("consumer2 消息消费完成....");
channel.basicAck(envelope.getDeliveryTag(),false);
} }
};
//监听消息
channel.basicConsume(QUEUE_NAME, false,consumer);
}catch (Exception e) {
e.printStackTrace();
}finally {
}
}
}

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

Consumer1:

consumer1 收到消息 'Hello World!0'
consumer1 消息消费完成....
consumer1 收到消息 'Hello World!2'
consumer1 消息消费完成....
consumer1 收到消息 'Hello World!4'
consumer1 消息消费完成....
consumer1 收到消息 'Hello World!6'
consumer1 消息消费完成....
consumer1 收到消息 'Hello World!8'
consumer1 消息消费完成.... Consumer2: consumer2 收到消息 'Hello World!1'
consumer2 消息消费完成....
consumer2 收到消息 'Hello World!3'
consumer2 消息消费完成....
consumer2 收到消息 'Hello World!5'
consumer2 消息消费完成....
consumer2 收到消息 'Hello World!7'
consumer2 消息消费完成....
consumer2 收到消息 'Hello World!9'
consumer2 消息消费完成....

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

4. "能者多劳"模式

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

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

  如下:

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

控制台打印结果:

Consumer1:

consumer1 收到消息 'Hello World!1'
consumer1 消息消费完成.... Consumer2: consumer2 收到消息 'Hello World!0'
consumer2 消息消费完成....
consumer2 收到消息 'Hello World!2'
consumer2 消息消费完成....
consumer2 收到消息 'Hello World!3'
consumer2 消息消费完成....
consumer2 收到消息 'Hello World!4'
consumer2 消息消费完成....
consumer2 收到消息 'Hello World!5'
consumer2 消息消费完成....
consumer2 收到消息 'Hello World!6'
consumer2 消息消费完成....
consumer2 收到消息 'Hello World!7'
consumer2 消息消费完成....
consumer2 收到消息 'Hello World!8'
consumer2 消息消费完成....
consumer2 收到消息 'Hello World!9'
consumer2 消息消费完成....

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

5. 消息的确认模式

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

  模式1:自动确认

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

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

channel.basicConsume(QUEUE_NAME, true,consumer);

  模式2: 手动确认模式

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

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

channel.basicConsume(QUEUE_NAME, false,consumer);

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

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. CorelDRAW X8官方正版特惠下载

    CorelDRAW X8自发布以来,价格居高不下,这也使一众忠粉望而却步,之前看过CorelDRAW做活动,都是X6\X7这些比较早的版本,比较新的版本也没做什么优惠,不过还好看了一下,CorelDR ...

  2. 复习MySQL①创建数据库及数据表

    • 创建数据库:create database 数据库名称; – 例:创建名为test的测试数据库 create database test; • 查看创建好的数据库:show create data ...

  3. Angular之constructor和ngOnInit差异及适用场景(转)

    原始地址:https://blog.csdn.net/u010730126/article/details/64486997 Angular中根据适用场景定义了很多生命周期函数,其本质上是事件的响应函 ...

  4. Project Euler 37 Truncatable primes

    题意:3797有着奇特的性质.不仅它本身是一个素数,而且如果从左往右逐一截去数字,剩下的仍然都是素数:3797.797.97和7:同样地,如果从右往左逐一截去数字,剩下的也依然都是素数:3797.37 ...

  5. MNIST机器学习数据集

    介绍 在学习机器学习的时候,首当其冲的就是准备一份通用的数据集,方便与其他的算法进行比较.在这里,我写了一个用于加载MNIST数据集的方法,并将其进行封装,主要用于将MNIST数据集转换成numpy. ...

  6. 04springMVC数据类型转换

    数据类型转换简介 Spring Web MVC中的数据类型转换 内建的类型转换器 自定义类型转换器 1      数据类型转换简介 当从页面提交数据到后台Action的时候,通过请求发送的数据,通常都 ...

  7. 洛谷——P2657 低头一族

    https://www.luogu.org/problem/show?pid=2657 题目描述 一群青年人排成一队,用手机互相聊天. 每个人的手机有一个信号接收指标,第i个人的接收指标设为v[i]. ...

  8. 安装ftp碰到的问题及解决方法

    1   CRT显示乱码:     本地windows机器.改动SecureCRT的设置.找到"选项"->"会话选项"->"外观" ...

  9. Linux 查找一个函数在哪调用

    grep "function has" -R ThinkPHP

  10. 深入理解 C 指针阅读笔记 -- 第五章

    Chapter5.h #ifndef __CHAPTER_5_ #define __CHAPTER_5_ /*<深入理解C指针>学习笔记 -- 第五章*/ /*不应该改动的字符串就应该用 ...