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. URLLib库使用

    Date: 2019-06-19 Author: Sun urllib ​ 在Python 3以后的版本中,urllib2这个模块已经不单独存在(也就是说当你import urllib2时,系统提示你 ...

  2. vc++元文件的保存,保存图形,重绘图形

    1, CMateFileDC 可以用来多次打开自己的画布,这个元文件包含许多接口的命令 当绘制好之后可以用来播放元文件 首先,创建一个CMateFileDC的元文件对象 然后调用Create原函数,创 ...

  3. MySQL数据表查询操

    准语法结构:编写DQL时一定要严格按照此语法的顺序来实现!/* SELECT [ALL | DISTINCT] ALL表示查询出所有的内容 DISTINCT 去重 {* | 表名.* | 表名.字段名 ...

  4. 关于Tomcat下项目被启动两次为问题

    最近遇见了一个很搞得事情,在tomcat下启动项目时自己写的定时程序被执行了两次,导致程序启动了两个线程,使定时任务在几秒间隔内执行了两次,后来通过日志查到,原来是tomcat将项目启动了两次,为什么 ...

  5. Project Euler 35 Circular primes

    题意:197被称为圆周素数,因为将它逐位旋转所得到的数:197/971和719都是素数.小于100的圆周素数有十三个:2.3.5.7.11.13.17.31.37.71.73.79和97.小于一百万的 ...

  6. P1423 小玉在游泳

    ... 题目描述 小玉开心的在游泳,可是她很快难过的发现,自己的力气不够,游泳好累哦.已知小玉第一步能游2米,可是随着越来越累,力气越来越小,她接下来的每一步都只能游出上一步距离的98%.现在小玉想知 ...

  7. 01.Python基础-1.Python简介及基础

    python简介 python简介 python是一种面向对象的解释型计算机程序设计语言,由荷兰人Guido van Rossum(吉多·范罗苏姆)于1989年发明,第一个公开发行版发行于1991年. ...

  8. 基于Vue的事件响应式进度条组件

    写在前面 找了很多Vue 进度条组件!,都不包含拖拽和点击事件,input range倒是原生包含input和change事件,但是直接基于input range做进度条的话,样式部分需要做大量调整和 ...

  9. python基础:局部变量--全局变量的使用

    局部变量: 使用原则:仅在本函数内部使用的变量,其他函数无法使用本函数的变量 代码: def function1(): a = 2 #定义一个局部变量 print(a) def function2() ...

  10. 如何在 VMware 上安装 CentOS 6.8

    一.下载 CentOS 6.8  64位 链接:https://pan.baidu.com/s/15qmWGar2m0WzsWxDk4tSSg 密码:wqra 二.安装步骤 1.新建虚拟机 2.自定义 ...