一、MQ(Message Queue:消息队列)
1、消息队列就是在消息传输过程中保存消息的容器
2、为什么用MQ?
在复杂的系统中,一个完整的系统是由多个子系统组成的,子系统往往可以单独使用,也可以和别的系统组合使用。这就要保证子系统间的信息通信,如果采用传统的远程同步调用就会增大系统耦合性,而消息中间件就能对应用解耦。
3、MQ优点
应用解耦:提高系统容错性和可维护性
异步提速:提升用户体验和系统吞吐量
削峰填谷:提高系统稳定性
常见的MQ:
二、rabbitMQ 五种模式
1、简单模式:一个生产者、一个消费者
生产者
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Product {
public static void main(String[] args) throws Exception{
//创建连接工厂 --配置连接信息
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.137.159");
//创建连接对象Connection
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//创建队列
/**
* String queue, 队列的名称
* boolean durable, 是否该队列持久化
* boolean exclusive, 是否独占 false
* boolean autoDelete, 是否自动删除
* Map<String, Object> arguments 额外参数 先给null
*/
channel.queueDeclare("zyb",true,false,false,null);
String msg = "22222221.....";
//发生消息
/**
* String exchange: 交换机的名称 如果没有则使用“” 它回自动采用默认
* String routingKey, 路由key 如果没有交换机的绑定 使用队列的名称
* BasicProperties props, 消息的一些额外配置 目前先不加 null
* byte[] body 消息的内容
*/
channel.basicPublish("", "zyb", null, msg.getBytes());
}
}
生产者启动
消费者
import com.rabbitmq.client.*;
import java.io.IOException;
public class Consumer01 {
public static void main(String[] args) throws Exception{
//创建连接工厂 --配置连接信息
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.137.159");
//创建连接对象Connection
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//接受消息
/**
* String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
DefaultConsumer callback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者:"+new String(body));
}
};
channel.basicConsume("zyb",true,callback);
}
}
消费者启动成功并获取到生产者生产的信息
队列中信息被消费
2、工作者模式:一个生产者、多个消费者、同一个队列,消费者间存在竞争关系
这个模式应该先启动消费者,再启动生产者,这样容易看出差别!
生产者
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Product {
public static void main(String[] args) throws Exception{
//创建连接工厂 --配置连接信息
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.137.159");
//创建连接对象Connection
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//创建队列
/**
* String queue, 队列的名称
* boolean durable, 是否该队列持久化
* boolean exclusive, 是否独占 false
* boolean autoDelete, 是否自动删除
* Map<String, Object> arguments 额外参数 先给null
*/
channel.queueDeclare("zyb",true,false,false,null);
//发生消息
/**
* String exchange: 交换机的名称 如果没有则使用“” 它回自动采用默认
* String routingKey, 路由key 如果没有交换机的绑定 使用队列的名称
* BasicProperties props, 消息的一些额外配置 目前先不加 null
* byte[] body 消息的内容
*/
for (int i=0;i<10;i++) {
String msg = "22222221....."+i;
channel.basicPublish("", "zyb", null, msg.getBytes());
channel.close();
connection.close();
}
}
}
消费者:消费者的代码基本一样,可以改变输出信息来区分信息消费人
import com.rabbitmq.client.*;
import java.io.IOException;
public class Consumer01 {
public static void main(String[] args) throws Exception{
//创建连接工厂 --配置连接信息
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.137.159");
//创建连接对象Connection
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//接受消息
/**
* (String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
DefaultConsumer callback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(1000l);//设置延时来查看两个消费者的消费过程
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者01:"+new String(body));
}
};
channel.basicConsume("zyb",true,callback);
}
}
3、订阅模式(广播模式):一个生产者,多个消费者,多个队列,交换机转发信息
生产者
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Product {
public static void main(String[] args) throws Exception{
//创建连接工厂 --配置连接信息
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.137.159");
//创建连接对象Connection
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//创建队列
/**
* String queue, 队列的名称
* boolean durable, 是否该队列持久化 rabbitMQ服务重启后该存放是否存在。
* boolean exclusive, 是否独占 false
* boolean autoDelete, 是否自动删除 如果长时间没有发生消息 则自动删除
* Map<String, Object> arguments 额外参数 先给null
*/
channel.queueDeclare("zyb01",true,false,false,null);
channel.queueDeclare("zyb02",true,false,false,null);
//创建交换机
/**
* String exchange,交换机的名称
* BuiltinExchangeType type, 交换机的类型
* boolean durable:是否持久化
*/
channel.exchangeDeclare("zyb_change", BuiltinExchangeType.FANOUT,true);
/**
* String queue, 队列名
* String exchange, 交换机的名称
* String routingKey:路由key 如果交换机为fanout模式则不需要路由key
*/
channel.queueBind("zyb01","zyb_change","");
channel.queueBind("zyb02","zyb_change","");
//发生消息
/**
* String exchange: 交换机的名称 如果没有则使用“” 它回自动采用默认
* String routingKey, 路由key 如果没有交换机的绑定 使用队列的名称
* BasicProperties props, 消息的一些额外配置 目前先不加 null
* byte[] body 消息的内容
*/
for(int i=0;i<10;i++) {
String msg = "22222221....."+i;
channel.basicPublish("zyb_change", "", null, msg.getBytes());
}
//生产者这里可以管理资源 消费者不能关闭资源。
channel.close();
connection.close();
}
}
消费者01
import com.rabbitmq.client.*;
import java.io.IOException;
public class Consumer01 {
public static void main(String[] args) throws Exception{
//创建连接工厂 --配置连接信息
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.137.159");
//创建连接对象Connection
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//接受消息
/**
* (String queue, 队列的名称
* boolean autoAck, 是否自动确认
* Consumer callback: 回调方法 当队列中存在信息后 会自动触发回调函数。
*/
DefaultConsumer callback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者01:"+new String(body));
}
};
channel.basicConsume("zyb01",true,callback);
}
}
消费者02
package com.zyb.test;
import com.rabbitmq.client.*;
import java.io.IOException;
public class Consumer02 {
public static void main(String[] args) throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.137.159");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
DefaultConsumer callback = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者02:"+new String(body));
}
};
channel.basicConsume("zyb02",true,callback);
}
}
4、路由模式:一个生产者、多个消费者、多个队列、交换机转发消息、绑定队列时设置routekey,匹配routekey才能到达队列
这个模式下,先启动生产者,在逐个启动消费者,可以看出明显的差异
package com.zyb.direct;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @Author zyb
* @Description TODO
* @date 2021/4/19 19:46
* @Version 1.0
*/
public class Product {
public static void main(String[] args) throws Exception{
//创建连接工厂 --配置连接信息
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.137.159");
//创建连接对象Connection
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//创建队列
/**
* String queue, 队列的名称
* boolean durable, 是否该队列持久化 rabbitMQ服务重启后该存放是否存在。
* boolean exclusive, 是否独占 false
* boolean autoDelete, 是否自动删除 如果长时间没有发生消息 则自动删除
* Map<String, Object> arguments 额外参数 先给null
*/
channel.queueDeclare("zyb_queue_direct01",true,false,false,null);
channel.queueDeclare("zyb_queue_direct02",true,false,false,null);
//创建交换机
/**
* String exchange,交换机的名称
* BuiltinExchangeType type, 交换机的类型
* boolean durable:是否持久化
*/
channel.exchangeDeclare("zyb_change_direct", BuiltinExchangeType.FANOUT,true);
/**
* String queue, 队列名
* String exchange, 交换机的名称
* String routingKey:路由key 如果交换机为fanout模式则不需要路由key
*/
channel.queueBind("zyb_queue_direct01","zyb_change_direct","error");
channel.queueBind("zyb_queue_direct02","zyb_change_direct","info");
channel.queueBind("zyb_queue_direct02","zyb_change_direct","error");
channel.queueBind("zyb_queue_direct02","zyb_change_direct","warning");
for(int i=0;i<10;i++) {
String msg = "22222221....."+i;
channel.basicPublish("zyb_change_direct", "info", null, msg.getBytes());
}
channel.close();
connection.close();
}
}
消费者
代码和订阅模式相同,但是注意交换机名称和队列名
启动消费者01,没有信息输出,因为它的队列中没有信息
启动消费者02,信息输出
更改routingkey为error
5、topic主体模式
绑定按照通配符的模式。
*: 统配一个单词。
#: 统配n个单词
生产者
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Product {
public static void main(String[] args) throws Exception{
//创建连接工厂 --配置连接信息
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.137.159");
//创建连接对象Connection
Connection connection = factory.newConnection();
//创建信道
Channel channel = connection.createChannel();
//创建队列
/**
* String queue, 队列的名称
* boolean durable, 是否该队列持久化 rabbitMQ服务重启后该存放是否存在。
* boolean exclusive, 是否独占 false
* boolean autoDelete, 是否自动删除 如果长时间没有发生消息 则自动删除
* Map<String, Object> arguments 额外参数 先给null
*/
channel.queueDeclare("zyb_queue_topic01",true,false,false,null);
channel.queueDeclare("zyb_queue_topic02",true,false,false,null);
//创建交换机
/**
* String exchange,交换机的名称
* BuiltinExchangeType type, 交换机的类型
* boolean durable:是否持久化
*/
channel.exchangeDeclare("zyb_exchange_topic", BuiltinExchangeType.TOPIC,true);
/**
* String queue, 队列名
* String exchange, 交换机的名称
* String routingKey:路由key 如果交换机为fanout模式则不需要路由key
*/
channel.queueBind("zyb_queue_topic01","zyb_exchange_topic","*.orange.*");
channel.queueBind("zyb_queue_topic02","zyb_exchange_topic","*.*.rabbit");
channel.queueBind("zyb_queue_topic02","zyb_exchange_topic","lazy.#");
for(int i=0;i<10;i++) {
String msg = "22222221....."+i;
channel.basicPublish("zyb_exchange_topic", "lazy.orange.rabbit", null, msg.getBytes());
}
channel.close();
connection.close();
}
}
消费者
代码和订阅模式相同,但是注意交换机名称和队列名
消费者执行结果:
更改routingkey