1.消息队列解决了什么问题?
异步处理
应用解耦
流量削峰
日志处理
2.rabbitMQ安装与配置
echo 'deb http://www.rabbitmq.com/debian/ testing main' | sudo tee /etc/apt/sources.list.d/rabbitmq.list
wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add -
sudo apt-get update
sudo apt-get install rabbitmq-server
invoke-rc.d rabbitmq-server stop/start/etc我们看见进程是启动的
打开管理页面
sudo rabbitmq-plugins enable rabbitmq_management
查看安装的插件
sudo rabbitmqctl list_users
查看用户
sudo rabbitmqctl list_users
新增管理员用户
sudo rabbitmqctl add_user admin admin
sudo rabbitmqctl set_user_tags admin administrator
用刚设置的账户登录管理页面
http://127.0.0.1:15672
3.Virtual hosts 管理
相当于mysql 的 db
创建数据库一般以/开头
创建完成后需要授权
获取MQ连接
public static Connection getConnection() {
//定义连接工厂类
ConnectionFactory factory = new ConnectionFactory();
//设置服务器地址
factory.setHost("127.0.0.1");
//AMQP端口 5672
factory.setPort(5672);
//vhost
factory.setVirtualHost("/vhost_mmr");
//用户名
factory.setUsername("admin");
//密码
factory.setPassword("admin");
try {
return factory.newConnection();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
- 生产者发送消息
private static final String QUEUE_NAME = "test_simple_queue";
public static void main(String[] args) {
//获取一个连接
Connection connection = ConnectionUtils.getConnection();
try {
//从连接中获取一个队列
Channel createChannel = connection.createChannel();
//创建队列声明
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
//消息
String msg = "hello simple 3";
//发送消息
createChannel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
System.out.println("send msg " + msg);
try {
createChannel.close();
connection.close();
} catch (TimeoutException e) {
e.printStackTrace();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
- 消费者接受消息:老方法旧版本
private static final String QUEUE_NAME = "test_simple_queue";
public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//连接中获取队列
Channel createChannel = connection.createChannel();
// createChannel.
QueueingConsumer consumer = new QueueingConsumer(createChannel);
createChannel.basicConsume(QUEUE_NAME, true, consumer);
while (true) {
Delivery nextDelivery = consumer.nextDelivery();
String value = new String(nextDelivery.getBody());
System.out.println("Recv = " + value);
}
}
- 消费者接受消息:新方法
//获取连接
Connection connection = ConnectionUtils.getConnection();
//连接中获取队列
Channel createChannel = connection.createChannel();
//创建队列声明
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
//定义消费者
DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
//获取到达的消息
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
String value = new String(body);
System.out.println("recv msg =" + value);
}
};
//监听队列
createChannel.basicConsume(QUEUE_NAME, true, defaultConsumer);
简单队列不足 耦合性高,生产者—对应消费者(如果有多个消费者想获取队列中的消息,就不行了)
5.work queues
为什么会出现工作队列,simple队列是一一对应的,而且我们实际开发中,生产者发送消息是毫不费力的,而消费者一般是要跟业务相结合的,消费者接收到消息之后就需要处理,可能会话费时间这个时候队列就会挤压很多消息.
- 轮询分发
- 生产者:发送消息
private static final String QUEUE_NAME = "test_worker_queue";
public static void main(String[] args) throws IOException, InterruptedException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取队列
Channel createChannel = connection.createChannel();
//声明队列
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
//发死你个数据
for (int i = 0; i < 50; i++) {
String msg = "hello" + i;
System.out.println("worker queue Send msg = " + msg);
createChannel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
Thread.sleep(i * 20);
}
createChannel.close();
connection.close();
}
- 消费者1:接受消息
private static final String QUEUE_NAME = "test_worker_queue";
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取队列
Channel createChannel = connection.createChannel();
//队列声明
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
//等AMQP中数据时会出发handleDelivery方法
DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
String str = new String(body, "utf-8");
System.out.println("{1} Recv query Msg = " + str);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("{1} done");
}
}
};
boolean autoAck = true;
createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
}
- 消费者2:接收消息
private static final String QUEUE_NAME = "test_worker_queue";
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取队列
Channel createChannel = connection.createChannel();
//队列声明
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
//等AMQP中数据时会出发handleDelivery方法
DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
String str = new String(body, "utf-8");
System.out.println("{2} Recv query Msg = " + str);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("{2} done");
}
}
};
boolean autoAck = true;
createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
}
现象
虽然消费者1与消费者2 都设置了不同时间的延迟,但是消费者1 与消费者2 处理的数据消息是一样的,这种方式叫做轮询分发,不管谁类谁轻松,都不会给多给谁多一个或少一个,你一个我一个
work queues 公平分发
公平分发保证每次发送一条数据,当消费者处理完成一条消息后告诉队列发送下一条消息给消费者,手动确认消息
- 生产者:发送消息
private static final String QUEUE_NAME = "test_worker_queue";
public static void main(String[] args) throws IOException, InterruptedException, TimeoutException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取队列
Channel createChannel = connection.createChannel();
//声明队列
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
/**
* 每个消费者发送确认消息之前,消息队列不发送下一条数据到消费者,一次只处理一个消息
*
* 限制发送给同一个消费者不得吵过一条消息
*/
int prefetchCount = 1;
createChannel.basicQos(prefetchCount);
//发死你个数据
for (int i = 0; i < 50; i++) {
String msg = "hello" + i;
System.out.println("worker queue Send msg = " + msg);
createChannel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
Thread.sleep(i * 20);
}
createChannel.close();
connection.close();
}
- 消费者1
private static final String QUEUE_NAME = "test_worker_queue";
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取队列
final Channel createChannel = connection.createChannel();
//队列声明
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
createChannel.basicQos(1); //保证每次发送一个
//等AMQP中数据时会出发handleDelivery方法
DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
String str = new String(body, "utf-8");
System.out.println("{1} Recv query Msg = " + str);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("{1} done");
createChannel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
boolean autoAck = false; //自动应答
createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
}
- 消费者2
private static final String QUEUE_NAME = "test_worker_queue";
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取队列
final Channel createChannel = connection.createChannel();
//队列声明
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
createChannel.basicQos(1);
//等AMQP中数据时会出发handleDelivery方法
DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
String str = new String(body, "utf-8");
System.out.println("{2} Recv query Msg = " + str);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("{2} done");
createChannel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
boolean autoAck = false;
createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
}
6.消息应答与消息持久化
boolean autoAck = false;
createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
boolean autoAck = true; //自动确认模式
一旦rabbitmq将消息发送给消费者,就会从内存将消息删除
这种情况下如果杀死正在执行的消费者,就会丢失正在处理的消息
boolean autoAck = false; //手动确认模式
如果有一个消费者挂掉,就会交付给其他消费者rabbitmq支持消息应答,
消费者发送一个消息应答告诉rabbitmq这个消息我已经处理完成,你可以删了
然后rabbitmq就删除内存中的消息
消息应答默认是打开的,默认是false
message acknowkedgment
如果rabbitmq挂了,我们的消息仍然会丢失!!!
消息的持久化
Boolean durable = false;
createChannel.queueDeclare(QUEUE_NAME, durable, false, false, null);
我们将程序中的 boolean durable = flase ;改成 true是不可以的尽管代码是正确的,他也不会运行成功!应为我们已经定义了一个test_worker_queue这个queue是未持久化的,rabbitmq不允许从新定义(不同参数)一个已存在的队列
想实现需要删除队列重新定义或者创建新的队列
7.publish / subscribe 发布订阅
解读
1.一个生产着多个消费者
2.每一个消费者都有自己对应的队列
3.生产者没有直接发送到队列,而是发送到交换机,转发器 exchange
4.每一个队列都要绑定到交换机上
5.生产者发送的消息 经过交换机 到达队列 就能实现一个消息被多个消费者消费
注册 → 邮件 → 短信
- 生产者
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 = " + msg);
channel.close();
connection.close();
}
消息那去了呢???
丢失了,应为交换机没有储存能力,rabbitmq里面只有队列有存储能力.因为这个时候没有队列绑定到这个交换机所以数据丢失了
- 消费者1
private static final String EXCHANGE_NAME = "test_exchange_fanout";
private static final String QUEUE_NAME = "test_queue_fanout_email";
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取队列
final Channel createChannel = connection.createChannel();
//队列声明
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
//队列绑定交换机
createChannel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
createChannel.basicQos(1);
//等AMQP中数据时会出发handleDelivery方法
DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
String str = new String(body, "utf-8");
System.out.println("{1} Recv query Msg = " + str);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("{1} done");
createChannel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
boolean autoAck = false;
createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
}
- 消费者2
private static final String EXCHANGE_NAME = "test_exchange_fanout";
private static final String QUEUE_NAME = "test_queue_fanout_note";
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取队列
final Channel createChannel = connection.createChannel();
//队列声明
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
//队列绑定交换机
createChannel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
createChannel.basicQos(1);
//等AMQP中数据时会出发handleDelivery方法
DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
String str = new String(body, "utf-8");
System.out.println("{2} Recv query Msg = " + str);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("{2} done");
createChannel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
boolean autoAck = false;
createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
}
8.Exchange(交换机,转发器)
一方面是接受生产者的消息,另一方面是想队列推送消息
匿名转发 “”
fanout : 不处理路由键交换机收到消息就会将消息传给跟他绑定的队列
Direct : 处理路由键,发送数据是需要路由Key,队列也要绑定路由Key 如果队列KEy与他相同,将数据交给他
9.routing路由模式
- 生产者
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();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
String msg = "hello direct"; //消息
System.out.println("send msg =" + msg);
String routingKey = "info"; //路由key
channel.basicPublish(EXCHANGE_NAME, routingKey, null, msg.getBytes());
channel.close();
connection.close();
}
- 消费者1
private static final String EXCHANGE_NAME = "test_exchange_direct";
private static final String QUEUE_NAME = "test_queue_direct_error";
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取队列
final Channel createChannel = connection.createChannel();
//队列声明
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
//队列绑定交换机
createChannel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "error");
createChannel.basicQos(1);
//等AMQP中数据时会出发handleDelivery方法
DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
String str = new String(body, "utf-8");
System.out.println("{1} Recv query Msg = " + str);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("{1} done");
createChannel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
boolean autoAck = false;
createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
}
- 消费者2
private static final String EXCHANGE_NAME = "test_exchange_direct";
private static final String QUEUE_NAME = "test_queue_direct_all";
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//获取队列
final Channel createChannel = connection.createChannel();
//队列声明
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
//队列绑定交换机
createChannel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "error");
createChannel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "info");
createChannel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
createChannel.basicQos(1);
//等AMQP中数据时会出发handleDelivery方法
DefaultConsumer defaultConsumer = new DefaultConsumer(createChannel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
String str = new String(body, "utf-8");
System.out.println("{2} Recv query Msg = " + str);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("{2} done");
createChannel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
boolean autoAck = false;
createChannel.basicConsume(QUEUE_NAME, autoAck, defaultConsumer);
}
10. topics 主题
将路由键和某个模式匹配
通配符
“#” : 匹配一个或者多个
“.” : 匹配一个
商品 : 发布 删除 修改 查询
- 生产者
private static final String EXCHANGE_NAME = "test_exchange_topics";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
String msg = "添加商品2";
channel.basicPublish(EXCHANGE_NAME, "Good.2", null, msg.getBytes());
System.out.println(" send add " + msg);
channel.close();
connection.close();
}
- 消费者
private static final String EXCHANGE_NAME = "test_exchange_topics";
private static final String QUEUE_NAME = "test_queue_topics";
public static void main(String[] args) throws IOException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//队列与交换机结合
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "Good.*");
DefaultConsumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body, "utf-8");
System.out.println(msg);
}
};
//等待数据
boolean autoAck = true;//自动确认
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
- 11.RabbitMQ的消息确认机制(事物+confirm)
在RabbitMQ中我们可以通过持久化数据解决rabbitmq服务器异常的数据丢失问题
问题
1.生产者将消息发送出去以后到底有没有到达RabbitMQ服务器
默认情况下是不知道的,
两种方式
1,AMQP 实现了事务机制
2.Confirm模式
事务机制
txSelect
用户当前channel设置成为transation模式
txCommit
提交事务
txRollback
回滚事务
- 生产者
private static final String QUEUE_NAME = "test_queue_tx";
public static void main(String[] args) throws Exception {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String msg = "";
try {
channel.txSelect();
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
channel.txCommit();
} catch (Exception e) {
System.out.println("send msg rollback");
channel.txRollback();
}
channel.close();
connection.close();
}
- 消费者
private static final String QUEUE_NAME = "test_queue_tx";
public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//连接中获取队列
Channel createChannel = connection.createChannel();
//创建队列声明
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
//监听队列
createChannel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(createChannel) {
//获取到达的消息
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
String value = new String(body);
System.out.println("recv msg =" + value);
}
});
}
模式弊
降低消息的吞吐量,走的通信过多,大量的请求,降低吞吐量
Confirm模式
生产者端confirm模式的实现原理
生产者将信道设置为confirm模式,一旦进入Confirm模式,所有在该信道上发布的消息都会被指派一个唯一ID(从1开始),一旦消息被投递到所有匹配的队列以后,broker就会发送一个确认消息给生产者,(包含消息的唯一ID),这使得生产者知道消息已经正确到达目的队列,如果消息和队列是持久化的,那么确认消息会将消息写入磁盘后发送出,broker回传给生产者的确认消息中delicer-tag域包含了确认消息的序列号,此外broker也可以设置basicack的nultiple域,表示到这个之前所有消息都已经得到处理
Confirm 模式最大的好处在于它是异步的
如果自身出现了自身奔溃,消息丢失就会发送一条Nack信息
开启confirm模式串行
Channel.confirmSelect();
编程模式
普通
发送一条 waitForConfirms();
批量
发松一批消息 waitForConfirms();
异步
Confirm模式支持异步
- 普通生产者
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//生产者调用confirmSelect 将channel设置为Confirm模式
channel.confirmSelect();
String msg = "1";
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
if (!channel.waitForConfirms()) {
System.out.println("message send to defeated");
} else {
System.out.println("message send to ok");
}
- 普通消费者
private static final String QUEUE_NAME = "test_queue_confirm_common";
public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
//获取连接
Connection connection = ConnectionUtils.getConnection();
//连接中获取队列
Channel createChannel = connection.createChannel();
//创建队列声明
createChannel.queueDeclare(QUEUE_NAME, false, false, false, null);
//监听队列
createChannel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(createChannel) {
//获取到达的消息
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
System.out.println("recv msg =" + new String(body));
}
});
}
- 批量生产者
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//生产者调用confirmSelect 将channel设置为Confirm模式
channel.confirmSelect();
String msg = "1";
for (int i = 0; i < 10; i++) {
msg = msg + i;
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
}
if (!channel.waitForConfirms()) {
System.out.println("message send to defeated");
} else {
System.out.println("message send to ok");
}
批量的消费者与普通消费者一致,没有变化
confirm模式异步
Channel 对象提供的ConfirmListener()回调方法只包含deliveryTag(当前Chanel发出的消息序号),我们需要自己为每一个Channel维护一个UNconfirm的消息序号集合,每publish一条数据,集合中元素加1,没回掉一次handleAck方法,UNconfirm集合删除相应的一条(multiple=false) 或多条(multiple = true) 记录,从程序运行效率上看,这个unconfirm集合采用有序集合sotedSet存储结构
异步生产者
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.confirmSelect();
//未确认的消息标识
final SortedSet<Long> confirm = Collections.synchronizedNavigableSet(new TreeSet<Long>());
channel.addConfirmListener(new ConfirmListener() {
//handleNack
public void handleNack(long arg0, boolean arg1) throws IOException {
if (arg1) {
System.out.println("-----handleNack---multiple");
confirm.headSet(arg0 + 1).clear();
} else {
System.out.println("-----handleNack---multiple false");
confirm.remove(arg0);
}
}
//没有问题的handleAck
public void handleAck(long arg0, boolean arg1) throws IOException {
if (arg1) {
System.out.println("-----handleAck---multiple");
confirm.headSet(arg0 + 1).clear();
} else {
System.out.println("-----handleAck---multiple false");
confirm.remove(arg0);
}
}
});
String msg = "sss";
while (true) {
long nextPublishSeqNo = channel.getNextPublishSeqNo();
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
confirm.add(nextPublishSeqNo);
}
异步消费者与普通消费者一致,没有变化
初级Spring-AMQP使用
- 在pom.xml中使用
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.6.5.RELEASE</version>
</dependency>
- XML配置
` <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<rabbit:connection-factory
virtual-host="/vhost_mmr" host="127.0.0.1" port="5672"
username="admin" password="admin" id="connectionFactory" />
<rabbit:template id="amqpTemplate"
connection-factory="connectionFactory" exchange="fanoutExchange" />
<rabbit:admin connection-factory="connectionFactory" />
<rabbit:queue name="myqueue" auto-declare="true"
durable="true" />
<rabbit:fanout-exchange name="fanoutExchange"
auto-declare="true">
<rabbit:bindings>
<rabbit:binding queue="myqueue"></rabbit:binding>
</rabbit:bindings>
</rabbit:fanout-exchange>
<rabbit:listener-container
connection-factory="connectionFactory">
<rabbit:listener ref="foo" method="listen"
queue-names="myqueue" />
</rabbit:listener-container>
<bean id="foo" class="com.oldkuo.rabbitmqDome.spring.RecvOne"></bean>
</beans>
生产者
AbstractApplicationContext context = new ClassPathXmlApplicationContext("classpath:context.xml");
RabbitTemplate template = context.getBean(RabbitTemplate.class);
template.convertAndSend("Hello world");
Thread.sleep(1000);
context.destroy();
消费者
public class RecvOne {
public void listen(String foo) {
System.out.println("消费者: " + foo);
}
}
简单使用SpringXML方式的Dome