RabbitMQ消息队列

一、Hello World

1.amqp-client客户端依赖

<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.0.0</version>
</dependency>

2.Rabbitmq类与方法

  • ConnectionFactory:设置port端口(5672)、host主机、用户名密码、virtual host虚拟主机信息
  • Channel: 通道
  • queueDeclare:声明队列,参数依次为:queueName队列名、durable持久化、exclusive排他队列、autoDelete自动删除、arguments(Map)其他参数配置
Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments) throws IOException;
  • basicPublish:发布消息,参数依次为:exchange交换机名、queueName队列名、props其他参数、body消息(Byte[])
void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;
  • DefaultConsumer:消息消费者,通过handleDelivery方法处理消息,使用时需重写该方法
  • handleDelivery:接收处理消息,参数依次为:consumerTag客户标记、envelope包含amqp参数、props其他参数、body消息(Byte[])
@Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body)
throws IOException
{
// no work to do
}
  • basicConsumer:监听队列,参数依次为:queue队列名、autoAck自动应答、callback回调
String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException;

3.Sender生产者

package com.lyf.springboot02.mq;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory; import java.io.IOException;
import java.util.concurrent.TimeoutException; /**
* @author lyf
* @date 2019/7/28 18:12
*/
public class Sender {
public static final String QUEUE_NAME = "simple"; public static void main(String[] args) throws IOException, TimeoutException {
/**
* 1. 获取连接connection
* 2. 声明消息队列queue name
* 3. 发送消息message
* 4. 关闭连接connection
*/
// 定义连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setPort(5672);
factory.setHost("192.168.37.200");
factory.setUsername("lyf");
factory.setPassword("123456");
factory.setVirtualHost("/lyf"); // 创建连接
Connection connection = factory.newConnection();
// 获取通道
Channel channel = connection.createChannel();
/* 声明一个 Queue
第一个参数叫 queue 名称
第二个参数 durable:是否持久化
第三个参数: Exclusive:排他队列,如果一个队列被声明为排他队列,该队列仅
对首次声明它的连接可见,并在连接断开时自动删除。这里需要注意三点:
其一,排他队列是基于连接可见的,同一连接的不同通道是可以同时访问同一个
连接创建的排他队列的。
其二,“首次”,如果一个连接已经声明了一个排他队列,其他连接是不允许建
立同名的排他队列的,这个与普通队列不同。
其三,即使该队列是持久化的,一旦连接关闭或者客户端退出,该排他队列都会
被自动删除的。
这种队列适用于只限于一个客户端发送读取消息的应用场景。
第四个参数:Auto-delete:自动删除,如果该队列没有任何订阅的消费者的话,该
队列会被自动删除。这种队列适用于临时队列。
*/
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 发送消息
String msg = "hello mq";
// 发送消息到 mq 队列中
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes("utf-8"));
// 关闭通道
channel.close();
// 关闭连接
connection.close();
}
}

4.Receiver消费者

package com.lyf.springboot02.mq;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException; public class Receiver {
public static final String QUEUE_NAME = "simple"; public static void main(String[] args) throws IOException, TimeoutException {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setPort(5672);
factory.setHost("192.168.37.200");
factory.setUsername("lyf");
factory.setPassword("123456");
factory.setVirtualHost("/lyf");
// 创建连接对象
Connection connection = factory.newConnection();
// 获取通道
Channel channel = connection.createChannel();
// 指定队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 获取消息
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
// 获取消息并在控制台打印
String msg = new String(body, "utf-8");
System.out.println("get msg:" + msg);
}
};
// 监听队列
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}

二、交换机类型 Exchange Type

  • Fanout 广播模式
  • Direct 路由模式
  • Topic 主题模式
  • Header
  • RPC

1.消息轮询分发(Round Robin)

 

默认方式,队列中的消息依次、平均分配给每个消费者。
注意:消费者中autoAck设为true,收到消息自动回执。
缺点:不同机子性能不一样,处理速度不一样。会造成慢的太累,快的太闲。
通过for循环生成多条消息,启动两个客户端,结果大致如下:

生产者:1 2 3 4 5 6 7 8 9 10
消费者1:1 3 5 7 9
消费者2:2 4 6 8 10

2.消息公平分发(Fair Dispatch)

 

机子性能好就多发,性能差就少发。
注意:

  • 消费者通道basicQos的prefetchCount设为1,每次只接收一条消息
  • autoAck设为false,手动回执
  • 回执消息basicAck,参数依次为:DeliveryTag消息通道名字、multiple批量

不足:每个消息只能被一个消费者消费
Receiver代码修改如下,通过线程睡眠模拟处理消息速度

channel.basicQos(1);

// 获取消息
Consumer consumer=new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg=new String(body,"utf-8");
System.out.println("1 号消费者消费消息:msg-->"+msg);
try {
Thread.sleep(250);
channel.basicAck(envelope.getDeliveryTag(), false);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}; channel.basicConsume(QUEUE_NAME, false, consumer);

3.发布与订阅模式(Publish/Subscribe)

 

Fanout:生产者发送消息到交换机,交换机投递到多个队列,每个队列由各自的消费者消费
注意:

  • 生产者不再声明队列,声明交换机exchangeDeclare,参数依次为:exchange交换机名、type交换机类型
Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type) throws IOException;
  • 发布消息时,指定交换机名、路由key为空
  • 消费者绑定队列到交换机queueBind,参数依次为:queue队列名、exchange交换机名、routingKey路由键
Queue.BindOk queueBind(String queue, String exchange, String routingKey) throws IOException;

缺点:不能区分消费者

Sender修改部分:

// 定义交换机名
final String EXCHANGE_NAME="hello-fanout";
// 声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
// 发布消息
String msg = "Hello";
channel.basicPublish(EXCHANGE_NAME,"",null,msg.getBytes("utf-8"));

Receiver修改如下:

// 定义交换机名
final String EXCHANGE_NAME="hello-fanout";
// 声明交换机
channel.exchangeDeclare(EXCHANGE_NAME,BuiltinExchangeType.FANOUT);
// 创建默认排他队列
String queueName = channel.queueDeclare().getQueue();
// 绑定队列到路由
channel.queueBind(queueName,EXCHANGE_NAME,"");

4.路由模式(Routing)

 

Direct:通过指定路由key发送到不同队列,区分不同消费者
缺点:路由规则不灵活
Sender修改部分:

// 发布消息
String msg = "Hello-info";
// 设置路由key
String routingKey = "info";
// 绑定队列到路由
channel.basicPublish(EXCHANGE_NAME,routingKey,null,msg.getBytes("utf-8"));

Receiver修改如下:

// 设置路由key
String routingKey = "info";
// 绑定队列到路由
channel.queueBind(queueName,EXCHANGE_NAME,routingKey);

5.主题模式(Topics)

 

路由规则:

  • . 通过点分割一个单词
  • * 模糊匹配一个单词
  • # 模糊匹配多个单词,可以没有

以上图中的配置为例:

routingKey=”quick.orange.rabbit”的消息会同时路由到Q1与Q2,
routingKey=”lazy.orange.fox”的消息会路由到Q1,Q2,
routingKey=”lazy.brown.fox”的消息会路由到Q2,
routingKey=”lazy.pink.rabbit”的消息会路由到Q2;
routingKey=”quick.brown.fox”、
routingKey=”orange”、
routingKey=”quick.orange.male.rabbit”的消息将会被丢弃,因为它们没有匹配任何bindingKey。

// 声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);

三、消息事务机制

如何保证生产者的消息成功投递到队列,两种方式:transition事务模式confirm确认模式,两种模式不能同时开启。

1.transition事务模式

类似JDBC操作,发送消息前开启事务,成功后提交事务,失败则回滚事务;
缺点:效率低

  • channel.txSelect() 开启事务
  • channel.txCommit() 提交事务
  • channel.txRollback() 回滚事务

2.confirm确认模式

实现生产者confirm 机制有三种方式:

  1. 普通confirm模式:每发送一条消息后,调用waitForConfirms()方法,等待服务器端confirm。实际上是一种串行confirm了。
  2. 批量confirm模式:每发送一批消息后,调用waitForConfirmsOrDie()方法,等待服务器端confirm。
  3. 异步confirm模式:提供一个回调方法,服务端confirm了一条或者多条消息后Client端会回调这个方法。
  • channel.confirmSelect() 开启确认模式
  • channel.waitForConfirms() 等待消息被确认
  • addConfirmListener() 添加异步监听器,重写handleAck消息成功和handleNack消息失败方法

1) 普通confirm模式

// 开启确认模式
channel.confirmSelect();
...
// 等待确认
if(channel.waitForConfirms()){
System.out.println("发送成功。。。");
}

2) 批量confirm模式

// 开启确认模式
channel.confirmSelect();
...
// 等待确认
channel.waitForConfirmsOrDie();
System.out.println("发送成功。。。");

3) 异步confirm模式

// 定义集合存放seqNO
final SortedSet<Long> confirmSet = Collections.synchronizedNavigableSet(new TreeSet<Long>());
...
// 开启确认模式
channel.confirmSelect(); // 绑定监听器
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
if(multiple){
System.out.println("mutil-succe: "+deliveryTag);
confirmSet.headSet(deliveryTag+1L).clear();
}else{
System.out.println("single-succe: "+deliveryTag);
confirmSet.remove(deliveryTag);
}
} @Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
if(multiple){
System.out.println("mutil-fail: "+deliveryTag);
confirmSet.headSet(deliveryTag+1L).clear();
}else{
System.out.println("single-fail: "+deliveryTag);
confirmSet.remove(deliveryTag);
}
}
});
...
// 获取编号
long seqNo = channel.getNextPublishSeqNo();
channel.basicPublish(EXCHANGE_NAME,routingKey,null,msg.getBytes("utf-8"));
System.out.println("Send: "+msg);
//添加编号
confirmSet.add(seqNo);

RabbitMQ实战的更多相关文章

  1. rabbitMQ实战(一)---------使用pika库实现hello world

    rabbitMQ实战(一)---------使用pika库实现hello world 2016-05-18 23:29 本站整理 浏览(267)     pika是RabbitMQ团队编写的官方Pyt ...

  2. Java SpringBoot集成RabbitMq实战和总结

    目录 交换器.队列.绑定的声明 关于消息序列化 同一个队列多消费类型 注解将消息和消息头注入消费者方法 关于消费者确认 关于发送者确认模式 消费消息.死信队列和RetryTemplate RPC模式的 ...

  3. websocket+rabbitmq实战

    1. websocket+rabbitmq实战 1.1. 前言   接到的需求是后台定向给指定web登录用户推送消息,且可能同一账号会登录多个客户端都要接收到消息 1.2. 遇坑 基于springbo ...

  4. celery+RabbitMQ 实战记录2—工程化使用

    上篇文章中,已经介绍了celery和RabbitMQ的安装以及基本用法. 本文将从工程的角度介绍如何使用celery. 1.配置和启动RabbitMQ 请参考celery+RabbitMQ实战记录. ...

  5. RabbitMQ实战经验分享

    前言 最近在忙一个高考项目,看着系统顺利完成了这次高考,终于可以松口气了.看到那些即将参加高考的学生,也想起当年高三的自己. 下面分享下RabbitMQ实战经验,希望对大家有所帮助: 一.生产消息 关 ...

  6. 【RabbitMQ 实战指南】一 RabbitMQ 开发

    1.RabbitMQ 安装 RabbitMQ 的安装可以参考官方文档:https://www.rabbitmq.com/download.html 2.管理页面 rabbitmq-management ...

  7. 【RabbitMQ 实战指南】一 延迟队列

    1.什么是延迟队列 延迟队列中存储延迟消息,延迟消息是指当消息被发送到队列中不会立即消费,而是等待一段时间后再消费该消息. 延迟队列很多应用场景,一个典型的应用场景是订单未支付超时取消,用户下单之后3 ...

  8. RabbitMQ实战应用技巧

    1. RabbitMQ实战应用技巧 1.1. 前言 由于项目原因,之后会和RabbitMQ比较多的打交道,所以让我们来好好整理下RabbitMQ的应用实战技巧,尽量避免日后的采坑 1.2. 概述 Ra ...

  9. rabbitmq实战:一、天降奇兵

    缘由,最近换了工作,而新的项目中使用了celery+rabbitmq来实现一个分布式任务队列系统,为了能够维护好这套系统,只能来学习一下这两个组件,顺便把学习笔记记录下来,留作以后回顾,当然如果碰巧能 ...

  10. Spring Boot 集成 RabbitMQ 实战

    Spring Boot 集成 RabbitMQ 实战 特别说明: 本文主要参考了程序员 DD 的博客文章<Spring Boot中使用RabbitMQ>,在此向原作者表示感谢. Mac 上 ...

随机推荐

  1. Ubuntu 安装MySQL报共享库找不到

    错误信息1: ./mysqld: error : cannot open shared object file: No such file or directory 解决办法:安装改库 # apt-g ...

  2. ckplayer去掉/修改右上角logo(位置)

    ckplayer.js中搜索:logo(ckplayer.xml中搜索<logo>) 1:去掉的方法是修改成logo:'null'(ckplayer.xml中修改成<logo> ...

  3. 微信小程序全局设置分享内容

    微信小程序每个页面都可以在onShareAppMessage中设置分享内容,如果想要全局设置成一样的分享内容如何设置呢? 在app.js中新增以下方法: //重写分享方法 overShare: fun ...

  4. freemarker null异常详解及兼容模式

    在读取user的时候,因为为空,报错了,错误处的代码是这样的 <#if user> 其实准确的写法应该是 <#if user??> 如果要消除错误,需要把前端代码修后成后面这种 ...

  5. 企业架构 Red Hat Drools KIE Project 三大核心产品

    美团放弃Drools自研规则引擎: https://blog.csdn.net/qq_18603599/article/details/80767912 Drools rule engine虽然好,但 ...

  6. Redis常见问题及解决方案

    在Redis的运维使用过程中你遇到过那些问题,又是如何解决的呢?本文收集了一些Redis的常见问题以及解决方案,与大家一同探讨. 码字不易,欢迎大家转载,烦请注明出处:谢谢配合 你的Redis有big ...

  7. Laya微信小游戏的开放域

    版本2.1.1.1 现在Laya的开放域比较好用了. 新建开放域项目,里面直接有个排行榜的示例. 直接发布 得到较少的文件,复制这些文件,粘贴到主项目bin/openDataContext下. (op ...

  8. zookeeper学习整理

    概述 zookeeper是一个高可用的分布式数据管理与协调框架,基于ZAB算法实现,主要解决分布式一致性问题. https://www.cnblogs.com/felixzh/p/5869212.ht ...

  9. Windows网络命令的相关指令(1)

    1.Ipconfig 该命令可以检查网络接口配置.如果用户系统不能到达远程主机,而同一系统的其他主机可以到达,那么用该命令对这种故障进行判断是有必要的.当主机能到达远程主机但不能到达本网主机时,则表示 ...

  10. PHP设计模式 - 模板方法模式

    模板模式准备一个抽象类,将部分逻辑以具体方法以及具体构造形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑.不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现.先制定一个顶级 ...