RabbitMq
一 概述
1、简介
MQ(Messages Queue)消息队列,FIFO(First In First Out),上游生产消息进入信道(Channel),由下游消费。万级吞吐量
2、优势
(1)流量削峰
如:10W订单时同时进行访问应用时,应用负载过大时,将订单消息放入MQ中,进行排队,并且消息队列中消息数量达到一定数量时,可以进行提示,当前访问人数过多。并行操作转换为串行操作。
(2)应用解耦
系统应用之间调用时,如:定单服务调用支付服务时,下单支付的动作直接调用时。将下单支付动作存入MQ中,MQ监听支付服务进行消费,失败时可设置重复消费以及异常监听。
(3)异步处理
如:A请求B时,B响应时间过长,A无需等待,B完成后直接通过A即可。传统时,需要使A一段时间确认B是否完成。
3、核心概念
3.1 名词介绍
(1)生产者
将消息打包成一定格式,发送至队列/交换机
(2)交换机
交换机可以将消息进行分配(bindKey)进行匹配相应的队列。
(3)队列
存入队列中的消息,由消费者进行消费。
(4)消费者
消费消息。
Broker:介绍和分发消息的应用。
Virtual Host:可以在Broker中建立多个服务应用(IP),进行不同应用的消息统一发送。
Connection :建立连接,Producer 、Broker以及Consumer之间需要建立TCP连接
Channel:由于建立连接开销较大,则建立一次连接,采用多信道(Channel)的形式,发一个消息时,只占用一个Channel,可支持发送多个消息,并且Channel采用逻辑连接的形式。
3.2 核心(六大模式)
3.2.1、HelloWorld 直连
![img](https://i-blog.csdnimg.cn/blog_migrate/b782535852338e1b5d04d033f835d300.gif)
在采用代码实现时,注意3.1中的结构图,首先在将消息上传至MQ时需要先完成几个步骤:
-
通过ConnectionFactory配置连接参数创建Connection连接。其中在配置连接参数时,VirtualHost配置相当于配置交换机标识,需要事先在rabbitMq中创建。
-
Connection如3.1中所述,连接仅创建一次,需要从Connection中创建Channel进行消息传递。
-
Channel为传递消息核心类型。
(1)其中提供绑定队列方法channel.queueDeclare(queueName,b1,b2,b3,map),参数详情其中如下:
①queueName:队列名称,上下游必须一致才可完成相应的发送以及接收动作,并且队列名称在Broker中不存在时会主动创建。
②b1是否队列持久化,选择是时,会将队列存储内置mnesia数据库中,否则则是存放在内存中。
③b2是否独占队列,建立一个队列之后,其他channel无法在创建同名队列,该机制仅限一个Connecton之下。
④b3是否自动删除
(2)消费者监听时间basicConsume(String queueName, boolean autoAllow, DeliverCallback handler, CancelCallback cancelCallBack)方法在 Chanel类中提供多个重载方法,以上述举例。
①queueName队列名称,绑定队列。
②autoAllow是否开启消息的自动确认机制
③handler监听事件,读取消息,信道中存在方法时,进行消费。
④cancelCallBack消息通道关闭时,调用该回调方法。
(3)生产者发送消息时,basicPublish(String exChange,String queueName,BasicProperties **otherMessages,**Byte[] message.getBytes()).
①exChange交换机,根据实际情况进行绑定,可为空。
②queueName 队列名称
③otherMessage 消息额外设置。
④message.getBytes,其中将需要转递的参数尽量转为json字符串形式,在将字符串转为Byte进行发送,减小传输成本。
采用代码试下如下,消费者代码:
/**
* 消费者监听如下,
*/
public class Consumer1 {
private static final String QUEUE_NAME = "Myqueue";
public static void main(String[] args) {
System.out.println("start+******************");
receive();
System.out.println("end+******************");
}
public static void receive() {
//声明连接工厂
ConnectionFactory factory = null;
//声明连接对象
Connection connection = null;
//声明通道
Channel channel = null;
try {
//创建连接工厂
factory = new ConnectionFactory();
//设置连接rabbitmq主机
factory.setHost("localhost");
//设置端口
factory.setPort(5672);
//设置虚拟主机
factory.setVirtualHost("/");
///设置连接rabbitmq用户名
factory.setUsername("guest");
//设置连接rabbitmq密码
factory.setPassword("guest");
//创建连接对象
connection = factory.newConnection();
//创建连接通道
channel = connection.createChannel();
//通道绑定消息队列,参数根据消息队列参数对应
//参数1:队列名称
//参数2:绑定队列是否是持久化
//参数3:绑定队列是否是独占
//参数4:绑定队列消费完成是否会自动删除
//参数5:绑定队列额外设置
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
Consumer consumer = new DefaultConsumer(channel) {
//内部类方法中,body为传送消息实体
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接受消息开始~~~~~~~~~~~~");
String message = new String(body, "UTF-8");
System.out.println("收到消息.........." + message);
}
};
//消费消息
//参数1:消费队列的队列名称
//参数2:开始消息的自动确认机制
//参数3:消费时回调
channel.basicConsume(QUEUE_NAME, true, consumer);
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}/*finally {
try {
channel.close();
connection.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}*/
}
}
/**
* 生产者发送消息
*/
public class Sender {
private final static String QUEUE_NAME = "Myqueue";
public static void main(String[] args) {
send();
}
public static void send() {
//声明连接工厂
ConnectionFactory factory = null;
//声明连接对象
Connection connection = null;
//声明信息通道
Channel channel = null;
try {
//创建mq连接工厂
factory = new ConnectionFactory();
//设置连接rabbittmq连接主机
factory.setHost("localhost");
//设置连接端口
factory.setPort(5672);
//设置连那个虚拟主机(此处名为默认的虚拟主机exChange)
factory.setVirtualHost("/");
//设置rabbitmq连接用户名
factory.setUsername("guest");
//设置rabbitmq连接密码
factory.setPassword("guest");
//获取连接对象
connection = factory.newConnection();
//获取连接中通道
channel = connection.createChannel();
//通道绑定消息队列
//参数1:队列名称,如果不存在会自动创建
//参数2:定义队列是否持久化, true:持久化 false:不持久化
//参数3:是否独占队列 true:独占 false:不独占
//参数4:是否消费完成后自动删除队列 true自动删除 false不自动删除
//参数5:额外附加参数
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
String message = "my first rabbitmq transfer1";
//消息传输
//参数1:指定交换机
//参数2:队列名称
//参数3:消息的额外设置
//参数4:消息的具体内容
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
try {
//关闭资源
channel.close();
connection.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
}
}
}
3.2.2 Work Queues(资源的竞争)
工作模式,可以在简单模式基础之上,加入多个生产者或多个消费者对同一个队列进行发送或接收消息。默认会采用轮询的方式进行消费。
public class ProducerTask {
private static final String QUEUE_NAME="Myqueue";
public static void main(String[] args) throws Exception {
try(Channel channel= RabbitUtil.getChannel();)
{ channel.queueDeclare(QUEUE_NAME,true,false,false,null);
//从控制台当中接受信息
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()){
String message = scanner.next();
channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
System.out.println("发送消息完成:"+message);
}
}
}
}
3.3 消息应答