RabbitMQ——交换机
张图画得非常形象,发布者和订阅者之间,不是直接和队列关联,还有交换机这一层。发布者将消息传递给交换机,交换机将消息分发到不同的队列,消费者选择队列读取消息。
因为多了一层交换机,发布者和消费者的关系变得非常灵活,发布者和队列之间,可以是1-n的关系,队列和订阅者,也可以是1-n的关系,能适配非常多复杂的业务场景。
功能概述:交换机介于发布者和队列之间,发布者可以通过交换机,将消息分发到一个或多个队列上。
常用的交换机
Direct Exchange ,
直连交换机,通过队列ID进行精准匹配。
Fanout Exchange
扇出交换机,消息会分配给每一个与交换机关联的队列。
Topic Exchange
主题交换机,通过模糊匹配的方式,将消息分配给命中的队列。
匹配规则:
* (星号) 用来表示一个单词 (必须出现的)
# (井号) 用来表示任意数量(零个或多个)单词
当一个队列的绑定键为 "#"(井号) 的时候,这个队列将会无视消息的路由键,接收所有的消息。
当 * (星号) 和 # (井号) 这两个特殊字符都未在绑定键中出现的时候,此时主题交换机等价于直连交换机。
因此,通过主题交换机,可以实现扇形交换机和直连交换机的功能。
另外还有 Header Exchange 头交换机 ,Default Exchange 默认交换机,Dead Letter Exchange 死信交换机。
测试用代码
发布者
/** * @author Mr.css * @date 2020-11-12 19:30 */ public class Send { /** * 队列名称 */ private static final String QUEUE_NAME = "queue_name"; /** * 路由键,交换机会根据路由键,把消息推到队列, * 如果路由键与队列名称一致的情况,消息会被精准推送到同名的队列 * (根据交换机的类型,设置不同的路由键) */ private static final String ROUTING_KEY = "queue_name"; /** * 交换机名称 */ private static final String EXCHANGE = "exchanges"; public static void main(String[] args) { try { Connection connection = ConnectionUtil.getConnection(); Channel channel = connection.createChannel(); //声明队列 channel.queueDeclare(QUEUE_NAME, false, false, false, null); //声明交换机,可以修改 BuiltinExchangeType 测试不同的交换机效果 channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.DIRECT); int cnt = 10; while (cnt-- > 0) { String message = "This is simple queue:" + cnt; //BasicProperties:和http协议类似,是允许添加请求头的,MQ也有这样的设计 AMQP.BasicProperties properties = MessageProperties.BASIC.builder().build(); //通过交换机,把消息发送到推送到队列中 channel.basicPublish(EXCHANGE, ROUTING_KEY, properties, message.getBytes(Charset.defaultCharset())); System.out.println("[send]:" + message); } channel.close(); connection.close(); } catch (IOException | TimeoutException e) { e.printStackTrace(); } } }
订阅者
/** * @author Mr.css * @date 2020-11-12 19:31 */ public class Receive { private static final String QUEUE_NAME = "queue_name"; public static void main(String[] args) { try { Connection connection = ConnectionUtil.getConnection(); Channel channel = connection.createChannel(); channel.queueDeclare(QUEUE_NAME, false, false, false, null); //限制消息接收的数量 channel.basicQos(0,1,false); DefaultConsumer consumer = new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) { //查看消息主体内容 String message = new String(body, StandardCharsets.UTF_8); System.out.println("[Receive]:" + message); //查看请求头 System.out.println(properties); //查看消息的发送情况 System.out.println(envelope); try { //模拟超时的情况 Thread.sleep(500); //手动应答 channel.basicAck(envelope.getDeliveryTag(), false); } catch (InterruptedException | IOException e) { e.printStackTrace(); } } }; //自动应答设置为false channel.basicConsume(QUEUE_NAME, false, consumer); } catch (IOException | ShutdownSignalException | ConsumerCancelledException e) { e.printStackTrace(); } } }
疯狂的妞妞 :每一天,做什么都好,不要什么都不做!