RabbitMQ入门教程(九):首部交换机Headers

简介

首部交换机和扇形交换机都不需要路由键routingKey,交换机是通过Headers头部来将消息映射到队列的,有点像HTTP的Headers,Hash结构中要求携带一个键“x-match”,这个键的Value可以是any或者all,这代表消息携带的Hash是需要全部匹配(all),还是仅匹配一个键(any)就可以了。相比直连交换机,首部交换机的优势是匹配的规则不被限定为字符串(string)而是Object类型。

  • any: 只要在发布消息时携带的有一对键值对headers满足队列定义的多个参数arguments的其中一个就能匹配上,注意这里是键值对的完全匹配,只匹配到键了,值却不一样是不行的;

  • all:在发布消息时携带的所有Entry必须和绑定在队列上的所有Entry完全匹配

RabbitMQ入门教程(九):首部交换机Headers_第1张图片

生产者

@Test
	public void produce() throws IOException, TimeoutException, InterruptedException {
		//创建连接工厂
		ConnectionFactory factory = new ConnectionFactory();
		//设置RabbitMQ相关信息
		factory.setHost("localhost");
		factory.setPort(AMQP.PROTOCOL.PORT);
		factory.setUsername("guest");
		factory.setPassword("guest");

		//创建一个新的连接
		Connection conn = factory.newConnection();
		//创建一个通道
		final Channel channel = conn.createChannel();
		
		// 首部消息
		Map headersMap = new HashMap();
		headersMap.put("api", "login");
		headersMap.put("version", 1);
		headersMap.put("radom", UUID.randomUUID().toString());

		// 生成发送首部消息的属性
		AMQP.BasicProperties.Builder properties = new AMQP.BasicProperties().builder().headers(headersMap);

		// 消息
		String message = "Hello RabbitMQ";
		String EXCHANGE_NAME = "exchange.headers";
		// 发布消息
		channel.basicPublish(EXCHANGE_NAME, "", false, properties.build(), message.getBytes());
		System.out.println(" [Produce] Sent '" + headersMap + "':'" + message + "'");
		
		Thread.sleep(100000);

		// 关闭连接
		channel.close();
		conn.close();
	}

 

消费者1

@Test
	public void consume1() throws IOException, TimeoutException, InterruptedException {
		ConnectionFactory factory = new ConnectionFactory();
		factory.setHost("localhost");
		factory.setPort(AMQP.PROTOCOL.PORT);
		factory.setUsername("guest");
		factory.setPassword("guest");

		Connection conn = factory.newConnection();
		Channel channel = conn.createChannel();

		// 声明一个headers交换机
		String EXCHANGE_NAME = "exchange.headers";
		channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.HEADERS);

		final Map arguments = new HashMap();
		// "x-match", "any" 代表:仅匹配一个键(any)就可以收到消息
		arguments.put("x-match", "any");
		arguments.put("api", "JDK1.9");
		arguments.put("version", 1);
		arguments.put("dataType", "json");
		
		// 声明一个临时队列
		String queueName = channel.queueDeclare().getQueue();
		// 将队列绑定到指定交换机上
		channel.queueBind(queueName, EXCHANGE_NAME, "", arguments);
		
		System.out.println("Consumer1 waiting receive message...");
		System.out.println(" [HeaderRecv ["+ arguments +"]] Waiting for messages.");
		
		// 接收消息
		channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String message = new String(body, "UTF-8");
				System.out.println("[C] received :" + message);
				System.out.println(" [HeaderRecv ["+ arguments +"] ] Received '" + properties.getHeaders() + "':'" + message + "'");
			}
		});

		Thread.sleep(100000);
	}

消费者2

@Test
	public void consume2() throws IOException, TimeoutException, InterruptedException {
		ConnectionFactory factory = new ConnectionFactory();
		factory.setHost("localhost");
		factory.setPort(AMQP.PROTOCOL.PORT);
		factory.setUsername("guest");
		factory.setPassword("guest");

		Connection conn = factory.newConnection();
		Channel channel = conn.createChannel();

		// 声明一个headers交换机
		String EXCHANGE_NAME = "exchange.headers";
		channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.HEADERS);

		final Map arguments = new HashMap();
		// "x-match", "any" 代表:仅匹配一个键(any)就可以收到消息
		arguments.put("x-match", "all");
		arguments.put("api", "JDK1.9");
		arguments.put("version", 4.0);
		arguments.put("dataType", "XML");
		
		// 声明一个临时队列
		String queueName = channel.queueDeclare().getQueue();
		// 将队列绑定到指定交换机上
		channel.queueBind(queueName, EXCHANGE_NAME, "", arguments);
		
		System.out.println("Consumer2 waiting receive message...");
		System.out.println(" [HeaderRecv ["+ arguments +"]] Waiting for messages.");
		
		// 接收消息
		channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
			@Override
			public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
					throws IOException {
				String message = new String(body, "UTF-8");
				System.out.println("[C2] received :" + message);
				System.out.println(" [HeaderRecv ["+ arguments +"] ] Received '" + properties.getHeaders() + "':'" + message + "'");
			}
		});

		Thread.sleep(100000);
	}

运行结果

先运行消费者,再运行生产者。

// all:匹配失败,缺少{"dataType", "json"}
Map heardersMap = new HashMap();
heardersMap.put("api", "login");
heardersMap.put("version", 1.0);

// all:匹配成功,生产者多发送一个head没关系
Map heardersMap = new HashMap();
heardersMap.put("api", "login");
heardersMap.put("version", 1.0);
heardersMap.put("dataType", "json");
heardersMap.put("ext", false);

Map arguments = new HashMap();
arguments.put("x-match", "all");
arguments.put("api", "login");
arguments.put("version", 1.0);
arguments.put("dataType", "json");

//------------------------------------------
// any: 匹配成功,只要有一个键值对能满足队列的arguments即可
Map heardersMap = new HashMap();
heardersMap.put("api", "login");

Map arguments = new HashMap();
arguments.put("x-match", "any");
arguments.put("api", "login");
arguments.put("version", 1.0);
arguments.put("dataType", "json");

// any: 匹配失败,键值对中的key和value必须全部匹配上
Map heardersMap = new HashMap();
heardersMap.put("api", "regist");

Map arguments = new HashMap();
arguments.put("x-match", "any");
arguments.put("api", "login");
arguments.put("version", 1.0);
arguments.put("dataType", "json");

直连接和首部类型的比较
绑定规则不同:直连接是一个简单的String;而首部是键值对Entry,而且键值对的value可以是任意类型Object

绑定个数不同:直连接一次只能绑定一个字符串,如果想绑定多个字符串就需要绑定多次或者循环调用queueBind()方法来绑定多次;而首部类型直接可以往Map中添加多个实体Entry即可

映射规则不同:直连接只需要比较路由键是否相等即可,而首部类型除了比较value还要比较key,因为首部类型是Entry类型,需要同时比较key和value,而且首部类型还可以通过x-match来控制匹配的条件,all:需要匹配所有Entry,相当于SQL中的 and操作,any:只需要匹配上一个Entry即可,相当于SQL中的or操作

直连接适用于计较简单的路由,而首部类型相比直连接匹配规则更强大

 

你可能感兴趣的:(RabbitMQ学习)