AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。
AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
package cn.com.laoli.rabitmq.utils;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ConnectionUtils {
/**
* 获取mq的连接
*
* @return
*/
public static Connection getConnection() throws IOException, TimeoutException {
//定义一个连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置服务地址
factory.setHost("127.0.0.1");
//设置端口号 AMQP协议
factory.setPort(5672);
//设置vhost
factory.setVirtualHost("你设置的vhost");
//用户名
factory.setUsername("你的用户名");
//密码
factory.setPassword("你的密码");
return factory.newConnection();
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Send {
private static final String QUEUE_NAME = "test_sample_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//获取一个连接
Connection connection = ConnectionUtils.getConnection();
//创建一个通道
Channel channel = connection.createChannel();
//创建队列声明
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
String msg = "hello simple !";
//发布
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
System.out.println("------send msg:"+msg);
channel.close();
connection.close();
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.TimeoutException;
public class Receive {
private static final String QUEUE_NAME = "test_sample_queue";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取链接
Connection connection = ConnectionUtils.getConnection();
//创建通道
Channel channel = connection.createChannel();
/*//老版本 定义队列消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
//监听队列
channel.basicConsume(QUEUE_NAME,true,queueingConsumer);
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("[recv] msg :"+msg);
}*/
//队列声明
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
//事件模型,一旦有消息进入就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body, "utf-8");
System.out.println("new api recv:" + msg);
}
};
//监听队列
channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
}
}
耦合性高,生产者一一对应消费者(如果我想有多个消费者消费队列中的消息,这时就不行了)
队列名变更,这时候得同时变更
Simple队列 是一一对应的,且实际开发,生产者发送消息是毫不费力的,而消费者一般是要跟业务相结合的。消费者接收到消息之后就需要处理 可能需要花费时间,这时候队列就积压了好多消息
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Send {
private static final String QUEUE__NAME = "test_work_queue";
/** |----c1
* p----Queue-----
* |----c2
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取channel
Channel channel = connection.createChannel();
//声明一个队列
channel.queueDeclare(QUEUE__NAME,false,false,false,null);
for (int i=0;i<50;i++){
String msg = "hello "+i;
channel.basicPublish("",QUEUE__NAME,null,msg.getBytes());
Thread.sleep(i*10);
}
channel.close();
connection.close();
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Receive {
private static final String QUEUE__NAME = "test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取通道channel
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 {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body,"utf-8");
System.out.println("recv1 msg:"+msg);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("1 done");
}
}
};
//后续会说明这个是什么
boolean autoAck = true;
//监听队列
channel.basicConsume(QUEUE__NAME,autoAck,consumer);
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Receive2 {
private static final String QUEUE__NAME = "test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取通道channel
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 {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body,"utf-8");
System.out.println("recv2 msg:"+msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("2 done");
}
}
};
//后续会说明这个是什么
boolean autoAck = true;
//监听队列
channel.basicConsume(QUEUE__NAME,autoAck,consumer);
}
}
消费者1和消费者2处理的消息是一样的
消费者1:偶数
消费者2:奇数
这种方式叫轮询分发(round-robin)结果就是不管谁忙或闲,都不会多给一个
任务消息总是你一个我一个
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Send {
private static final String QUEUE__NAME = "test_work_queue";
/** |----c1
* p----Queue-----
* |----c2
* @param args
* @throws IOException
* @throws TimeoutException
*/
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取channel
Channel channel = connection.createChannel();
//声明一个队列
channel.queueDeclare(QUEUE__NAME,true,false,false,null);
/**
* 每个消费者 发送确认消息之前,消息队列不发送下一个消息到消费者,一次只处理一个消息
*
* 限制发送给同意消费者不超过一条
*/
int prefetchCount = 1;
channel.basicQos(prefetchCount);
for (int i=0;i<50;i++){
String msg = "hello "+i;
channel.basicPublish("",QUEUE__NAME,null,msg.getBytes());
System.out.println("发送"+i+":"+msg);
Thread.sleep(i*10);
}
channel.close();
connection.close();
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Receive {
private static final String QUEUE__NAME = "test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取通道channel
final Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE__NAME,false,false,false,null);
//保证一次只发一个
int prefetchCount = 1;
channel.basicQos(prefetchCount);
//定义一个消费者
Consumer consumer =new DefaultConsumer(channel){
//消息到达就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body,"utf-8");
System.out.println("recv1 msg:"+msg);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("1 done");
//第一个参数是一个标识 表示已经收到消息 手动回值
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//自动应答
boolean autoAck = false;
channel.basicConsume(QUEUE__NAME,autoAck,consumer);
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Receive2 {
private static final String QUEUE__NAME = "test_work_queue";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取通道channel
final Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE__NAME,false,false,false,null);
//保证一次只发一个
int prefetchCount = 1;
channel.basicQos(prefetchCount);
//定义一个消费者
Consumer consumer =new DefaultConsumer(channel){
//消息到达就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body,"utf-8");
System.out.println("recv2 msg:"+msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("2 done");
//第一个参数是一个标识 表示已经收到消息 手动回值
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//自动应答
boolean autoAck = false;
channel.basicConsume(QUEUE__NAME,autoAck,consumer);
}
}
消费者2处理的比消费者1多 能者多劳 公平分发
boolean autoAck = false;
channel.basicConsume(QUEUE__NAME,autoAck,consumer);
手动模式 如果有一个消费者挂掉 就会交付给其他消费者
rabbitmq支持消息应答,消费者发送一个消息应答告诉rabbitmq这个消息我已经处理完成,可以删了,然后rabbitmq就删除内存中的数据
自动确认模式 表示一旦rabbitmq将消息发送给消费者,消息就会从内存中删除
如果rabbitmq挂掉了 消息可能会丢失
如果杀死正在执行的消费者,就会丢失正在处理的消息
//声明队列
boolean durable = false
channel.queueDeclare(QUEUE__NAME,durable,false,false,null);
我们将程序中的durable=false改为true是不可以的,尽管代码是正确的,也不会运行成功!!!
因为我们已经定义了一个叫test_work_queue,这个queue是未持久化的,tabbitmq不允许重新定以(不同参数)一个已存在的队列
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Send {
private static final String EXCHANGE_NAME="test_exchange_fanout";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//通道
Channel channel = connection.createChannel();
//声明交换机\
//分发类型
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
//发送消息
String msg = "hello_ps";
channel.basicPublish(EXCHANGE_NAME,"",null,msg.getBytes());
System.out.println("send:"+msg);
channel.close();
connection.createChannel();
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Recv {
private static final String QUEUE_NAME = "test_queue_fanout_email";
private static final String EXCHANGE_NAME="test_exchange_fanout";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取通道channel
final Channel channel = connection.createChannel();
//队列声明
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//绑定交换机
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"");
//保证一次只发一个
int prefetchCount = 1;
channel.basicQos(prefetchCount);
//定义一个消费者
Consumer consumer =new DefaultConsumer(channel){
//消息到达就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body,"utf-8");
System.out.println("recv1 msg:"+msg);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("1 done");
//第一个参数是一个标识 表示已经收到消息 手动回值
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//自动应答
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Recv2 {
private static final String QUEUE_NAME = "test_queue_fanout_sms";
private static final String EXCHANGE_NAME="test_exchange_fanout";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取通道channel
final Channel channel = connection.createChannel();
//队列声明
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//绑定交换机
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"");
//保证一次只发一个
int prefetchCount = 1;
channel.basicQos(prefetchCount);
//定义一个消费者
Consumer consumer =new DefaultConsumer(channel){
//消息到达就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body,"utf-8");
System.out.println("recv2 msg:"+msg);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("2 done");
//第一个参数是一个标识 表示已经收到消息 手动回值
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//自动应答
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);
}
}
只要是绑定了的都能收到消息
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
发送的时候需要带一个key,队列也需要带一个key,如果相匹配就会把消息转到对应的队列里
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Send {
private static final String EXCHANGE_NAME = "test_exchange_direct";
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取通道
Channel channel = connection.createChannel();
//声明exchange 声明交换机
channel.exchangeDeclare(EXCHANGE_NAME,"direct");
String msg = "hello Direct";
String routingKey = "info";
channel.basicPublish(EXCHANGE_NAME,routingKey,null,msg.getBytes());
System.out.println("send:"+msg);
channel.close();
connection.close();
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Recv {
private static final String EXCHANGE_NAME = "test_exchange_direct";
private static final String QUEUE_NAME = "test_queue_direct1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
final Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"error");
//每次只发一个消息
channel.basicQos(1);
Consumer consumer =new DefaultConsumer(channel){
//消息到达就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body,"utf-8");
System.out.println("recv1 msg:"+msg);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("1 done");
//第一个参数是一个标识 表示已经收到消息 手动回值
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//自动应答
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Recv2 {
private static final String EXCHANGE_NAME = "test_exchange_direct";
private static final String QUEUE_NAME = "test_queue_direct2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
final Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"error");
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"info");
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"warning");
//每次只发一个消息
channel.basicQos(1);
Consumer consumer =new DefaultConsumer(channel){
//消息到达就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body,"utf-8");
System.out.println("recv2 msg:"+msg);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("2 done");
//第一个参数是一个标识 表示已经收到消息 手动回值
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//自动应答
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Send {
private static final String EXCHANGE_NAME = "test_exchange_topic";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME,"topic");
String msg = "商品.....";
System.out.println("send:"+msg);
channel.basicPublish(EXCHANGE_NAME,"goods.add",null,msg.getBytes());
channel.close();
connection.close();
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Recv {
private static final String EXCHANGE_NAME = "test_exchange_topic";
private static final String QUEUE_NAME = "test_queue_topic1";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
final Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"goods.add");
//每次只发一个消息
channel.basicQos(1);
Consumer consumer =new DefaultConsumer(channel){
//消息到达就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body,"utf-8");
System.out.println("recv1 msg:"+msg);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("1 done");
//第一个参数是一个标识 表示已经收到消息 手动回值
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//自动应答
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Recv2 {
private static final String EXCHANGE_NAME = "test_exchange_topic";
private static final String QUEUE_NAME = "test_queue_topic2";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
final Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"goods.#");
//每次只发一个消息
channel.basicQos(1);
Consumer consumer =new DefaultConsumer(channel){
//消息到达就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
String msg = new String(body,"utf-8");
System.out.println("recv2 msg:"+msg);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("2 done");
//第一个参数是一个标识 表示已经收到消息 手动回值
channel.basicAck(envelope.getDeliveryTag(),false);
}
}
};
//自动应答
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME,autoAck,consumer);
}
}
在rabbitmq中,我们可以通过持久化数据,解决rabbitmq服务器异常的数据丢失问题
生产者将消息发送出去之后吗,消息到底有没有到达rabbitmq服务器,默认情况是不知道的
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Send {
private static final String QUEUE_NAME = "test_queue_tx";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
String msg = "hello";
try {
channel.txSelect();
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
int xx = 1/0;
channel.txCommit();
}catch (Exception e){
channel.txRollback();
System.out.println("seng msg rollback");
}
channel.close();
connection.createChannel();
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Recv {
private static final String QUEUE_NAME = "test_queue_tx";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.basicConsume(QUEUE_NAME,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
System.out.println("rev msg :"+new String(body,"utf-8"));
}
});
}
}
生产者将信道设置成confirm模式,一旦信道进入confirm模式,所有在该信道上发布的消息都会被指派一个唯一ID(从1开始),一旦消息被投递到所有匹配的队列之后,broker就会发送一个确认给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了,如果消息和队列是可持久化的,那么确认消息会将消息写入磁盘之后发出,broker回传给生产者的确认消息中deliver-tag域包含了确认消息的序列号,此外broker也可以设置basic.ack的multiple域,表示到这个序列号之前的所有消息都已经得到了处理
confirm最大的好处在于他是异步
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Send1 {
private static final String QUEUE_NAME = "test_queue_confirm1";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//生产者调用 confirmSelect 将channel设为confirm模式 注意得是一个新队列如果之前开了事务模式在开confirm模式会报错 rabbitmq不允许
channel.confirmSelect();
String msg = "hello confir message";
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
if (!channel.waitForConfirms()){
System.out.println("消息发送失败");
}else{
System.out.println("发送成功");
}
channel.close();
connection.close();
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Recv {
private static final String QUEUE_NAME = "test_queue_confirm3";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
channel.basicConsume(QUEUE_NAME,true,new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
super.handleDelivery(consumerTag, envelope, properties, body);
System.out.println("rev msg :"+new String(body,"utf-8"));
}
});
}
}
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author: LiDeLin [email protected]
* @description : 普通模式 多条 比单条效率高 但是如果有一条失败那么全部消息都会被返回
*/
public class Send2 {
private static final String QUEUE_NAME = "test_queue_confirm1";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//生产者调用 confirmSelect 将channel设为confirm模式 注意得是一个新队列如果之前开了事务模式在开confirm模式会报错 rabbitmq不允许
channel.confirmSelect();
String msg = "hello confir message";
for (int i = 0; i<=9 ;i++){
channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
}
if (!channel.waitForConfirms()){
System.out.println("消息发送失败");
}else{
System.out.println("发送成功");
}
channel.close();
connection.close();
}
}
同上
channel对象提供的ConfirmListener()回调方法只包含deliverTag(当前Channelfa发出的消息序列号),我们需要自己为每一个Channel维护一个unconfirm的消息序号集合,每publishi一条数据,集合元素加1,每回调一次handleAck方法吗,unconfirm集合删掉相应的一条(multiple=false)或者多条(multiple=true)记录,从程序运行效率上看,这个unconfirm集合最好采用有序集合SortedSet存储结构
import cn.com.laoli.rabitmq.utils.ConnectionUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import java.io.IOException;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.TimeoutException;
public class Send3 {
private static final String QUEUE_NAME = "test_queue_confirm3";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//生产者调用confirmSelect 将channel设置为confirm模式
channel.confirmSelect();
//存放未确认的消息的标识
final SortedSet confirmSet = Collections.synchronizedSortedSet(new TreeSet());
channel.addConfirmListener(new ConfirmListener() {
//没有问题的
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
if (multiple){
System.out.println("handleAck----multiple");
confirmSet.headSet(deliveryTag+1).clear();
}else{
System.out.println("handleAck----multiple false");
confirmSet.remove(deliveryTag);
}
}
//回值有问题的
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
if (multiple){
System.out.println("nak-----multiple");
confirmSet.headSet(deliveryTag+1).clear();
}else{
System.out.println("nak-----multiple---false");
confirmSet.remove(deliveryTag);
}
}
});
String msgStr = "sssss";
while (true){
long seqNo = channel.getNextPublishSeqNo();
channel.basicPublish("",QUEUE_NAME,null,msgStr.getBytes());
confirmSet.add(seqNo);
}
}
}
同上
server:
port: 8091
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
# 开启confirm
publisher-confirms: true
listener:
simple:
# 手动ack
acknowledge-mode: manual
org.springframework.boot
spring-boot-starter-amqp
注入
@Autowired
private RabbitTemplate rabbitTemplate;
这个是springboot提供的
/**
* 简单模型
*/
@Test
public void test(){
rabbitTemplate.convertAndSend("hello","yue xin 10k");
}
@Component
/**
* 简单模式
* queuesToDeclare:没有队列就声明一个队列
* queue的属性可以直接设置,布尔值是字符串,默认非持久化,非独占,不是自动删除的队列
*/
//@RabbitListener(queuesToDeclare = @Queue(value = "hello",declare = "true",autoDelete = "true"))
@RabbitListener(queuesToDeclare = @Queue("hello"))
@Slf4j
public class HelloConsumer {
/**
* 取出消息后的回调函数
* @param message
*/
@RabbitHandler
public void receive(String message){
log.info("收到的消息:{}",message);
}
}
/**
* 工作队列,默认是轮询分发,开启自动应答即可能者多劳,配置文件中开启,当前已开启
*/
@Test
public void testWork(){
for (int i = 0; i < 20; i++) {
rabbitTemplate.convertAndSend("work","work模型");
}
}
@Component
@Slf4j
public class WorkConsumer {
@RabbitListener(queuesToDeclare = @Queue("work"))
public void receive1(String message) throws InterruptedException {
// Thread.sleep(2000);
System.out.println("消费者1");
log.info("消费者1:------------------>{}",message);
}
@RabbitListener(queuesToDeclare = @Queue("work"))
public void receive2(String message) throws InterruptedException {
// Thread.sleep(1000);
System.out.println("消费者2");
}
}
/**
* fanout广播
*/
@Test
public void testFanout(){
rabbitTemplate.convertAndSend("logs","","Fanout的模型传递发送的消息");
}
@Component
public class FanoutConsumer {
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,//创建临时队列
exchange = @Exchange(value = "logs",type = "fanout") //绑定的交换机
)
})
public void receive1(String message){
System.out.println("message1"+message);
}
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,//创建临时队列
exchange = @Exchange(value = "logs",type = "fanout") //绑定的交换机
)
})
public void receive2(String message){
System.out.println("message2"+message);
}
}
/**
* route 路由模式
*/
@Test
public void testRoute(){
rabbitTemplate.convertAndSend("directs","info","发送info的key的路由信息");
}
@Component
public class RouteConsumer {
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,//创建临时队列
exchange = @Exchange(value = "directs",type = "direct"), //绑定的交换机
key = {"info","error","debug"} //路由key
)
})
public void receive1(String message){
System.out.println("message1"+message);
}
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,//创建临时队列
exchange = @Exchange(value = "directs",type = "direct"), //绑定的交换机
key = {"error"}
)
})
public void receive2(String message){
System.out.println("message2"+message);
}
}
/**
* topic 动态路由/订阅模式
*/
@Test
public void testTopic(){
rabbitTemplate.convertAndSend("topics","user.save","user.save 路由消息");
}
@Component
public class TopicConsumer {
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,//创建临时队列
exchange = @Exchange(value = "topics",type = "topic"), //绑定的交换机
key = {"user.save","user.*"} //路由key
)
})
public void receive1(String message){
System.out.println("message1"+message);
}
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,//创建临时队列
exchange = @Exchange(value = "topics",type = "topic"), //绑定的交换机
key = {"order.#","product.#","user.*"}
)
})
public void receive2(String message){
System.out.println("message2"+message);
}
}