在建立的生产者消费者案例中。
消费端
public static void main(String[] args) throws Exception{
Channel Channel = getConn.getchannel();
/**
*
* meeage表示接收的消息
*/
DeliverCallback DeliverCallback=(consumer,meeage)->{
System.out.println(new String(meeage.getBody())+"已拿到");
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* meeage.getEnvelope().getDeliveryTag() 消息标记Tag
* false表示不批量应答,只应答这一个消息
*/
System.out.println(new String(meeage.getBody())+"确认完成");
Channel.basicAck(meeage.getEnvelope().getDeliveryTag(),false);
};
CancelCallback CancelCallback=consumer->{
System.out.println("消息消费被中断");
};
/**
* 一:消息队列名称
* 二:是否自动应答
* true
* 就是当生产者发送消息之后,RabbitMq就会将队列中的消息删除,不会管消费者是否已经处理完了消息
* 弊端就是当生产者发送数据之后,如果在消费者没有处理完消息就宕机了,那这个消息就丢失了
*
* false
* 就是当生产者发送消息之后,RabbitMq不会立刻将队列中的消息删除,而是等消费者发送ACk(表示确认已经处理完毕),才
* 会将队列中的消息删除,假若在没有发送ACK时,消费者宕机了,则这条消息会重新入队,被其他消费者获取。保证了消息不会丢失
*
* 三:没有消费成功的反馈
* 四:拿取消费的回调
*/
boolean AutoACk=false;
System.out.println( "C3等待消息");
Channel.basicConsume(DL,AutoACk,DeliverCallback,CancelCallback);
}
在 Channel.basicConsume设置中,我们将消息应答改为false(手动应答)只有发送了ACk之后,
RabbitMQ才会删除消息,否则会重新入队
这里我们消费者发送了2条消息
"C:\Program Files\Java\jdk-14.0.1\bin\java.exe" "-javaagent:D:\IntelliJ IDEA 2021.3.3\lib\idea_rt.jar=56306:D:\IntelliJ IDEA 2021.3.3\bin" -Dfile.encoding=UTF-8 -classpath E:\IDEA_Project\com.hwx.java\target\classes;C:\Users\lxkj\.m2\repository\com\rabbitmq\amqp-client\5.9.0\amqp-client-5.9.0.jar;C:\Users\lxkj\.m2\repository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;C:\Users\lxkj\.m2\repository\commons-io\commons-io\2.8.0\commons-io-2.8.0.jar;C:\Users\lxkj\.m2\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;C:\Users\lxkj\.m2\repository\com\guicedee\services\sl4j\1.0.13.5\sl4j-1.0.13.5.jar;C:\Users\lxkj\.m2\repository\com\guicedee\services\log4j-core\1.0.13.5\log4j-core-1.0.13.5.jar RabbitMQ.provider
发送消息
hello hwx
hello已发送
发送消息
hwx已发送
消费者一
"C:\Program Files\Java\jdk-14.0.1\bin\java.exe" "-javaagent:D:\IntelliJ IDEA 2021.3.3\lib\idea_rt.jar=56312:D:\IntelliJ IDEA 2021.3.3\bin" -Dfile.encoding=UTF-8 -classpath E:\IDEA_Project\com.hwx.java\target\classes;C:\Users\lxkj\.m2\repository\com\rabbitmq\amqp-client\5.9.0\amqp-client-5.9.0.jar;C:\Users\lxkj\.m2\repository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;C:\Users\lxkj\.m2\repository\commons-io\commons-io\2.8.0\commons-io-2.8.0.jar;C:\Users\lxkj\.m2\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;C:\Users\lxkj\.m2\repository\com\guicedee\services\sl4j\1.0.13.5\sl4j-1.0.13.5.jar;C:\Users\lxkj\.m2\repository\com\guicedee\services\log4j-core\1.0.13.5\log4j-core-1.0.13.5.jar RabbitMQ.consumer
C1等待消息
hello已拿到
进程已结束,退出代码130
消费者二
"C:\Program Files\Java\jdk-14.0.1\bin\java.exe" "-javaagent:D:\IntelliJ IDEA 2021.3.3\lib\idea_rt.jar=56318:D:\IntelliJ IDEA 2021.3.3\bin" -Dfile.encoding=UTF-8 -classpath E:\IDEA_Project\com.hwx.java\target\classes;C:\Users\lxkj\.m2\repository\com\rabbitmq\amqp-client\5.9.0\amqp-client-5.9.0.jar;C:\Users\lxkj\.m2\repository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;C:\Users\lxkj\.m2\repository\commons-io\commons-io\2.8.0\commons-io-2.8.0.jar;C:\Users\lxkj\.m2\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;C:\Users\lxkj\.m2\repository\com\guicedee\services\sl4j\1.0.13.5\sl4j-1.0.13.5.jar;C:\Users\lxkj\.m2\repository\com\guicedee\services\log4j-core\1.0.13.5\log4j-core-1.0.13.5.jar RabbitMQ.consumer
C2等待消息
hwx已拿到
hwx确认完成
hello已拿到
hello确认完成
进程已结束,退出代码130
消费者C1拿到hello之后,在还没有处理完成,没有发送ACK时我们关闭程序,此时我们发现hello被
消费者C2拿到了,这说明在消费者没有发送ACK之前,RabbitMQ不会删除队列中的这个消息,没有处理完毕重新入队,被C2拿到。
在生产者发送消息之后,默认情况下,消费者采用轮询分发,每一个消费端轮流处理每一个消息,
但是在这种情况下,有些消费者可能处理速度很慢,有些很快,这样造成快的消费端浪费的资源,这里我们可以设置分发方式。
设置分发方式在消费端的信道设置
// true表示自动应答,false表示手动应答
boolean AutoACk=false;
// 1表示不公平分发(能者多劳) ,0表示轮询分发
int pp=1;
Channel.basicQos(pp);
System.out.println( "C3等待消息");
Channel.basicConsume(DL,AutoACk,DeliverCallback,CancelCallback);
其中的 Channel.basicQos()方法就是设置分发方式,默认是0。
刚刚说到,假若在信息设置为持久化发送之后,但是在RabbitMQ还没有持久化的间隙时间内,RabbitMQ发送宕机,那么信息就丢失了,所以需要发布确认,即当RabbitMQ确实已经持久化之后,向生产者发布一个信息确认。
开启方式在创建信道后,使用
Channel.confirmSelect();
进行发布确认。
消费者1
package RabbitMQ.three;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import comp.getConn;
public class provider {
public static final String EX="logs";
public static void main(String[] args) throws Exception{
Channel getchannel = getConn.getchannel();
// 声明交换机
getchannel.exchangeDeclare(EX,"fanout");
// 获取临时队列 ,消费者断开连接就删除了
String queue = getchannel.queueDeclare().getQueue();
// 第一个队列,第二个交换机,第三个队列与交换机绑定的key
getchannel.queueBind(queue,EX,"");
DeliverCallback DeliverCallback=(one,two)->{
System.out.println(new String(two.getBody(),"UTF-8"));
};
CancelCallback CancelCallback=consumer->{
};
// 队列名,是否开启自动应答,
getchannel.basicConsume(queue,true,DeliverCallback,CancelCallback);
}
}
消费者2
package RabbitMQ.three;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import comp.getConn;
public class provider2 {
public static final String EX="logs";
public static void main(String[] args) throws Exception{
Channel getchannel = getConn.getchannel();
// 声明交换机 fanout (消息/订阅模式)
getchannel.exchangeDeclare(EX,"fanout");
// 获取临时队列 ,消费者断开连接就删除了
String queue = getchannel.queueDeclare().getQueue();
// 第一个队列,第二个交换机,第三个队列与交换机绑定的key
getchannel.queueBind(queue,EX,"");
DeliverCallback DeliverCallback=(one,two)->{
System.out.println(new String(two.getBody(),"UTF-8"));
};
CancelCallback CancelCallback=consumer->{
};
// 队列名,是否开启自动应答,
getchannel.basicConsume(queue,true,DeliverCallback,CancelCallback);
}
}
生产者
package RabbitMQ.three;
import com.rabbitmq.client.Channel;
import comp.getConn;
import java.util.Scanner;
public class consumer {
public static final String JHJ="logs";
public static void main(String[] args) throws Exception{
Channel getchannel = getConn.getchannel();
Scanner in=new Scanner(System.in);
do{
System.out.println("输入消息:");
String tx=in.next();
getchannel.basicPublish(JHJ,"",null,tx.getBytes("UTF-8"));
}while (in.hasNext());
}
}
这里获取一个临时队列,绑定的key为字符空,这里的两个消费者代码是完全一样的,
生产者向队列绑定交换机的key发送消息,即"",因为消费者一和二绑定的都是"",所以两个人都可以收到。即实现了消息/订阅,即广播。
直接模式中,即可以针对性的发送给某些消费端
消费者一
// DIRECT为直连模式
getchannel.exchangeDeclare(JHJ, BuiltinExchangeType.DIRECT);
// 设置队列
getchannel.queueDeclare("ap_one",false,false,false,null);
// 绑定交换机,key值为info
getchannel.queueBind("ap_one",JHJ,"info");
getchannel.queueBind("ap_one",JHJ,"warn");
消费者二
// DIRECT为直连模式
getchannel.exchangeDeclare(JHJ, BuiltinExchangeType.DIRECT);
// 设置队列
getchannel.queueDeclare("ap_two",false,false,false,null);
// 绑定交换机,key值为error
getchannel.queueBind("ap_two",JHJ,"error");
生产者发送消息
getchannel.basicPublish(JHJ,"info",null,iix.getBytes(StandardCharsets.UTF_8));
这里的第一项是交换机名称,第二项是key,这里是向key为info发送消息,即消费者一才能收到,
当然,消费者一绑定了两个key,所以向warn发送也是消费者一才能收到,向eroor发送就只有
消费者二才能收到。
主题模式主要就是设置key值改变了
key值的设置必须满足 单词.单词 ,这里的单词可以是小于255的任意字符,只有之间用 . 连接即可
消费者一
getchannel.queueDeclare("one",false,false,false,null);
getchannel.queueBind("one",HHH,"*.vip.*");
消费者二
getchannel.queueDeclare("one",false,false,false,null);
getchannel.queueBind("one",HHH,"*.vip.vip");
消费者三
getchannel.queueDeclare("two",false,false,false,null);
getchannel.queueBind("two",HHH,"user.vip.*");
这里分别向
1. user.vip.ee 发送消息
2. er.vip.vip 发送消息
3. erer.common.pp 发送消息
其中,1被消费者三的two队列和one队列接收
2同时满足消费者一和消费者二,但是由于两个消费者是同一个队列,
所以只能有一个获取消息
3.不满足任何一个队列