RabbitMQ系列之三 RabbitMQ几种典型模式

RabbitMQ教程:全面解析六种核心模式
本文详细介绍了RabbitMQ的六种模式:简单模式、工作模式、发布订阅模式、路由模式、通配符模式和远程调用模式。每个模式都包含生产者和消费者的实现代码,旨在帮助读者理解RabbitMQ在不同场景下的应用。

本文详细介绍简单模式Simple、工作模式Work、发布订阅模式Publish/Subscribe、路由模式Routing、通配符模式Topics、远程调用模式RPC(暂不对该队列模式进行详解)

模式1:简单模式(Simple / HelloWorld 单生产单消费)
简单的发送与接收,没有特别的处理。

RabbitMQ连接(公共的连接方法,其他模式共用此方法)

package com.sc.queuemode.connection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class ConnectionUtil {
public static Connection getConnection() throws IOException, TimeoutException {
//定义连接池
ConnectionFactory connectionFactory = new ConnectionFactory();
//连接地址
connectionFactory.setHost(“localhost”);
//连接端口
connectionFactory.setPort(5672);
//用户名
connectionFactory.setUsername(“guest”);
//密码
connectionFactory.setPassword(“guest”);
//通过连接工厂获取连接
Connection connection = connectionFactory.newConnection();
//返回连接
return connection;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Producer:

package com.sc.queuemode.simple;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.sc.queuemode.connection.ConnectionUtil;

public class Producter {
private final static String QUEUE_NAME = “hello”;
public static void main(String[] args) throws IOException, TimeoutException {
//获取连接
Connection connection = ConnectionUtil.getConnection();
//从连接中声明通道
Channel channel = connection.createChannel();
//队列申明
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//消息内容
String message = “simple queue hello world !”;
//推送发布消息
//basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
//信道关闭
channel.close();
//连接关闭
connection.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Consumer:

package com.sc.queuemode.simple;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.sc.queuemode.connection.ConnectionUtil;

public class Consumer1 {

private final static String QUEUE_NAME = "hello_queue";

public static void main(String[] args) throws IOException, TimeoutException{
	//获取连接
	Connection connection = ConnectionUtil.getConnection();
	//声明信道
	Channel channel = connection.createChannel();
	//声明队列
	channel.queueDeclare(QUEUE_NAME, false, false, false, null);
	//声明消费者
	Consumer consumer = 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("customer 消费消息:"+message);
		}
	};
	channel.basicConsume(QUEUE_NAME, true,consumer);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
模式2:工作模式(Work单发送多接收)
一个生产者端,多个消费者端。示例中为了保证消息发送的可靠性,不丢失消息,使消息持久化了。同时为了防止接收端在处理消息时down掉,只有在消息处理完成后才发送消息确认。

Producer:

package com.sc.queuemode.work;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.sc.queuemode.connection.ConnectionUtil;

public class Producter {

private final static String QUEUE_NAME = "work_queue";

public static void main(String[] args) throws IOException, TimeoutException {
	//获取连接
	Connection connection = ConnectionUtil.getConnection();
	//声明信道
	Channel channel = connection.createChannel();
	//队列申明,durable:true消息持久化
	channel.queueDeclare(QUEUE_NAME, true, false, false, null);
	//消息内容
	String message = getMessage(args);
	for(int i = 0; i < 20; i++) {
		//发布消息
		//basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
		channel.basicPublish("", QUEUE_NAME, null, (i + " " +message).getBytes());
	}
	//关闭通道
	channel.close();
	//连接关闭
	connection.close();
}

private static String getMessage(String[] strings) {
	if(strings.length < 1) {
		return "Hello World!";
	}
	return joinStrings(strings, "");
}

private static String joinStrings(String[] strings, String delimiter) {
	int length = strings.length;
	if(length == 0) return "";
	StringBuilder words = new StringBuilder(strings[0]);
	for(int i = 0; i < length; i++) {
		words.append(delimiter).append(strings[i]);
	}
	return words.toString();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
Consumer:
消费者1

package com.sc.queuemode.work;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.sc.queuemode.connection.ConnectionUtil;
/*

  • 消费者1
    */
    public class Consumer1 {

    private final static String QUEUE_NAME = “work_queue”;
    public static void main(String[] args) throws IOException, TimeoutException{
    Connection connection = ConnectionUtil.getConnection();
    Channel channel = connection.createChannel();
    channel.queueDeclare(QUEUE_NAME, true, false, false, null);

      //同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
      //保证在接收端一个消息没有处理完时不会接收另一个消息,即消费者端发送了ack后才会接收下一个消息。
      //在这种情况下生产者端会尝试把消息发送给下一个空闲的消费者。
      channel.basicQos(1);
      
      //声明队列的消费者O
      Consumer consumer1 = new DefaultConsumer(channel){
      	@Override
      	public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
      			throws IOException {
      		//channel.basicConsume(QUEUE_NAME, false, consumer1);
      		String message = new String(body, "UTF-8");
      		System.out.println("customer1 消费消息:"+message);
      		//手动返回结果
      		channel.basicAck(envelope.getDeliveryTag(), false);
      		try {
      			Thread.sleep(50);
      		} catch (InterruptedException e) {
      			e.printStackTrace();
      		}
      	}
      };
      //定义的消费者监听队列 autoAck:true自动返回结果,false手动返回
      channel.basicConsume(QUEUE_NAME, false,consumer1);
    

    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    消费者2

package com.sc.queuemode.work;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.sc.queuemode.connection.ConnectionUtil;
/*
* 消费者2
*/
public class Consumer2 {
private final static String QUEUE_NAME = “work_queue”;

public static void main(String[] args) throws IOException, TimeoutException{
	Connection connection = ConnectionUtil.getConnection();
	Channel channel = connection.createChannel();
	channel.queueDeclare(QUEUE_NAME, true, false, false, null);
	
	//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
	channel.basicQos(1);
	
	//声明队列的消费者
	Consumer consumer2 = new DefaultConsumer(channel){
		@Override
		public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
				throws IOException {
			//channel.basicConsume(QUEUE_NAME, false, consumer1);
			String message = new String(body, "UTF-8");
			System.out.println("customer2 消费消息:"+message);
			channel.basicAck(envelope.getDeliveryTag(), false);
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	};
	//定义的消费者监听队列(第二个参数:true自动返回结果,false手动返回)
	channel.basicConsume(QUEUE_NAME, false,consumer2);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
模式3:发布、订阅模式(Publish/Subscribe)
使用场景:发布、订阅模式,生产者端发送消息,多个消费者同时接收所有的消息。

Producer:

package com.sc.queuemode.publish_subscrible;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.sc.queuemode.connection.ConnectionUtil;

public class Producter {

private final static String EXCHANGE_NAME = "publishSubscrible_exchange";

public static void main(String[] args) throws IOException, TimeoutException {
	Connection connection = ConnectionUtil.getConnection();
	Channel channel = connection.createChannel();
	//声明交换机Exchange类型为fanout
	channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
	
	String message = "publish/subscrible hello world";
	channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
	System.out.println("发布订阅 生产者 发布消息:"+message);
	channel.close();
	connection.close();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
生产者端发布消息到交换机,使用“fanout”方式发送,即广播消息,不需要使用queue,发送端不需要关心谁接收。
消费者1:

package com.sc.queuemode.publish_subscrible;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.ConsumerCancelledException;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.ShutdownSignalException;
import com.sc.queuemode.connection.ConnectionUtil;

public class Customer1 {

//队列1
private final static String QUEUE_NAME = "publishSubscrible_queue";
private final static String EXCHANGE_NAME = "publishSubscrible_exchange";

public static void main(String[] args) throws IOException, TimeoutException{
	Connection connection = ConnectionUtil.getConnection();
	Channel channel = connection.createChannel();
	//声明交换机
	channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
	//声明队列
	channel.queueDeclare(QUEUE_NAME, true, false, false, null);
	//绑定队列到交换机
	channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
	
	//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
	//保证在接收端一个消息没有处理完时不会接收另一个消息,即消费者端发送了ack后才会接收下一个消息。
	//在这种情况下生产者端会尝试把消息发送给下一个空闲的消费者。
	channel.basicQos(1);
	
	//申明消费者
	Consumer consumer1 = 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("发布订阅 消费者1 消费消息:"+message);
			//手动返回结果
			channel.basicAck(envelope.getDeliveryTag(), false);
		}
	};
	channel.basicConsume(QUEUE_NAME, false, consumer1);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
消费者2:

package com.sc.queuemode.publish_subscrible;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.sc.queuemode.connection.ConnectionUtil;

public class Customer2 {

//队列2
private final static String QUEUE_NAME = "publishSubscrible_queue2";
private final static String EXCHANGE_NAME = "publishSubscrible_exchange";

public static void main(String[] args) throws IOException, TimeoutException{
	Connection connection = ConnectionUtil.getConnection();
	Channel channel = connection.createChannel();
	//声明交换机
	channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
	//声明队列
	channel.queueDeclare(QUEUE_NAME, true, false, false, null);
	//绑定队列到交换机
	channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
	
	//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
	//保证在接收端一个消息没有处理完时不会接收另一个消息,即消费者端发送了ack后才会接收下一个消息。
	//在这种情况下生产者端会尝试把消息发送给下一个空闲的消费者。
	channel.basicQos(1);
	
	//申明消费者
	Consumer consumer2 = 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("发布订阅 消费者2 消费消息:"+message);
			//手动返回结果
			channel.basicAck(envelope.getDeliveryTag(), false);
		}
	};
	channel.basicConsume(QUEUE_NAME, false, consumer2);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
消费者端:
1、声明和生产者端一样的交换机。
2、注意binding queue的时候,channel.queueBind()的第三个参数Routing key为空,即所有的消息都接收。如果这个值不为空,在exchange type为“fanout”方式下该值被忽略!
3、消息队列和交换机绑定如下图所示

模式4:路由模式(Routing)
生产者按routing key发送消息,不同的消费者端按不同的routing key接收消息。

Producer:

package com.sc.queuemode.routing;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.sc.queuemode.connection.ConnectionUtil;

public class Producter {

private final static String EXCHANGE_NAME = "routing_exchange";

public static void main(String[] args) throws IOException, TimeoutException {
	Connection connection = ConnectionUtil.getConnection();
	Channel channel = connection.createChannel();
	//声明交换机Exchange类型为direct
	channel.exchangeDeclare(EXCHANGE_NAME, "direct");
	
	//发布消息3种routingKey的消息
	String message = "hello info";
	channel.basicPublish(EXCHANGE_NAME, "info", null, message.getBytes());
	System.out.println("路由模式发布info消息:"+message);
	
	message = "hello warning";
	channel.basicPublish(EXCHANGE_NAME, "warning", null, message.getBytes());
	System.out.println("路由模式发布warning消息:"+message);
	
	message = "hello error";
	channel.basicPublish(EXCHANGE_NAME, "error", null, message.getBytes());
	System.out.println("路由模式发布error消息:"+message);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
消费者端和模式3(发布订阅模式)的区别:
1、exchange的type为direct
2、发送消息的时候加入了routing key
Consumer:
消费者1

package com.sc.queuemode.routing;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.sc.queuemode.connection.ConnectionUtil;

public class Customer1 {

private final static String QUEUE_NAME = "routing_queue1";
private final static String EXCHANGE_NAME = "routing_exchange";

public static void main(String[] args) throws IOException, TimeoutException{
	Connection connection = ConnectionUtil.getConnection();
	Channel channel = connection.createChannel();
	//声明交换机
	channel.exchangeDeclare(EXCHANGE_NAME, "direct");
	//申明队列
	channel.queueDeclare(QUEUE_NAME, true, false, false, null);
	
	//队列绑定交换机,指定路由routingKey
	//结束路由routingKey为info和warning的消息
	channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "info");
	channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "warning");
	
	//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
	//保证在接收端一个消息没有处理完时不会接收另一个消息,即消费者端发送了ack后才会接收下一个消息。
	//在这种情况下生产者端会尝试把消息发送给下一个空闲的消费者。
	channel.basicQos(1);
	
	//声明消费者
	Consumer consumer = 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("路由模式 消费者1 消费消息:"+message);
			channel.basicAck(envelope.getDeliveryTag(), false);
		}
	};
	channel.basicConsume(QUEUE_NAME, false, consumer);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
消费者2

package com.sc.queuemode.routing;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.sc.queuemode.connection.ConnectionUtil;

public class Customer2 {

private final static String QUEUE_NAME = "routing_queue2";
private final static String EXCHANGE_NAME = "routing_exchange";

public static void main(String[] args) throws IOException, TimeoutException{
	Connection connection = ConnectionUtil.getConnection();
	Channel channel = connection.createChannel();
	//声明交换机
	channel.exchangeDeclare(EXCHANGE_NAME, "direct");
	//申明队列
	channel.queueDeclare(QUEUE_NAME, true, false, false, null);
	
	//队列绑定交换机,指定路由routingKey
	//结束路由routingKey为error的消息
	channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "error");
	
	//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
	//保证在接收端一个消息没有处理完时不会接收另一个消息,即消费者端发送了ack后才会接收下一个消息。
	//在这种情况下生产者端会尝试把消息发送给下一个空闲的消费者。
	channel.basicQos(1);
	
	//声明消费者
	Consumer consumer2 = 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("路由模式 消费者2 消费消息:"+message);
			channel.basicAck(envelope.getDeliveryTag(), false);
		}
	};
	channel.basicConsume(QUEUE_NAME, false, consumer2);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
消费者端和模式3(发布订阅模式)的区别:
在绑定queue和exchange的时候使用了路由routing key,即从该exchange上只接收routing key指定的消息。
队列绑定如下图所示

模式5:通配符(或主题)模式(Topics ,按topic发送接收)
生产者端不只按固定的routing key发送消息,而是按字符串“匹配”发送,消费者端同样如此。
与之前的路由模式相比,它将信息的传输类型的key更加细化,以“key1.key2.keyN…”的模式来指定信息传输的key的大类型和大类型下面的小类型,让消费者端可以更加精细的确认自己想要获取的信息类型。而在消费者端,不用精确的指定具体到哪一个大类型下的小类型的key,而是可以使用类似正则表达式(但与正则表达式规则完全不同)的通配符在指定一定范围或符合某一个字符串匹配规则的key,来获取想要的信息。“通配符交换机”(Topic Exchange)将路由键和某模式进行匹配。此时队列需要绑定在一个模式上。符号“#”匹配一个或多个词,符号“*”仅匹配一个词。

Producer:

package com.sc.queuemode.topics;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.sc.queuemode.connection.ConnectionUtil;

public class Producter {

private static final String EXCHANGE_NAME = "topic_exchange";

public static void main(String[] args) throws IOException, TimeoutException {
	Connection connection = ConnectionUtil.getConnection();
	Channel channel =connection.createChannel();
	//声明交换机类型为topic
	channel.exchangeDeclare(EXCHANGE_NAME, "topic");
	
	String message = "发布了一条中国新闻消息";
	channel.basicPublish(EXCHANGE_NAME, "china.news", null, message.getBytes());
	
	message = "发布了一条中国天气消息";
	channel.basicPublish(EXCHANGE_NAME, "china.weather", null, message.getBytes());
	
	message = "发布了一条美国新闻消息";
	channel.basicPublish(EXCHANGE_NAME, "usa.news", null, message.getBytes());
	
	message = "发布了一条美国天气消息";
	channel.basicPublish(EXCHANGE_NAME, "usa.weather", null, message.getBytes());
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
生产者和模式4(路由模式)的区别:
1、交换机exchange的type为topic
2、发送消息的routing key不是固定的单词,而是匹配字符串,如"china.#",匹配一个单词,#匹配0个或多个单词。因此如“china.#”能够匹配到“china.news.info”,但是“china. ”只会匹配到“china.news”

Consumer:
消费者1

package com.sc.queuemode.topics;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.sc.queuemode.connection.ConnectionUtil;

public class Customer1 {

private static final String QUEUE_NAME = "topics_queue1";
private static final String EXCHANGE_NAME = "topic_exchange";

public static void main(String[] args) throws IOException, TimeoutException{
	Connection connection = ConnectionUtil.getConnection();
	Channel channel = connection.createChannel();
	//交换机申明
	channel.exchangeDeclare(EXCHANGE_NAME, "topic");
	//队列声明
	channel.queueDeclare(QUEUE_NAME, true, false, false, null);
	//队列绑定交换机并制定路由routingKey
	channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "china.#");
	//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
	//保证在接收端一个消息没有处理完时不会接收另一个消息,即消费者端发送了ack后才会接收下一个消息。
	//在这种情况下生产者端会尝试把消息发送给下一个空闲的消费者。
	channel.basicQos(1);
	//申明消费者
	Consumer consumer1 = 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("通配符模式 消费者1消费:"+message);
			channel.basicAck(envelope.getDeliveryTag(), false);
		}
	};
	channel.basicConsume(QUEUE_NAME, false, consumer1);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
消费者2

package com.sc.queuemode.topics;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.sc.queuemode.connection.ConnectionUtil;

public class Customer2 {

private static final String QUEUE_NAME = "topics_queue2";
private static final String EXCHANGE_NAME = "topic_exchange";

public static void main(String[] args) throws IOException, TimeoutException{
	Connection connection = ConnectionUtil.getConnection();
	Channel channel = connection.createChannel();
	//交换机申明
	channel.exchangeDeclare(EXCHANGE_NAME, "topic");
	//队列声明
	channel.queueDeclare(QUEUE_NAME, true, false, false, null);
	//队列绑定交换机并制定路由routingKey
	channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "usa.#");
	//同一时刻服务器只发送1条消息给消费者(能者多劳,消费消息快的,会消费更多的消息)
	//保证在接收端一个消息没有处理完时不会接收另一个消息,即消费者端发送了ack后才会接收下一个消息。
	//在这种情况下生产者端会尝试把消息发送给下一个空闲的消费者。
	channel.basicQos(1);
	//申明消费者
	Consumer consumer2 = 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("通配符模式 消费者2消费:"+message);
			channel.basicAck(envelope.getDeliveryTag(), false);
		}
	};
	channel.basicConsume(QUEUE_NAME, false, consumer2);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
交换机和队列绑定如下图所示

————————————————
版权声明:本文为优快云博主「通往架构师的路上」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/caiqing116/article/details/84032099

<think>好的,我现在要解决用户的问题:RabbitMQ几种交换机。我需要先回忆或查找相关资料,确保回答准确。首先,RabbitMQ作为消息队列中间件,其交换机类型是关键组件,负责消息路由。根据之前的学习,常见的交换机类型有四种,但可能还有插件提供的其他类型。 首先,确认四种核心交换机类型:直连(Direct)、扇出(Fanout)、主题(Topic)和头(Headers)。每种类型有不同的路由机制。比如,Direct交换机根据路由键精确匹配,Fanout广播到所有绑定队列,Topic支持模式匹配,Headers则基于消息头而非路由键。 接下来,需要详细解释每种交换机的工作方式、适用场景以及示例。例如,Direct适用于单播路由,Fanout用于广播,Topic处理多条件匹配,Headers用于复杂的属性匹配。此外,可能需要提到默认交换机(无名交换机),它自动将队列绑定到其名称的路由键。 需要检查是否有其他类型,比如通过插件添加的,例如一致性哈希(Consistent Hash)交换机,但可能属于高级或较少用到的类型。在回答中应明确区分核心类型和扩展类型,避免混淆用户。 结构方面,按照用户要求,分点说明,结构清晰。每个类型用标题标记,解释其原理、路由规则、应用场景,可能给出使用示例或类比,帮助用户理解。 最后,确保使用中文,数学公式正确应用,但此问题可能涉及较少公式,更多是技术描述。检查LaTeX是否符合要求,例如变量用$...$,但此处可能不需要数学公式,除非举例中有涉及。 总结步骤:确认四种核心类型,补充默认交换机,检查是否有其他类型,组织内容结构,确保语言准确易懂,符合用户的中文需求,并验证信息正确性。</think>RabbitMQ的交换机(Exchange)是消息路由的核心组件,负责根据规则将消息分发到队列。以下是其主要的交换机类型及工作原理: --- ### 1. **直连交换机(Direct Exchange)** - **路由规则**: 通过精确匹配`routing key`(路由键)将消息发送到绑定队列。 *例如*:队列A绑定`routing_key="order"`,只有携带`order`的消息会被路由到队列A。 - **适用场景**: 单播(点对点)通信,如订单处理系统。 - **示例**: ```python channel.exchange_declare(exchange='direct_logs', exchange_type='direct') ``` --- ### 2. **扇出交换机(Fanout Exchange)** - **路由规则**: 忽略`routing key`,将消息**广播**到所有绑定的队列。 *类比*:类似群发通知,所有订阅者都会收到消息。 - **适用场景**: 发布/订阅模式,如新闻推送、实时广播。 - **示例**: ```python channel.exchange_declare(exchange='fanout_logs', exchange_type='fanout') ``` --- ### 3. **主题交换机(Topic Exchange)** - **路由规则**: 通过`routing key`的模式匹配(支持`*`和`#`通配符)路由消息。 - `*`匹配一个单词(如`order.*`匹配`order.new`但不匹配`order.new.paid`) - `#`匹配零或多个单词(如`order.#`匹配`order.new.paid`) - **适用场景**: 多条件动态路由,如日志分级(`error`、`info`)或分类消息(`user.notification`)。 - **示例**: ```python channel.exchange_declare(exchange='topic_logs', exchange_type='topic') ``` --- ### 4. **头交换机(Headers Exchange)** - **路由规则**: 不依赖`routing key`,而是根据消息的`headers`属性匹配绑定的键值对。 - 匹配方式:`x-match`参数设为`all`(完全匹配)或`any`(部分匹配)。 - **适用场景**: 复杂路由逻辑,如基于消息内容(如设备类型、版本号)过滤。 - **示例**: ```python headers = {'type': 'report', 'format': 'pdf', 'x-match': 'all'} channel.queue_bind(queue='queue1', exchange='headers_logs', arguments=headers) ``` --- ### 5. **默认交换机(Default Exchange)** - **特性**: 每个队列自动绑定到默认交换机(无名,用空字符串`""`标识),`routing key`默认为队列名。 - **使用场景**: 无需显式声明交换机,直接通过`routing key`指定队列名称发送消息。 --- ### 扩展类型(插件支持) - **一致性哈希交换机(Consistent Hash Exchange)** 通过哈希算法分配消息到队列,确保相同键的消息路由到同一队列(需启用插件`rabbitmq_consistent_hash_exchange`)。 --- ### 总结表格 | 交换机类型 | 路由依据 | 典型场景 | |-----------------|------------------------|----------------------------| | Direct | 精确匹配`routing key` | 点对点通信(如订单处理) | | Fanout | 无,广播所有队列 | 发布/订阅(如通知广播) | | Topic | 通配符匹配`routing key`| 多条件路由(如日志分类) | | Headers | 消息头属性 | 复杂属性过滤(如设备类型) | | Default | 队列名称 | 直接指定队列发送消息 | 合理选择交换机类型可优化消息路由效率,建议根据业务需求匹配最适方案。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值