七、RabbitMQ与Spring实战

1.场景

用户下订单买商品,订单处理成功后,去扣减库存,在这个场景里,订单系统是生产者,库存系统是消费者。

库存是必须扣减的,在业务上来说,有库存直接扣减即可,没库存或者低于某个阈值,可以扣减成功,不过要通知其他系统(如通知采购系统尽快采购,通知用户订单系统我们会尽快调货)。

2.RPC实现

通过RPC的实现,可以看到RPC会造成耦合。一旦库存系统失败,订单系统也会跟着失败。我们希望库存系统本身的失败,不影响订单系统的继续执行,在业务流程上,进行订单系统和库存系统的解耦。

3.消息中间件的实现

对于我们消息模式的实现,为保证库存必须有扣减,我们要考虑几个问题:

  • 1、订单系统发给Mq服务器的扣减库存的消息必须要被Mq服务器接收到,意味着需要使用发送者确认。
  • 2、Mq服务器在扣减库存的消息被库存服务正确处理前必须一直保存,那么需要消息进行持久化。
  • 3、某个库存服务器出了问题,扣减库存的消息要能够被其他正常的库存服务处理,需要我们自行对消费进行确认,意味着不能使用消费者自动确认,而应该使用手动确认。

所以生产者订单系统这边需要 ,配置文件中队列和交换器进行持久化,消息发送时的持久化,发送者确认的相关配置和代码。
所以消费者库存系统这边要进行手动确认。

4.与Spring集成时的更多配置

4.1 发送者确认

在这里插入图片描述

设置发送消息时的mandatory以及接收RabbitMQ中间件的应答

在这里插入图片描述

4.2 交换器持久化配置

在这里插入图片描述

4.3 消息持久化

在这里插入图片描述

4.4 队列参数(死信交换器、持久化、队列级别消息过期、独占模式)

在这里插入图片描述

4.5 消费者手动确认消息

在这里插入图片描述

处理库存类要实现ChannelAwareMessageListener。

在这里插入图片描述

和对消息确认或拒绝

在这里插入图片描述

5.实战

5.1 订单生产者

Spring配置

<?xml version="1.0" encoding="UTF-8"?>
<!-- 查找最新的schemaLocation 访问 http://www.springframework.org/schema/ -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:task="http://www.springframework.org/schema/task"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
    http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
    http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd
    http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-2.0.xsd">

    <!-- 配置扫描路径 -->
    <context:component-scan base-package="com.zava">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- rabbitMQ配置 -->
    <bean id="rabbitConnectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
        <constructor-arg value="127.0.0.1"/>
        <property name="username" value="guest"/>
        <property name="password" value="guest"/>
        <property name="channelCacheSize" value="8"/>
        <property name="port" value="5672"/>

        <!-- 发布确认 -->
        <property name="publisherConfirms" value="true"/>
    </bean>

    <rabbit:admin connection-factory="rabbitConnectionFactory"/>

    <rabbit:queue name="depot_queue" durable="true"/>

    <rabbit:direct-exchange name="depot-amount-exchange" durable="true">
        <rabbit:bindings>
            <rabbit:binding queue="depot_queue" key="amount.depot"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>

    <rabbit:template id="rabbitTemplate" connection-factory="rabbitConnectionFactory" mandatory="true"
                     return-callback="sendReturnCallback" confirm-callback="confirmCallback">
	</rabbit:template>
</beans>

发送消息

package com.zava.service;

import com.google.gson.Gson;
import com.zava.vo.GoodTransferVo;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

/**
 */
@Service
@Qualifier("mq")
public class MqMode  implements IProDepot {

    private final static String DEPOT_RK = "amount.depot";
    private final static String DEPOT_EXCHANGE = "depot-amount-exchange";

    @Autowired
    RabbitTemplate rabbitTemplate;

    private static Gson gson = new Gson();

    public void processDepot(String goodsId, int amount) {
        GoodTransferVo goodTransferVo = new GoodTransferVo();
        goodTransferVo.setGoodsId(goodsId);
        goodTransferVo.setChangeAmount(amount);
        goodTransferVo.setInOrOut(false);
        String goods = gson.toJson(goodTransferVo);
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);

        rabbitTemplate.send(DEPOT_EXCHANGE, DEPOT_RK, new Message(goods.getBytes(), messageProperties));

        rabbitTemplate.send(DEPOT_EXCHANGE, "ErrorRoute",
                new Message(goods.getBytes(), messageProperties));
    }
}

失败通知

package com.zava.service.callback;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

/**
 */
@Component
public class SendReturnCallback implements RabbitTemplate.ReturnCallback {
    public void returnedMessage(Message message, int replyCode,
                                String replyText, String exchange,
                                String routingKey) {
        String msg = new String(message.getBody());
        System.out.println("返回的replyText :"+replyText);
        System.out.println("返回的exchange :"+exchange);
        System.out.println("返回的routingKey :"+routingKey);
        System.out.println("返回的message :"+message);

    }
}

发布者确认

package com.zava.service.callback;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.stereotype.Component;

/**
 */
@Component
public class ConfirmCallback implements RabbitTemplate.ConfirmCallback {
    public void confirm(CorrelationData correlationData,
                        boolean ack,
                        String cause) {
        if(ack){
            System.out.println("消息发送给mq成功");
        }else{
            System.out.println("消息发送给mq失败,原因:"+cause);
        }

    }
}

5.2 库存消费者

Spring配置

<?xml version="1.0" encoding="UTF-8"?>
<!-- 查找最新的schemaLocation 访问 http://www.springframework.org/schema/ -->
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:mvc="http://www.springframework.org/schema/mvc"
	   xmlns:tx="http://www.springframework.org/schema/tx"
	   xmlns:jee="http://www.springframework.org/schema/jee"
	   xmlns:p="http://www.springframework.org/schema/p"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xmlns:task="http://www.springframework.org/schema/task"
	   xmlns:rabbit="http://www.springframework.org/schema/rabbit"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
    http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
    http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd
    http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-2.0.xsd">

     <!-- 配置扫描路径 -->
     <context:component-scan base-package="com.zava">
     	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
     </context:component-scan>

	<!-- rabbitMQ配置 -->
	<bean id="rabbitConnectionFactory"
		  class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
		<constructor-arg value="127.0.0.1"/>
		<property name="username" value="guest"/>
		<property name="password" value="guest"/>
		<property name="channelCacheSize" value="8"/>
		<property name="port" value="5672"></property>
	</bean>
	<rabbit:admin connection-factory="rabbitConnectionFactory"/>

    <rabbit:queue name="depot_queue" durable="true"/>

	<rabbit:direct-exchange name="depot-amount-exchange"
          xmlns="http://www.springframework.org/schema/rabbit" durable="true">
        <rabbit:bindings>
            <rabbit:binding queue="depot_queue" key="amount.depot"></rabbit:binding>
        </rabbit:bindings>
	</rabbit:direct-exchange>

	<!-- 对消息要手动确认 -->
    <rabbit:listener-container connection-factory="rabbitConnectionFactory"
	acknowledge="manual">
        <rabbit:listener queues="depot_queue" ref="processDepot"
                         method="onMessage" />
    </rabbit:listener-container>
</beans>

消费消息:手动确认

package com.zava.mq;

import com.google.gson.Gson;
import com.rabbitmq.client.Channel;
import com.zava.service.DepotManager;
import com.zava.vo.GoodTransferVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 */
@Service
public class ProcessDepot  implements ChannelAwareMessageListener{

    private static Logger logger = LoggerFactory.getLogger(ProcessDepot.class);

    @Autowired
    private DepotManager depotManager;

    private static Gson gson = new Gson();

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        String msg = new String(message.getBody());
        logger.info(">>>>>>>>>>>>>>接收到消息:"+msg);
        GoodTransferVo goodTransferVo = gson.fromJson(msg,GoodTransferVo.class);
        try {
            depotManager.operDepot(goodTransferVo);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            logger.info(">>>>>>>>>>>>>>库存处理完成,应答Mq服务");
        } catch (Exception e) {
            logger.error(e.getMessage());
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
            logger.info(">>>>>>>>>>>>>>库存处理失败,拒绝消息,要求Mq重新派发");
            throw e;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RabbitMQ是一个开源的消息中间件,用于在应用程序之间传递消息。它使用AMQP(高级消息队列协议)作为消息传递的标准协议。在实际应用中,可以使用RabbitMQ来实现消息的生产和消费。 要实现一个RabbitMQ实战demo,你可以按照以下步骤进行操作: 1. 首先,确保你已经安装了Erlang和RabbitMQ。你可以使用命令`rabbitmq-server -detached`来启动RabbitMQ服务,并使用`rabbitmqctl status`来查看服务状态。如果需要关闭服务,可以使用`rabbitmqctl stop`命令。 2. 在你的应用程序中,添加RabbitMQ的配置信息。你可以在应用的配置文件中添加以下内容: ``` spring.application.name=springboot-rabbitmq spring.rabbitmq.host=192.168.217.128 spring.rabbitmq.port=5672 spring.rabbitmq.username=developer spring.rabbitmq.password=dev123456 spring.rabbitmq.virtual-host=/ spring.rabbitmq.listener.direct.acknowledge-mode=manual ``` 其中,`spring.rabbitmq.host`是RabbitMQ的IP地址,`spring.rabbitmq.port`是RabbitMQ的端口号,`spring.rabbitmq.username`和`spring.rabbitmq.password`是RabbitMQ的用户名和密码,`spring.rabbitmq.virtual-host`是RabbitMQ的虚拟主机,`spring.rabbitmq.listener.direct.acknowledge-mode`是消息的确认模式。 3. 在你的应用程序中,编写生产者和消费者的代码。生产者负责发送消息到RabbitMQ,消费者负责接收并处理消息。你可以根据你的需求来编写相应的代码逻辑。 4. 运行你的应用程序,并测试RabbitMQ的功能。你可以通过发送消息到RabbitMQ来测试生产者的功能,然后通过消费者来接收并处理这些消息。 这是一个简单的RabbitMQ实战demo的步骤,你可以根据你的具体需求来进行相应的修改和扩展。希望对你有帮助!\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *2* [RabbitMQ 实战](https://blog.csdn.net/weixin_58026642/article/details/121486694)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [《RabbitMQ 实战指南》第一章 RabbitMQ 简介](https://blog.csdn.net/qq_45593575/article/details/122155110)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值