import com.rabbitmq.client.*; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.concurrent.ConcurrentNavigableMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.TimeoutException; /** * 现在:exchange交换机的topic模式 * topic模式可以使用累心可以使用类似通配符一样的东西 * 匹配规则: * 1.#代表零个或者多个字符 * 2.*代表一个字符 * * 场景 * 红色,黄色,蓝色颜色的消息,分别发送到不同的队列,颜色只是其中的一个属性 * 然后路由分别是:#.red.# 中间是红色的发送到red.queue队列 * #.yellow.# 中间是黄色的发送到yellow.queue队列 * *.blud.* 中间是蓝色的也发送到yellow.queue队列 * * 在topic模式的基础上增加发布人 confirm模式 * 三种方式: * 1.单条消息确认,同步等待消息确认 简单,速度慢 * 2.批量确认消息,同步等待消息确认 简单/速度适中,但是很难判断是那条消息出问题 * 3.异步处理,最佳的性能和资源利用,在错误的情况下能发现 * * 消息确认:channel.addConfirmListener(param1,param2)监听, param1.消息成功处理 param2:消息到达exchange失败处理(可异常记录日志,或者尝试重新发送等) * channel.addReturnListener()监听,消息到达exchange,但是没有匹配到队列,返回消息 */ public class ExchangeTest { private static final String EXCHANGE_NAME = "topic.exchange"; private static final String RED_QUEUE = "red.queue"; private static final String RED_ROUTING = "#.red.#"; public static void main(String[] args) { new Thread( () -> { /** * 发送消息 */ ConnectionFactory factory = new ConnectionFactory(); factory.setHost("127.0.0.1"); factory.setUsername("rabbit"); factory.setPassword("rabbit"); //创建连接和通道 try (Connection conn = factory.newConnection(); Channel channel = conn.createChannel()) { //------第一种简单设置,同步等待确认 begin-------- //声明交换机 topic模式 /*channel.exchangeDeclare(EXCHANGE_NAME, "topic"); //开启confirm模式 channel.confirmSelect(); for (int i = 0; i < 50; i++) { //发送到red.queue队列 String message = "red rabbitmq "; //发送到红色队列,匹配上面的路径#.red.# channel.basicPublish(EXCHANGE_NAME, "red.less", null, message.getBytes(StandardCharsets.UTF_8)); //等待确认设置5秒超时时间 如果超时则抛出异常/重新发送消息 //channel.waitForConfirms(5000); }*/ //------第一种简单设置,同步等待确认 end-------- //------第二种批量设置,同步等待确认 begin-------- //开启confirm模式 /*channel.confirmSelect(); int batchSize = 0; for (int i = 0; i < 50; i++) { batchSize++; //发送到red.queue队列 String message = "red rabbitmq "; //发送到红色队列,匹配上面的路径#.red.# channel.basicPublish(EXCHANGE_NAME, "red.less", null, message.getBytes(StandardCharsets.UTF_8)); //等待确认设置5秒超时时间 如果超时则抛出异常/重新发送消息 if (batchSize == 10) { channel.waitForConfirms(5000); batchSize = 0; } } if (batchSize >0) { channel.waitForConfirms(5000); }*/ //------第二种批量设置,同步等待确认 end-------- //------第三种异步设置,异步等待确认 begin-------- channel.confirmSelect(); int batchSize = 0; for (int i = 0; i < 50; i++) { batchSize++; //发送到red.queue队列 String message = "red rabbitmq "; ConcurrentNavigableMap<Long, String> map = new ConcurrentSkipListMap<Long, String>(); map.put(channel.getNextPublishSeqNo(), message); channel.getNextPublishSeqNo(); ConfirmCallback commitBack = new ConfirmCallback() { @Override//l id, b true单条消息确认,false批量确认 public void handle(long l, boolean b) throws IOException { if (true) { //获取批量的数据, ConcurrentNavigableMap currentMap = map.headMap(l, true); currentMap.clear(); System.out.println("批量,之前消息全部确认"); } else { map.remove(l); System.out.println("单条消息确认"); } } }; channel.addConfirmListener(commitBack, (seq, multi)->{ //未发送消息到交换机exchange,可根据情况是否再次发送 System.err.println("消息发送失败seq"+seq+";mes:"+message); commitBack.handle(seq, multi); }); channel.addReturnListener(new ReturnListener() { @Override//达到交换机,但是没有路由匹配 public void handleReturn(int i, String s, String s1, String s2, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException { //根据情况是否重新发送 System.err.println("消息发送失败code"+i+";mes:"+new String(bytes, StandardCharsets.UTF_8)); } }); //发送到红色队列,匹配上面的路径#.red.# channel.basicPublish(EXCHANGE_NAME, "red.less", null, message.getBytes(StandardCharsets.UTF_8)); } //------第三种异步设置,异步等待确认 end-------- } catch (Exception e) { e.printStackTrace(); } } ).start(); //消费者1 new Thread( () -> { //接收消息 ConnectionFactory comsumerFacotry = new ConnectionFactory(); comsumerFacotry.setHost("127.0.0.1"); comsumerFacotry.setUsername("rabbit"); comsumerFacotry.setPassword("rabbit"); Connection comsumerConn = null; try { //这里不手动关闭连接和通道,因为需要监听器一直监听是否有消息,所以需要一直保持连接 comsumerConn = comsumerFacotry.newConnection(); Channel channel = comsumerConn.createChannel(); //声明交换机 topic模式 channel.exchangeDeclare(EXCHANGE_NAME, "topic"); //声明队列RED_QUEUE channel.queueDeclare(RED_QUEUE, false, false, false, null); //绑定交换机和队列 channel.queueBind(RED_QUEUE, EXCHANGE_NAME, RED_ROUTING); //消息监听器 boolean autoAck = true;//是否自动ack 接收到消息则认为成功 channel.basicConsume(RED_QUEUE, autoAck, (consumerTag, message) -> { System.out.println("消费者 红色收到消息:" + new String(message.getBody(), StandardCharsets.UTF_8)); }, consumerTag -> { }); } catch (IOException e) { e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } } ).start(); } }