RabbitMQ消息实现方案

MQ消息的可靠投递

在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。官网描述几种模式地址:https://www.rabbitmq.com/getstarted.html

confirm 确认模式
return 退回模式

1、rabbitmq 整个消息投递的路径为:
producer—>rabbitmq broker—>exchange—>queue—>consumer
2、消息从 producer 到 exchange 则会返回一个 confirmCallback 。
3、消息从 exchange–>queue 投递失败则会返回一个 returnCallback 。

生产者的实现

public class Producer{
    @Autowired
    RabbitTemplate rabbitTemplate;
    @Autowired
    ConfirmCallback confirmCallback;
    @Autowired
    ReturnCallback returnCallback;
    @Autowired
    private CommonFeignClient commonFeignClient;

	/**
	* 推送MQ消息(第一种)不需要创建队列的MQ,可以队列创建在bean工厂里
	*/
    public void sendMail(String ex, String message) {
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setConfirmCallback(confirmCallback);
        rabbitTemplate.setReturnCallback(returnCallback);
        //第一个是参数交换机(exchange)的名字,第二个是路由键(routing-key)**的名字,第三个则为消息的内容
        rabbitTemplate.convertAndSend(ex, SysCode.BUSINESS.GROUP_KEY, message);
    }

	/**
     * 推送MQ消息(第二种)
     *
     * @param message
     * @return
     */
    public String sendMessage(MessageData message) {
        Connection connection = null;
        //创建信道
        Channel channel = null;
        try {
	        /**
		     * Mandatory为true时,消息通过交换器无法匹配到队列会返回给生产者
		     *          为false时,匹配不到会直接被丢弃
		     */
            rabbitTemplate.setMandatory(true);
            /**
             * 消费者确认收到消息后,手动ack回执回调处理
             */
            rabbitTemplate.setConfirmCallback(confirmCallback);
            /**
             * 消息投递到队列失败回调处理
             */
            rabbitTemplate.setReturnCallback(returnCallback);
            ConnectionFactory connectionFactory = rabbitTemplate.getConnectionFactory();
			//日志对象
            CommonInterfaceLog commonInterfaceLog = new CommonInterfaceLog();

            //创建连接
            connection = connectionFactory.createConnection();
            // 建立信道 构造参数 true代表该信道开启 Transactional 事务模式
 			// 此处传入true,就不需要再显示编码 channel.txSelect()了
 			// 内部已经调用了channle.txSelect();
            //创建信道
            channel = connection.createChannel(false);
            //在信道中设置交换器
            channel.exchangeDeclare(message.getEx(), ExchangeTypes.TOPIC, true);
            
            commonInterfaceLog.setCreateTime(new Date());
            commonInterfaceLog.setMqKey(message.getKey());
            commonInterfaceLog.setMqExchange(message  .getEx());
            commonInterfaceLog.setMqQueue(message.getQueue());
            commonInterfaceLog.setBusinessCode(message.getBusinessNumber());
            commonInterfaceLog.setSource(message.getSource());
            commonInterfaceLog.setMqReqType(ExchangeTypes.TOPIC);
            commonInterfaceLog.setRequestMessage(JSONObject.toJSONString(message.getMessageJsonStr()));
            commonInterfaceLog.setInterfaceType("MQ");
            //进行日志的写入
            commonFeignClient.commonInterfaceLogService(commonInterfaceLog);
            //发送消息
            log.info("------------pushMessageInfo[" + JSONObject.toJSONString(message.getMessageJsonStr()).getBytes().toString() + "]");
            channel.basicPublish(message.getEx(), message.getKey(),null, JSONObject.toJSONString(message.getMessageJsonStr()).getBytes());
            channel.close();
            connection.close();
        

            if(channel!=null||connection!=null){
                channel.clearConfirmListeners();
                channel.clearReturnListeners();
                connection.close();
            }
            return "------------pushMessageInfo[" + JSONObject.toJSONString(message.getMessageJsonStr()).getBytes().toString() + "]";
        } catch (Exception e) {
            log.info("MQ消息队列异常:" + message.getQueue() + ":" + message.getKey() + ":" + e.getMessage());
            e.printStackTrace();
            throw new RuntimeException("MQ消息队列异常:" + message.getQueue() + ":" + message.getKey() + ":" + e.getMessage());
        }
    }
}

ConfirmCallback 类的实现

//ConfirmCallback确认模式
@Slf4j
@Component
public class ConfirmCallback implements RabbitTemplate.ConfirmCallback {

    @Autowired
    RabbitTemplate rabbitTemplate;
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (ack && null != correlationData) {
            try{
                Message returnedMessage = correlationData.getReturnedMessage();
//                returnedMessage.getMessageProperties().getMessageId();
                log.info("消息发送成功:correlationData({}),ack({}),cause({})", new String(returnedMessage.getBody()), ack, cause);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            String uuid = rabbitTemplate.getUUID();
            log.info("消息发送失败:correlationData({}),ack({}),cause({}),uuid({})",correlationData.getReturnedMessage().getBody(),ack, cause,uuid);

        }
    }
}

ReturnCallback 类的实现

//ReturnCallback 退回模式
@Slf4j
@Component
public class ReturnCallback implements RabbitTemplate.ReturnCallback {


    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        log.info("消息退回 ===> replyCode={} ,replyText={} ,exchange={} ,routingKey={}", replyCode, replyText, exchange, routingKey);
    }
}

CommonfaceLog 日志表,根据自己的需求进行编写

@Data
@TableName("common_log")
public class CommonfaceLog implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.INPUT)
    private Integer id;
    /**
     * 业务编码
     */
    @TableField(value = "business_code")
    private String businessCode;
    /**
     * 接口名称
     */
    @TableField(value = "interface_name")
    private String interfaceName;
    /**
     * 接口类型
     */
    @TableField(value = "interface_type")
    private String interfaceType;
    /**
     * mq交换机
     */
    @TableField(value = "mq_exchange")
    private String mqExchange;
    /**
     * mq队列
     */
    @TableField(value = "mq_queue")
    private String mqQueue;
    /**
     * mq方式
     */
    @TableField(value = "mq_req_type")
    private String mqReqType;
    /**
     * topic_KEY
     */
    @TableField(value = "mq_key")
    private String mqKey;
    /**
     * 接口地址
     */
    @TableField(value = "interface_address")
    private String interfaceAddress;
    /**
     * 上次调用时间
     */
    @TableField(value = "last_call_time")
    private Date lastCallTime;
    /**
     * 请求报文
     */
    @TableField(value = "request_message")
    private String requestMessage;
    /**
     * 响应报文
     */
    @TableField(value = "response_message")
    private String responseMessage;
    /**
     * 失败次数
     */
    @TableField(value = "failures_number")
    private Integer failuresNumber;
    /**
     * 本次失败原因
     */
    @TableField(value = "this_failure_reasons")
    private String thisFailureReasons;
    /**
     * 创建时间
     */
    @TableField(value = "create_time")
    private Date createTime;
    /**
     * 请求状态
     */
    @TableField(value = "request_status")
    private Integer requestStatus;
    /**
     * 业务条线
     */
    @TableField(value = "source")
    private String source;
}

消费者的实现

@Log4j2
@Component("CarPolicysDirectListener")
public class CarPolicysDirectListener {
    @Autowired
    CoreSalesPolicysService coreSalesPolicysService;

    @Autowired
    CommonFeignClient commonFeignClient;

    @RabbitHandler
    @Qualifier("carPolicyListenerContainer") //指定消费的bean来控制消费者
    @RabbitListener( containerFactory = "carPolicysFactory", //bean名称,根据业务起
    		bindings = {@QueueBinding(
            value = @Queue(name = SysCode.CAR_POLICY.QUEUES, durable = "true"),//队列
            exchange = @Exchange(name = SysCode.CAR_POLICY.EX,//交换机
                    type = ExchangeTypes.DIRECT,//模式
                    ignoreDeclarationExceptions = "true"),
            key = SysCode.CAR_POLICY.KEY,//路由规则key
            ignoreDeclarationExceptions = "true")})
    public void getCarPolicysTopic(@Payload String message, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag, Channel channel){
        CommonInterfaceLog commonInterfaceLog =new CommonInterfaceLog();
        int length=255;
        try {
			//那个消息开始消费
            log.info("["+SysCode.SOURCE.CAR+"]getCarPolicysTopic监听到消息:"+message);
            //判断是否为空
            if(StringUtils.isNotBlank(message)){
				//把消息json串转换成你所需要的对象
                CoreSalesPolicysModel coreSalesPolicysModel = JSONObject.parseObject(message, CoreSalesPolicysModel.class);
                //执行相关的业务
                if (SysCode.CERTITYPE.POLICY.equals(coreSalesPolicysModel.getCertiType())){
                    commonInterfaceLog.setBusinessCode(coreSalesPolicysModel.getPolicyNo());
                }else if (SysCode.CERTITYPE.ENDORSE.equals(coreSalesPolicysModel.getCertiType())){
                    commonInterfaceLog.setBusinessCode(coreSalesPolicysModel.getCertiNo());
                }
                R r=coreSalesPolicysService.savePolicys(coreSalesPolicysModel);

				//异常处理
                if(500==r.getCode()){
                    log.info(""+r.getMsg());
                    commonInterfaceLog.setThisFailureReasons(r.getMsg());

                    //出现异常,告诉mq抛弃该消息
                    channel.basicNack(deliveryTag,false,false);
                }else {
                    //消费消息
                    channel.basicAck(deliveryTag, false);
                }

                commonInterfaceLog.setResponseMessage(r.toString());
                log.info("["+SysCode.SOURCE.CAR+"]getCarPolicysTopic执行完毕**************");

            }else {
                channel.basicReject(deliveryTag,false);
                //出现异常,告诉mq抛弃该消息
                channel.basicNack(deliveryTag,false,false);
            }

        } catch (Exception e) {
            commonInterfaceLog.setThisFailureReasons(null != e.getMessage() ? e.getMessage().substring(0, e.getMessage().length() > length ? length : e.getMessage().length()) : null);
            try {
                //出现异常,告诉mq抛弃该消息
                channel.basicNack(deliveryTag,false,false);
                e.printStackTrace();
            }catch (IOException e1) {
                e1.printStackTrace();
            }
        }finally {
        	//进行日志表的写入,记录MQ日志,建议存储到非关系数据库
            commonInterfaceLog.setInterfaceName("savePolicys");
            commonInterfaceLog.setInterfaceType("MQ");
            commonInterfaceLog.setMqExchange(SysCode.CAR_POLICY.EX);
            commonInterfaceLog.setMqQueue(SysCode.CAR_POLICY.QUEUES);
            commonInterfaceLog.setMqKey(SysCode.CAR_POLICY.KEY);
            commonInterfaceLog.setMqReqType(ExchangeTypes.DIRECT);
            commonInterfaceLog.setRequestMessage(message);
            commonInterfaceLog.setCreateTime(new Date());
            commonInterfaceLog.setSource(SysCode.SOURCE.CAR);
            commonFeignClient.saveInterfaceLog(commonInterfaceLog);
        }
    }
}

1、创建bean来控制MQ的消费者

    @Bean(name = "carPolicyListenerContainer")
    public SimpleRabbitListenerContainerFactory baseMultiListenerContainer(@Qualifier("carPolicyConnectionFactory") ConnectionFactory secondConnectionFactory){
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(secondConnectionFactory);
        //手动确认消息,默认自动确认消息
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        //初始化消费者数量
        factory.setConcurrentConsumers(5);
        //最大消费者数量
        factory.setMaxConcurrentConsumers(5);
		//一次处理的消息数量
        factory.setPrefetchCount(2);
        factory.setDefaultRequeueRejected(true);
        return factory;
    }

2、定义MQ的连接
其中Integer.parseInt(SysCode.RABBITMQPORT)是MQ的端口号,可以写活

    @Bean(name = "carPolicyConnectionFactory")
    public ConnectionFactory groupPolicyConnectionFactory(
            @Value("${spring.rabbitmq.group.addresses}") String addresses,//地址
            @Value("${spring.rabbitmq.group.virtualhost}") String virtualHost,//端口和指定位置组合,例如 /car,5672/car等
            @Value("${spring.rabbitmq.group.username}") String username,//用户名
            @Value("${spring.rabbitmq.group.password}") String password//密码
    ) {
        return connectionFactory1(addresses, Integer.parseInt(SysCode.RABBITMQPORT), username, password,virtualHost);
    }

    public CachingConnectionFactory connectionFactory1(String host, int port, String username, String password,String virtualHost) {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setAddresses(host);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(virtualHost);
        log.info("MQ配置:{}",connectionFactory);
        return connectionFactory;
    }

版权声明:转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Boy_Martin/article/details/126104042

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值