衡量标准
适用于中小型企业,但在高并发情况下效率较低,不适用与并发
高吞吐、速度快,但不支持事务,不能保证消息的可靠性
一般用于日志传输,不适用于事务场景
阿里开发的商业性MQ,商业版收费
高吞吐、高可用
适用于大规模分布式
性能略差于RocketMQ,高于ActiveMQ,且支持大规模分布式,可靠性高,稳定性好
AMQP核心概念
包含几种属性:
correlationId:消息唯一ID
deliverMode:2为持久化消息
expiration:过期时间
headers:消息头,可以存放自定义消息
包含几种属性:
Name:交换机名称
Type:交换机类型direct、topic、fanout、headers
Durability:是否持久化
Auto Delete:当最后一个绑定到Exchange上的队列删除后,自动删除该Exchange
Internal:当前Exchange是否用于RabbitMQ内部使用,默认false
Arguments:扩展参数,扩展AMQP协议自定义化使用
包含几种属性:
Name:队列名称
Durability:是否持久化
Auto Delete:当最后一个监听删除后,自动删除该Queue
//初始化连接
public static Connection getConnection() throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
//服务ip
String HOST = "127.0.0.1";
//默认端口号
Integer PORT = 5672;
//虚拟主机名称
String VIRTUALHOST = "/vhost_z";
//用户名
String USERNAME = "user_z";
//密码
String PASSWORD = "123";
connectionFactory.setHost(HOST);
connectionFactory.setPort(PORT);
connectionFactory.setUsername(USERNAME);
connectionFactory.setPassword(PASSWORD);
connectionFactory.setVirtualHost(VIRTUALHOST);
Connection connection = connectionFactory.newConnection();
return connection;
}
//生产者
public static void main(String[] args) {
try {
//1.获取连接
Connection connection = ConnectionUtils.getConnection();
//2.获取通道
Channel channel = connection.createChannel();
//3.声明队列 可以在消费者端声明
//channel.queueDeclare("test_queue",false,false,false,null);
String msg = "sent message!";
//4.发送消息 如果不声明交换机 那么消息会自动发到amqp的默认交换机上
// 默认交换机会根据routingkey去找匹配的队列
channel.basicPublish("","test_queue",null,msg.getBytes());
//5.关闭通道和连接
channel.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//消费者
public static void main(String[] args) {
try {
//1.获取连接
Connection connection = ConnectionUtils.getConnection();
//2.创建通道
Channel channel = connection.createChannel();
//3.声明队列
channel.queueDeclare("test_queue",false,false,false,null);
//消费者对象
DefaultConsumer defaultConsumer = 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("consumer1===="+msg);
}
};
//4.监听队列
channel.basicConsume("test_queue",false,defaultConsumer);
} catch (Exception e) {
e.printStackTrace();
}
}
QOS(服务质量保证),在非自动确认消息的前提下(一定要手动确认消息),如果一定数目的消息未被确认前,不进行消费新的消息
//生产者
public static void main(String[] args) {
try {
//1.获取连接
Connection connection = ConnectionUtils.getConnection();
//2.获取通道
Channel channel = connection.createChannel();
//是否持久化 true 是
boolean flag = true;
//3.声明队列
channel.queueDeclare("test_queue",flag,false,false,null);
for (int i=0;i<50;i++){
String msg = "sent message i= "+i+"!";
//4.发送消息
channel.basicPublish("","test_queue",null,msg.getBytes());
}
//5.关闭通道和连接
channel.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//消费者1
public static void main(String[] args) {
try {
Thread.sleep(2000);
//1.获取连接
Connection connection = ConnectionUtils.getConnection();
//2.创建通道
Channel channel = connection.createChannel();
//3.声明队列
channel.queueDeclare("test_queue",true,false,false,null);
//保证只发一个消息 需要手动应答 默认工作队列使用轮询,给连个消费者均分消息
//若一个消费者消费的快,想提高效率 那么可以用手动应答
//修改basicQos限制每次消费1条消息 谁先消费完 就应答 再让生产者发送一条消息
channel.basicQos(1);
DefaultConsumer defaultConsumer = 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("consumer1===="+msg);
//手动应答
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
//4.监听队列
channel.basicConsume("test_queue",false,defaultConsumer);
} catch (Exception e) {
e.printStackTrace();
}
}
//消费者2
public static void main(String[] args) {
try {
Thread.sleep(1000);
//1.获取连接
Connection connection = ConnectionUtils.getConnection();
//2.创建通道
Channel channel = connection.createChannel();
//3.定义消费者
channel.queueDeclare("test_queue",false,false,false,null);
//保证只发一个消息
channel.basicQos(1);
DefaultConsumer defaultConsumer = 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("consumer2===="+msg);
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
//4.监听队列
channel.basicConsume("test_queue",true,defaultConsumer);
} catch (Exception e) {
e.printStackTrace();
}
}
在自动应答模式下,消息发送完后消息就会删除,但此时消费者服务宕机,消息就会丢失
手动应答模式,在消息消费完,应答之后消息才会删除,但内存是断电清空,为了防止MQ服务器宕机,可以将消息持久化,将队列也声明为持久化(已声明的队列无法再次修改属性)
//手动应答 将第二个参数设为false
channel.basicConsume("test_queue",flase,defaultConsumer);
//生产者
public static void main(String[] args) {
try {
//1.获取连接
Connection connection = ConnectionUtils.getConnection();
//2.获取通道
Channel channel = connection.createChannel();
String exchangeName = "test_ack_exchange";
String routingKey = "ack.info";
//5.发送消息
for(int i = 0;i<5;i++){
Map<String,Object> headers = new HashMap();
String msg = "sent ack message is info" + i;
headers.put("num",i);
AMQP.BasicProperties basicProperties = new AMQP.BasicProperties.Builder().
deliveryMode(2).//消息持久化设置
contentEncoding("utf-8").
headers(headers).
build();
channel.basicPublish(exchangeName,routingKey,true,basicProperties,msg.getBytes());
}
//6.关闭通道和连接
channel.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//消费者
public static void main(String[] args) {
try {
//1.获取连接
Connection connection = ConnectionUtils.getConnection();
//2.创建通道
Channel channel = connection.createChannel();
String exchangeName = "test_ack_exchange";
String queueName = "test_ack_queue";
String routingKey = "ack.#";
//3.声明队列和交换机
channel.exchangeDeclare(exchangeName,"topic",true,false,false,null);
channel.queueDeclare(queueName,false,false,false,null);
//4.绑定队列和路由key
channel.queueBind(queueName,exchangeName,routingKey);
//保证只发一个消息
//第一个参数指消息总数量
//第二个参数时每次消费次数
//第三个参数 是否将整个channel都设置成该配置
channel.basicQos(0,1,false);
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
Integer num = (Integer) properties.getHeaders().get("num");
System.out.println("body="+new String(body,"utf-8") +" i="+num);
String msg = new String(body,"utf-8");
if(num.equals(0))