rabbitmq java 广播_rabbitmq 在 java 的应用

本文介绍了如何在Java应用中选择和使用RabbitMQ,详细讲解了RabbitMQ的基础知识,包括生产者、消费者、队列、交换机等概念。接着展示了在Spring Boot中配置和使用RabbitMQ,特别强调了如何发送延迟消息以及处理错误。通过具体的代码示例,演示了点对点、广播和主题路由模式的监听方法。
摘要由CSDN通过智能技术生成

1. mq的选型

rabbitq

kafka

rocketmq[开源]

activemq

rocketmq[ali]

SQS[aws]

云服务支持

×(aws)√(ali)

×

性能

3

5

4

2

5

5

易用性

5

4

3

5

3

3

功能性

高级特性支持mqtt/延迟

不支持

手动性

支持延迟

支持延迟

支持延迟(最大15分钟延时)

更多参数对比请参照链接

2. rabbitmq基础知识普及

生产者(Producer):发送消息的应用

消费者(Consumer):接收消息的应用。

队列(Queue):存储消息的缓存。

消息(Message):由生产者通过RabbitMQ发送给消费者的信息。由(headers[hashmap]和payload)组成

连接(Connection):连接RabbitMQ和应用服务器的TCP连接

通道(Channel):连接里的一个虚拟通道。当你通过消息队列发送或者接收消息时,这个操作都是通过通道进行的。

连接(Connection):连接RabbitMQ和应用服务器的TCP连接

交换机(Exchange):交换机负责从生产者那里接收消息,并根据交换类型分发到对应的消息列队里。要实现消息的接收,一个队列必须到绑定一个交换机。

math?formula=%5Ccolor%7B%23FF0000%7D%7B%E5%AD%98%E5%9C%A8%E7%9A%84%E6%84%8F%E4%B9%89%3AAMQP%20%E5%8D%8F%E8%AE%AE%E4%B8%AD%E7%9A%84%E6%A0%B8%E5%BF%83%E6%80%9D%E6%83%B3%E5%B0%B1%E6%98%AF%E7%94%9F%E4%BA%A7%E8%80%85%E5%92%8C%E6%B6%88%E8%B4%B9%E8%80%85%E7%9A%84%E8%A7%A3%E8%80%A6%7D

direct--------------------------点到点的模式

fanout-------------------------广播模式

topic---------------------------路由模式(rountingKey) 正则表达式

header------------------------路由模式(headers)

x-delayed-message-------需要安装延迟队列插件标识(x-delayed-type 标志了这个队列除了是个延迟队列,他还属于上面4种队列种的一个)

5573231216c5

在管理界面新建延时exchange.png

安装延迟队列插件:

插件下载 rabbitmq_delayed_message_exchange.ez【请下载rabbitmq对应的版本】

放在rabbitmq安装目录的plugins目录下

执行rabbitmq-plugins enable rabbitmq_delayed_message_exchange

绑定(Binding):绑定是队列和交换机的一个关联连接。

路由键(Routing Key):路由键是供交换机查看并根据键来决定如何分发消息到列队的一个键。路由键可以说是消息的目的地址。

5573231216c5

图片来源https://www.jianshu.com/p/256c502d09cd

3.rabbitmq在java的运用

因为我们经常使用spring boot,所以cuber决定选用spring-boot-starter-amqp

引用依赖

org.springframework.boot

spring-boot-starter-amqp

application.yml设置如下:

spring:

rabbitmq:

addresses: 10.8.3.95:5672

username: admin

password: admin

virtual-host: rabbitmq_vhost

输入输出jackson

@Bean

//定义message 转换类[使用spring 已有的jackson bean objectMapper(这样定义的jackson配置参数对rabbitmq也共用一套)]

public Jackson2JsonMessageConverter messageConverter(ObjectMapper objectMapper) {

return new Jackson2JsonMessageConverter(objectMapper);

}

@EventListener

// 不自定义rabbitTemplate 和 Jackson2JsonMessageConverter 是因为不影响配置参数被清空

public void applicationReady(ApplicationReadyEvent applicationReadyEvent) {

RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);

Jackson2JsonMessageConverter jackson2JsonMessageConverter = applicationContext.getBean(Jackson2JsonMessageConverter.class);

SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory = applicationContext.getBean(SimpleRabbitListenerContainerFactory.class);

rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter);

simpleRabbitListenerContainerFactory.setMessageConverter(jackson2JsonMessageConverter);

}

发送延迟队列

//1.所有的延迟队列都使用一个交换器(exchange), 不应该使用网上现有的direct的延迟队列

//2.延迟参数其实可以为long类型, 最大为2^31-1 毫秒, 也就是说最大延时大概是49天多一点

public void sendDelay(String routingKey, T t, TimeUnit timeUnit, long times) {

rabbitTemplate.convertAndSend("x-delay-exchange", routingKey, t, message -> {

long timeDelay = timeUnit.toMillis(times);

message.getMessageProperties().setHeader(MessageProperties.X_DELAY, timeDelay);

return message;

});

}

发送延时流 pt5s,pt20s,pt1m,pt1m,pt5m,pt30m,pt2h,p1d

延迟5s执行, 执行失败延迟20s执行.....

结合message 的header 和延时队列来做

发送端

//将延迟数组字符串放在头部,当前游标放置头部

public void sendSequence(String routingKey, T t, String sequence) {

if (!isRight(sequence)) {

throw new RuntimeException("[" + sequence + "]非法");

}

rabbitTemplate.convertAndSend("x-delay-exchange", routingKey, t, message -> {

String[] durations = sequence.split(",");

Duration duration = Duration.parse(durations[0]);

message.getMessageProperties().setHeader("PHP-DELAY-SEQUENCE", sequence);

message.getMessageProperties().setHeader("PHP-DELAY-SEQUENCE-CURRENT", 0);

message.getMessageProperties().setHeader(MessageProperties.X_DELAY, duration.toMillis());

return message;

});

}

//延迟数组字符串是否合法

public static boolean isRight(String sequence) {

boolean result = false;

if (StringUtils.isNotBlank(sequence)) {

String[] durations = sequence.split(",");

result = Arrays.stream(durations).allMatch(RabbitmqPlusTemplate::match);

}

return result;

}

//Duration 实现类的正则表达式

public static final Pattern PATTERN =

Pattern.compile("([-+]?)P(?:([-+]?[0-9]+)D)?" +

"(T(?:([-+]?[0-9]+)H)?(?:([-+]?[0-9]+)M)?(?:([-+]?[0-9]+)(?:[.,]([0-9]{0,9}))?S)?)?",

Pattern.CASE_INSENSITIVE);

private static boolean match(String text) {

Matcher matcher = PATTERN.matcher(text);

return matcher.matches();

}

错误处理类

@Slf4j

public class Rabbitmq2ErrorHandler implements RabbitListenerErrorHandler {

private RabbitTemplate rabbitTemplate;

private AmqpAdmin amqpAdmin;

public Rabbitmq2ErrorHandler(RabbitTemplate rabbitTemplate, AmqpAdmin amqpAdmin) {

this.rabbitTemplate = rabbitTemplate;

this.amqpAdmin = amqpAdmin;

}

@Override

public Object handleError(Message message, org.springframework.messaging.Message> message1, ListenerExecutionFailedException e) throws Exception {

String receivedExchange = message.getMessageProperties().getReceivedExchange();

String consumerQueue = message.getMessageProperties().getConsumerQueue();

log.warn("[{}]消费消息失败:{}", consumerQueue, message);

log.error("消费失败", e.getCause());

try {

Map headers = message.getMessageProperties().getHeaders();

String resendQueue = resendQueue(headers, consumerQueue);

if (StringUtils.isNotBlank(resendQueue)) {

Message resendMessage = MessageBuilder.withBody(message.getBody())

.setContentType(message.getMessageProperties().getContentType())

.copyHeaders(headers)

.build();

rabbitTemplate.send(receivedExchange, resendQueue, resendMessage);

} else {

log.warn("消息不会重新发送没有设置deadCallQueue");

}

} catch (Exception error) {

log.error("错误处理遇见问题", error);

}

return null;

}

public String resendQueue(Map headers, String consumerQueue) {

String dealCall = getDealCallQueue(consumerQueue);

String queueName = dealCall;

if (MapUtils.isNotEmpty(headers) && headers.containsKey("PHP-DELAY-SEQUENCE")) {

String[] durations = StringUtils.split(MapUtils.getString(headers, "PHP-DELAY-SEQUENCE"), ",");

Integer index = MapUtils.getInteger(headers, "PHP-DELAY-SEQUENCE-CURRENT") + 1;

if (index < durations.length) {

headers.put("PHP-DELAY-SEQUENCE-CURRENT", index);

Duration duration = Duration.parse(durations[index]);

headers.put(MessageProperties.X_DELAY, duration.toMillis());

queueName = consumerQueue;

} else {

headers.remove("PHP-DELAY-SEQUENCE");

}

}

//如果转到deal_call

if (StringUtils.equals(queueName, dealCall)) {

QueueInformation dealCallQueue = amqpAdmin.getQueueInfo(dealCall);

if (Objects.isNull(dealCallQueue)) {

queueName = StringUtils.EMPTY;

}

}

return queueName;

}

private String getDealCallQueue(String consumerQueue) {

return consumerQueue + "_DEAD_CALL";

}

}

队列监听

//点到点发送

@RabbitListener(queuesToDeclare = @Queue("test_p2p"))

public void rabbitmq_p2p(GatewayAccess gatewayAccess) throws Exception {

log.info("point_point:[{}]", gatewayAccess);

}

//广播a

@RabbitListener(bindings = @QueueBinding(value = @Queue("a"),

exchange = @Exchange(value = "test_fanout", type = ExchangeTypes.FANOUT)))

public void fanout_a(GatewayAccess gatewayAccess) throws Exception {

log.info("fanout_a:[{}]", gatewayAccess);

}

//广播b

@RabbitListener(bindings = @QueueBinding(value = @Queue("b"),

exchange = @Exchange(value = "test_fanout", type = ExchangeTypes.FANOUT)))

public void fanout_b(GatewayAccess gatewayAccess) throws Exception {

log.info("fanout_a:[{}]", gatewayAccess);

}

//topic

@RabbitListener(bindings = @QueueBinding(value = @Queue("topic_a"),

exchange = @Exchange(value = "test_topic", type = ExchangeTypes.TOPIC), key = "topic_a"))

public void topic_a(GatewayAccess gatewayAccess) throws Exception {

log.info("topic_a:[{}]", gatewayAccess);

}

//topic

@RabbitListener(bindings = @QueueBinding(value = @Queue("topic_b"),

exchange = @Exchange(value = "test_topic", type = ExchangeTypes.TOPIC), key = "topic_b"))

public void topic_b(GatewayAccess gatewayAccess) throws Exception {

log.info("topic_b:[{}]", gatewayAccess);

}

//Rabbitmq2ErrorHandler

@RabbitListener(bindings = @QueueBinding(value = @Queue("test_error"),

exchange = @Exchange(value = "x-delay-exchange", type = "x-delayed-message", arguments =

@Argument(name ="x-delayed-type", value = ExchangeTypes.TOPIC)), key = "test_error"), errorHandler = "rabbitmq2ErrorHandler")

public void test_error(GatewayAccess gatewayAccess) throws Exception {

log.info("test_error[{}]", gatewayAccess);

throw new RuntimeException("故意出错");

}

@RabbitListener(bindings = @QueueBinding(value = @Queue("test_error" + RabbitmqConstant._DEAD_CALL),

exchange = @Exchange(value = "x-delay-exchange", type = "x-delayed-message", arguments =

@Argument(name = "x-delayed-type", value = ExchangeTypes.TOPIC)), key = "test_error" + "_DEAD_CALL"))

public void test_error_dead_call(GatewayAccess gatewayAccess) throws Exception {

log.info("test_error_dead_call[{}]", gatewayAccess);

}

使用@queuesToDeclare会创建队列,当队列不存在的时候

使用 @QueueBinding会创建exchange,bing,queue还有rountingKey

手动设置延迟时间,在rocketmq 启动的时候就需要设置好,以后都不能更改 ↩

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值