rabbitmq内置交换机消息发送接收例子

本文介绍了RabbitMQ中队列的使用,包括多个消费者从同一队列获取消息的公平分配,发布订阅模式下消息的广播,路由匹配实现精确分发,以及主题订阅实现灵活过滤。详细展示了不同场景下的代码实现,如消费者权重调整、路由键匹配以及主题订阅的多条件过滤。
摘要由CSDN通过智能技术生成

准备

创建一个工具类,用来建立连接,定义队列名

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class RabbitUtil {
	
	public static String HELLO_QUEUE = "helloworld";
	public static String WORK_QUEUE = "workqueue";
	public static String PUBSUB_QUEUE = "pubsub";
		
	public static String EXCHG_FRIENDMSG = "friend.msg";
	public static String EXCHG_ROUTEMSG = "route.msg";
	public static String EXCHG_TOPIC = "topic.msg";
	
	public static Connection getConn() throws Exception {
		ConnectionFactory factory = new ConnectionFactory();
		factory.setHost("127.0.0.1");
		factory.setPort(5672);
		factory.setUsername("guest");
		factory.setPassword("guest");
		Connection conn = factory.newConnection();
		return conn;
	}
}

1.一个队列多个消费者

创建队列

String QUEUE_NAME = RabbitUtil.WORK_QUEUE;
channel.queueDeclare(QUEUE_NAME, false, false, false, null);

启动消费者1:

Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
channel.basicQos(1);//每次只取一条
channel.basicConsume(RabbitUtil.WORK_QUEUE, false, new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties      properties, byte[] body)    throws IOException {
    System.out.println("处理消息:"+new String(body));
    channel.basicAck(envelope.getDeliveryTag(), false);
    }
});
System.out.println("consumer1 start");

启动消费者2:

Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
channel.basicQos(1);每次取1条
channel.basicConsume(RabbitUtil.WORK_QUEUE, false, new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)    throws IOException {
    System.out.println("处理消息:"+new String(body));
    try {
        //休眠一会,权重降低
        Thread.sleep(100);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    channel.basicAck(envelope.getDeliveryTag(), false);

}
});
System.out.println("consumer2 start");

生产消息:

String QUEUE_NAME = RabbitUtil.WORK_QUEUE;
String message = "hello";
for(int i=0;i<10;i++) {
  channel.basicPublish("", QUEUE_NAME, null, (message+i).getBytes());
}
System.out.println("send message over");

最后打印结果:

consumer1 start
处理消息:hello0
处理消息:hello2
处理消息:hello3
处理消息:hello4
处理消息:hello5
处理消息:hello7
处理消息:hello8
处理消息:hello9


consumer2 start
处理消息:hello1
处理消息:hello6

两个消费者都能重队列获取到消息。第二个队列处理消息时睡眠100毫秒,很明显获取到消息数变少。

2.发布订阅

准备一个fanout类型的exchange,创建两个队列同时获取相同的一条消息。

类似朋友圈发消息,一个发圈,另外两个好友都可看到。

定义交换机与队列

channel.exchangeDeclare(RabbitUtil.EXCHG_FRIENDMSG, "fanout");
channel.queueDeclare("follower1", false, false, false, null);
channel.queueDeclare("follower2", false, false, false, null);
//将队列与交换机绑定
channel.queueBind("follower1", RabbitUtil.EXCHG_FRIENDMSG, "");
channel.queueBind("follower2", RabbitUtil.EXCHG_FRIENDMSG, "");

启动第一个队列消费者

String QUEUE_NAME = "follower1";
Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
channel.basicConsume(QUEUE_NAME, false, new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)    throws IOException {
    System.out.println("处理消息:"+new String(body));
    channel.basicAck(envelope.getDeliveryTag(), false);
    }
});
System.out.println("follower1 start");

启动第二个队列消费者

String QUEUE_NAME = "follower2";
Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
channel.basicConsume(QUEUE_NAME, false, new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)    throws IOException {
    System.out.println("处理消息:"+new String(body));
    channel.basicAck(envelope.getDeliveryTag(), false);
    }
});
System.out.println("follower2 start");

发布一条消息

Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
String message = "today ok";
channel.basicPublish(RabbitUtil.EXCHG_FRIENDMSG, "", null, (message).getBytes());

输出结果

follower2 start
处理消息:today ok


follower1 start
处理消息:today ok

两个队列都能收到发布的消息。

3.路由匹配

使用route类型的交换机,根据routeKey精确匹配

定义交换机和队列

//direct类型的exchange
channel.exchangeDeclare(RabbitUtil.EXCHG_ROUTEMSG, "direct");
//模拟两个朋友圈分组,定义两个队列
String QUEUE1 = "follower_colleague";//同事组队列
String QUEUE2 = "follower_familiy";//家庭组队列
channel.queueDeclare(QUEUE1, false, false, false, null);
channel.queueDeclare(QUEUE2, false, false, false, null);
//同事组绑定两个路由key值colleague、all
channel.queueBind(QUEUE1, RabbitUtil.EXCHG_ROUTEMSG, "colleague");
channel.queueBind(QUEUE1, RabbitUtil.EXCHG_ROUTEMSG, "all");
//家庭组绑定两个路由key值family、all
channel.queueBind(QUEUE2, RabbitUtil.EXCHG_ROUTEMSG, "family");
channel.queueBind(QUEUE2, RabbitUtil.EXCHG_ROUTEMSG, "all");
/*
* 这样就有三个组,all,family,colleague。
* 如果发消息路由key是all,则两个队列都能收到消息。
* 如果发消息路由key是family,则colleague第一个队列收不到消息,相当于被屏蔽了。
*/

发布消息

Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
//第一条消息 routeKey是all两个队列都能收到
String message = "hello";
String routeKey = "all";
channel.basicPublish(RabbitUtil.EXCHG_ROUTEMSG, routeKey, null, message.getBytes());

//第二条消息,routeKey是family只有QUEUE2收到
message = "work is hard";
routeKey = "family";
channel.basicPublish(RabbitUtil.EXCHG_ROUTEMSG, routeKey, null, message.getBytes());

//第三条消息。routeKey是colleague只有QUEUE1收到
message = "my father is fanny";
routeKey = "colleague";
channel.basicPublish(RabbitUtil.EXCHG_ROUTEMSG, routeKey, null, message.getBytes());

//第四条消息。routeKey是none,没有队列绑定。两个队列都未绑定,都收不到该条消息,会被丢弃。
String message = "life is cool";
/*
* 没有人关注绑定我这个routeKey,也没有人能看到我这条消息
*/
String routeKey = "none";
channel.basicPublish(RabbitUtil.EXCHG_ROUTEMSG, routeKey, null, message.getBytes());

family队列消费者

String QUEUE_NAME = "follower_familiy";
Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
channel.basicQos(1);
channel.basicConsume(QUEUE_NAME, false, new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)    throws IOException {
    System.out.println("处理消息:"+new String(body));
    channel.basicAck(envelope.getDeliveryTag(), false);
    }
});
System.out.println("family start");
/**
输出结果:
family start
处理消息:hello
处理消息:work is hard
*/

colleague队列消费者

String QUEUE_NAME = "follower_colleague";
Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
channel.basicQos(1);
channel.basicConsume(QUEUE_NAME, false, new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)    throws IOException {
    System.out.println("处理消息:"+new String(body));
    channel.basicAck(envelope.getDeliveryTag(), false);
    }
});
System.out.println("colleague start");
/**
输出结果:
colleague start
处理消息:hello
处理消息:my father is fanny
*/

4.主题订阅

模拟新闻资讯主题订阅

交互机与队列定义

//topic类型的exchange
channel.exchangeDeclare(RabbitUtil.EXCHG_TOPIC, "topic");

String QUEUE1 = "topic_q1";//用户1
String QUEUE2 = "topic_q2";//用户2
String QUEUE3 = "topic_q3";//用户3
String QUEUE4 = "topic_q4";//用户4
channel.queueDeclare(QUEUE1, false, false, false, null);
channel.queueDeclare(QUEUE2, false, false, false, null);
channel.queueDeclare(QUEUE3, false, false, false, null);
channel.queueDeclare(QUEUE4, false, false, false, null);
//QUEUE1只关注生活
channel.queueBind(QUEUE1, RabbitUtil.EXCHG_TOPIC, "*.*.life");
//QUEUE2只关注新闻
channel.queueBind(QUEUE2, RabbitUtil.EXCHG_TOPIC, "news.*.*");
//QUEUE3只关注体育
channel.queueBind(QUEUE3, RabbitUtil.EXCHG_TOPIC, "sport.*.*");
//QUEUE4只关注nba和英超
channel.queueBind(QUEUE4, RabbitUtil.EXCHG_TOPIC, "*.*.nba");
channel.queueBind(QUEUE4, RabbitUtil.EXCHG_TOPIC, "*.*.yingchao");

发送不同主题类型消息

Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();

HashMap<String, String> msgs = new HashMap<String, String>();
msgs.put("news.beijing.life", "news.bj.life");
msgs.put("news.beijing.ecnomic", "news.bj.ecnomic");
msgs.put("news.beijing.story", "news.bj.story");
msgs.put("news.shanghai.life", "news.shanghai.life");
msgs.put("sport.foot.yingchao", "sport.foot.yingchao");
msgs.put("sport.foot.xijia", "sport.foot.xijia");
msgs.put("sport.bask.nba", "sport.bask.nba");
msgs.put("sport.bask.cba", "sport.bask.cba");
for(Entry<String, String> msg: msgs.entrySet()) {
channel.basicPublish(RabbitUtil.EXCHG_TOPIC, msg.getKey(),
null, msg.getValue().getBytes());
}

user1消费者:

String QUEUE_NAME = "topic_q1";
Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
String routeKey = "*.*.life";
channel.basicConsume(QUEUE_NAME, false, new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)    throws IOException {
    System.out.println("处理消息:"+new String(body));
    channel.basicAck(envelope.getDeliveryTag(), false);
    }
});
System.out.println("user1 "+routeKey+":start");
/**
输出结果:
user1 *.*.life:start
处理消息:news.shanghai.life
处理消息:news.bj.life
*/

user2消费者:

String QUEUE_NAME = "topic_q2";
Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
String routeKey = "news.*.*";
channel.basicConsume(QUEUE_NAME, false, new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)    throws IOException {
    System.out.println("处理消息:"+new String(body));
    channel.basicAck(envelope.getDeliveryTag(), false);
    }
});
System.out.println("user2 "+routeKey+":start");
/**
输出结果:
user2 news.*.*:start
处理消息:news.shanghai.life
处理消息:news.bj.ecnomic
处理消息:news.bj.life
处理消息:news.bj.story
*/

user3消费者:

String QUEUE_NAME = "topic_q3";
Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
String routeKey = "sport.*.*";
//与交换机绑定
channel.basicConsume(QUEUE_NAME, false, new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)    throws IOException {
    System.out.println("处理消息:"+new String(body));
    channel.basicAck(envelope.getDeliveryTag(), false);
    }
});
System.out.println("user3 "+routeKey+":start");
/**
输出结果:
user3 sport.*.*:start
处理消息:sport.foot.yingchao
处理消息:sport.foot.xijia
处理消息:sport.bask.cba
处理消息:sport.bask.nba
*/

user4消费者

String QUEUE_NAME = "topic_q4";
Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
channel.basicConsume(QUEUE_NAME, false, new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)    throws IOException {
    System.out.println("处理消息:"+new String(body));
    channel.basicAck(envelope.getDeliveryTag(), false);
    }
});
System.out.println("user4 [nba&yingchao]:start");
/**
输出结果:
user4 [nba&yingchao]:start
处理消息:sport.foot.yingchao
处理消息:sport.bask.nba
*/

5.多属性匹配

队列交换机定义

String queue = "headerqueue";
String exchange = RabbitUtil.EXCHG_HEADER;
//header类型的交换机
channel.exchangeDeclare(exchange, BuiltinExchangeType.HEADERS);
channel.queueDeclare(queue, false, false, false, null);
//设置队列header属性
Map<String, Object> headers = new HashMap<String, Object>();
//x-match : all所有属性都要匹配/ any 任意一个匹配即可
headers.put("x-match", "all");
headers.put("sex", "boy");
headers.put("city", "beijing");
//将队列与交换机绑定 设置以上header信息
channel.queueBind(queue, exchange, "",headers);

发送一条与上面队列绑定属性完全相符的消息

Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
String message = "bj ok";
//设置发布消息属性
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("x-match", "all");
headers.put("sex", "boy");
headers.put("city", "beijing");
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.headers(headers).build();
channel.basicPublish(RabbitUtil.EXCHG_HEADER, "", props, (message).getBytes());

启动队列消费者

String QUEUE_NAME = "headerqueue";
Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
channel.basicConsume(QUEUE_NAME, false, new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
    throws IOException {
    System.out.println("处理消息:"+new String(body));
    channel.basicAck(envelope.getDeliveryTag(), false);
    }
});
System.out.println("headerqueue start");
/**
headerqueue start
处理消息:bj ok
*/

发送一个不匹配的消息

Connection conn = RabbitUtil.getConn();
Channel channel = conn.createChannel();
String message = "shanghai ok";
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("x-match", "all");
headers.put("sex", "boy");
headers.put("city", "shanghai");
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.headers(headers).build();
//添加消息被退回监听
channel.addReturnListener(new ReturnCallback() {

    @Override
    public void handle(Return returnMessage) {
        int replyCode = returnMessage.getReplyCode();
        String replyText = returnMessage.getReplyText();
        System.out.println("消息被退回了,code:"+replyCode+",text:"+replyText);
    }
});
/*
* mandatory 参数设置true表示消息不能路由到队列时会被return
* basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body) 
*/
channel.basicPublish(RabbitUtil.EXCHG_HEADER, "",true,false, props, (message).getBytes());
/**
输出结果:
消息被退回了,code:312,text:NO_ROUTE
*/
		

官方关于mandatory 的解释:

https://www.rabbitmq.com/publishers.html

When a published message cannot be routed to any queue (e.g. because there are no bindings defined for the target exchange), and the publisher set the mandatory message property to false (this is the default), the message is discarded or republished to an alternate exchange, if any.

When a published message cannot be routed to any queue, and the publisher set the mandatory message property to true, the message will be returned to it. The publisher must have a returned message handler set up in order to handle the return (e.g. by logging an error or retrying with a different exchange)

immediate:

方法给的提示Note that the RabbitMQ server does not support this flag.

应该是以后不会再继续支持该参数了。本来作用百度看是当路由投递到的队列如果没有消费者时也被推回。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值