rabbitmq在商户通知中的使用代码实例

代码选自jeepay

controller层:

@RestController
@RequestMapping("/api/mchNotify")
public class MchNotifyController extends CommonCtrl {

    @Autowired private MchNotifyRecordService mchNotifyService;
    @Autowired private IMQSender mqSender;

@PreAuthorize("hasAuthority('ENT_MCH_NOTIFY_RESEND')")
@RequestMapping(value="resend/{notifyId}", method = RequestMethod.POST)
public ApiRes resend(@PathVariable("notifyId") Long notifyId) {
    MchNotifyRecord mchNotify = mchNotifyService.getById(notifyId);
    if (mchNotify == null) {
        return ApiRes.fail(ApiCodeEnum.SYS_OPERATION_FAIL_SELETE);
    }
    if (mchNotify.getState() != MchNotifyRecord.STATE_FAIL) {
        throw new BizException("请选择失败的通知记录");
    }

    //更新通知中
    mchNotifyService.getBaseMapper().updateIngAndAddNotifyCountLimit(notifyId);

    //调起MQ重发
    mqSender.send(PayOrderMchNotifyMQ.build(notifyId));

    return ApiRes.ok(mchNotify);
}
    
}

MQ 消息发送器 接口

public interface IMQSender {

    /** 推送MQ消息, 实时 **/
    void send(AbstractMQ mqModel);

    /** 推送MQ消息, 延迟接收,单位:s **/
    void send(AbstractMQ mqModel, int delay);

}

有三个实现类,根据自己使用的mq类型来选择:
在这里插入图片描述
这里看rabbitmq的:

@Component
@ConditionalOnProperty(name = MQVenderCS.YML_VENDER_KEY, havingValue = MQVenderCS.RABBIT_MQ)
//在spring boot中有时候需要控制配置类是否生效,可以使用@ConditionalOnProperty注解来控制@Configuration是否生效.

public class RabbitMQSender implements IMQSender {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public void send(AbstractMQ mqModel) {

        if(mqModel.getMQType() == MQSendTypeEnum.QUEUE){

            rabbitTemplate.convertAndSend(mqModel.getMQName(), mqModel.toMessage());
        }else{

            // fanout模式 的 routeKEY 没意义。
            this.rabbitTemplate.convertAndSend(RabbitMQConfig.FANOUT_EXCHANGE_NAME_PREFIX + mqModel.getMQName(), null, mqModel.toMessage());
        }
    }

    @Override
    public void send(AbstractMQ mqModel, int delay) {


        if(mqModel.getMQType() == MQSendTypeEnum.QUEUE){

            rabbitTemplate.convertAndSend(RabbitMQConfig.DELAYED_EXCHANGE_NAME, mqModel.getMQName(), mqModel.toMessage(), messagePostProcessor ->{
                messagePostProcessor.getMessageProperties().setDelay(Math.toIntExact(delay * 1000));
                return messagePostProcessor;
            });
        }else{

            // fanout模式 的 routeKEY 没意义。  没有延迟属性
            this.rabbitTemplate.convertAndSend(RabbitMQConfig.FANOUT_EXCHANGE_NAME_PREFIX + mqModel.getMQName(), null, mqModel.toMessage());
        }
    }

}

PayOrderMchNotifyMQ的 build方法:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PayOrderMchNotifyMQ extends AbstractMQ {

    /** 【!重要配置项!】 定义MQ名称 **/
    public static final String MQ_NAME = "QUEUE_PAY_ORDER_MCH_NOTIFY";

    /** 内置msg 消息体定义 **/
    private MsgPayload payload;

    /**  【!重要配置项!】 定义Msg消息载体 **/
    @Data
    @AllArgsConstructor
    public static class MsgPayload {

        /** 通知单号 **/
        private Long notifyId;

    }

    @Override
    public String getMQName() {
        return MQ_NAME;
    }

    /**  【!重要配置项!】 **/
    @Override
    public MQSendTypeEnum getMQType(){
        return MQSendTypeEnum.QUEUE;  // QUEUE - 点对点 、 BROADCAST - 广播模式
    }

    @Override
    public String toMessage() {
        return JSONObject.toJSONString(payload);
    }

    /**  【!重要配置项!】 构造MQModel , 一般用于发送MQ时 **/
    public static PayOrderMchNotifyMQ build(Long notifyId){
        return new PayOrderMchNotifyMQ(new MsgPayload(notifyId));
    }

    /** 解析MQ消息, 一般用于接收MQ消息时 **/
    public static MsgPayload parse(String msg){
        return JSON.parseObject(msg, MsgPayload.class);
    }

    /** 定义 IMQReceiver 接口: 项目实现该接口则可接收到对应的业务消息  **/
    public interface IMQReceiver{
        void receive(MsgPayload payload);
    }

}

RabbitMQ的配置项


/**
* RabbitMQ的配置项
* 1. 注册全部定义好的Queue Bean
* 2. 动态注册fanout交换机
* 3. 将Queue模式绑定到延时消息的交换机
*
* @author terrfly
* @site https://www.jeequan.com
* @date 2021/7/23 16:33
*/
@Component
@ConditionalOnProperty(name = MQVenderCS.YML_VENDER_KEY, havingValue = MQVenderCS.RABBIT_MQ)
public class RabbitMQConfig {

    /** 全局定义延迟交换机名称 **/
    public static final String DELAYED_EXCHANGE_NAME = "delayedExchange";

    /** 扇形交换机前缀(activeMQ中的topic模式), 需根据queue动态拼接 **/
    public static final String FANOUT_EXCHANGE_NAME_PREFIX = "fanout_exchange_";

    /** 注入延迟交换机Bean **/
    @Autowired
    @Qualifier(DELAYED_EXCHANGE_NAME)
    private CustomExchange delayedExchange;

    /** 注入rabbitMQBeanProcessor **/
    @Autowired
    private RabbitMQBeanProcessor rabbitMQBeanProcessor;

    /** 在全部bean注册完成后再执行 **/
    @PostConstruct
    public void init(){

        // 获取到所有的MQ定义
        Set<Class<?>> set = ClassUtil.scanPackageBySuper(ClassUtil.getPackage(AbstractMQ.class), AbstractMQ.class);

        for (Class<?> aClass : set) {

            // 实例化
            AbstractMQ amq = (AbstractMQ) ReflectUtil.newInstance(aClass);

            // 注册Queue === new Queue(name),  queue名称/bean名称 = mqName
            rabbitMQBeanProcessor.beanDefinitionRegistry.registerBeanDefinition(amq.getMQName(),
                    BeanDefinitionBuilder.rootBeanDefinition(Queue.class).addConstructorArgValue(amq.getMQName()).getBeanDefinition());

            // 广播模式
            if(amq.getMQType() == MQSendTypeEnum.BROADCAST){

                // 动态注册交换机, 交换机名称/bean名称 =  FANOUT_EXCHANGE_NAME_PREFIX + amq.getMQName()
                rabbitMQBeanProcessor.beanDefinitionRegistry.registerBeanDefinition(FANOUT_EXCHANGE_NAME_PREFIX +amq.getMQName(),
                        BeanDefinitionBuilder.genericBeanDefinition(FanoutExchange.class, () ->{

                            // 普通FanoutExchange 交换机
                             return new FanoutExchange(FANOUT_EXCHANGE_NAME_PREFIX +amq.getMQName(),true,false);

                            // 支持 延迟的 FanoutExchange 交换机, 配置无效果。
//                            Map<String, Object> args = new HashMap<>();
//                            args.put("x-delayed-type", ExchangeTypes.FANOUT);
//                            return new CustomExchange(RabbitMQConfig.DELAYED_EXCHANGE_NAME, "x-delayed-message", true, false, args);
                        }

                        ).getBeanDefinition()
                );

            }else{

                // 延迟交换机与Queue进行绑定, 绑定Bean名称 = mqName_DelayedBind
                rabbitMQBeanProcessor.beanDefinitionRegistry.registerBeanDefinition(amq.getMQName() + "_DelayedBind",
                        BeanDefinitionBuilder.genericBeanDefinition(Binding.class, () ->
                                BindingBuilder.bind(SpringBeansUtil.getBean(amq.getMQName(), Queue.class)).to(delayedExchange).with(amq.getMQName()).noargs()

                        ).getBeanDefinition()
                );
            }
        }
    }
}

RabbitMQBeanProcessor


/**
* 将spring容器的 [bean注册器]放置到属性中,为 RabbitConfig提供访问。
*  顺序:
 *  1. postProcessBeanDefinitionRegistry (存放注册器)
 *  2. postProcessBeanFactory (没有使用)
 *  3. 注册延迟消息交换机的bean: delayedExchange
 *  4. 动态配置RabbitMQ所需的bean。
*/
@Configuration
@ConditionalOnProperty(name = MQVenderCS.YML_VENDER_KEY, havingValue = MQVenderCS.RABBIT_MQ)
public class RabbitMQBeanProcessor implements BeanDefinitionRegistryPostProcessor {
//对标准BeanFactoryPostProcessor SPI 的扩展,允许在常规 BeanFactoryPostProcessor 检测开始之前注册进一步的 bean 定义。
// 特别是,BeanDefinitionRegistryPostProcessor 可以注册进一步的 bean 定义,
// 这些定义反过来定义 BeanFactoryPostProcessor 实例
    /** bean注册器 **/
    protected BeanDefinitionRegistry beanDefinitionRegistry;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        this.beanDefinitionRegistry = beanDefinitionRegistry;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    }

    /** 自定义交换机: 用于延迟消息 **/
    @Bean(name = RabbitMQConfig.DELAYED_EXCHANGE_NAME)
    CustomExchange delayedExchange() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange(RabbitMQConfig.DELAYED_EXCHANGE_NAME, "x-delayed-message", true, false, args);
    }

}

MQ 线程池配置

@Configuration
@EnableAsync
public class MqThreadExecutor {

    public static final String EXECUTOR_PAYORDER_MCH_NOTIFY = "mqQueue4PayOrderMchNotifyExecutor";

    /*
     * 功能描述:
     * 支付结果通知到商户的异步执行器 (由于量大, 单独新建一个线程池处理, 之前的不做变动 )
     * 20, 300, 10, 60  该配置: 同一时间最大并发量300,(已经验证通过, 商户都可以收到请求消息)
     * 缓存队列尽量减少,否则将堵塞在队列中无法执行。  corePoolSize 根据机器的配置进行添加。此处设置的为20
     */
    @Bean
    public Executor mqQueue4PayOrderMchNotifyExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(20); // 线程池维护线程的最少数量
        executor.setMaxPoolSize(300);  // 线程池维护线程的最大数量
        executor.setQueueCapacity(10); // 缓存队列
        executor.setThreadNamePrefix("payOrderMchNotifyExecutor-");
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //对拒绝task的处理策略
        executor.setKeepAliveSeconds(60); // 允许的空闲时间
        executor.initialize();
        return executor;
    }

}

mq清除商户登录信息通知

/**
*
* 定义MQ消息格式
* 业务场景: [ 清除商户登录信息 ]
/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CleanMchLoginAuthCacheMQ extends AbstractMQ {

    /** 【!重要配置项!】 定义MQ名称 **/
    public static final String MQ_NAME = "QUEUE_CLEAN_MCH_LOGIN_AUTH_CACHE";

    /** 内置msg 消息体定义 **/
    private MsgPayload payload;

    /**  【!重要配置项!】 定义Msg消息载体 **/
    @Data
    @AllArgsConstructor
    public static class MsgPayload {

        /** 用户ID集合 **/
        private List<Long> userIdList;

    }

    @Override
    public String getMQName() {
        return MQ_NAME;
    }

    /**  【!重要配置项!】 **/
    @Override
    public MQSendTypeEnum getMQType(){
        return MQSendTypeEnum.QUEUE;  // QUEUE - 点对点 、 BROADCAST - 广播模式
    }

    @Override
    public String toMessage() {
        return JSONObject.toJSONString(payload);
    }

    /**  【!重要配置项!】 构造MQModel , 一般用于发送MQ时 **/
    public static CleanMchLoginAuthCacheMQ build(List<Long> userIdList){
        return new CleanMchLoginAuthCacheMQ(new MsgPayload(userIdList));
    }

    /** 解析MQ消息, 一般用于接收MQ消息时 **/
    public static MsgPayload parse(String msg){
        return JSON.parseObject(msg, MsgPayload.class);
    }

    /** 定义 IMQReceiver 接口: 项目实现该接口则可接收到对应的业务消息  **/
    public interface IMQReceiver{
        void receive(MsgPayload payload);
    }

}

对应消息接收器:

/**
 * rabbitMQ消息接收器:仅在vender=rabbitMQ时 && 项目实现IMQReceiver接口时 进行实例化
 * 业务:  清除商户登录信息
 * /
@Component
@ConditionalOnProperty(name = MQVenderCS.YML_VENDER_KEY, havingValue = MQVenderCS.RABBIT_MQ)
@ConditionalOnBean(CleanMchLoginAuthCacheMQ.IMQReceiver.class)
public class CleanMchLoginAuthCacheRabbitMQReceiver implements IMQMsgReceiver {

    @Autowired
    private CleanMchLoginAuthCacheMQ.IMQReceiver mqReceiver;

    /** 接收 【 queue 】 类型的消息 **/
    @Override
    @Async(MqThreadExecutor.EXECUTOR_PAYORDER_MCH_NOTIFY)
    @RabbitListener(queues = CleanMchLoginAuthCacheMQ.MQ_NAME)
    public void receiveMsg(String msg){
        mqReceiver.receive(CleanMchLoginAuthCacheMQ.parse(msg));
    }

}


/**
* 接收MQ消息
* 业务: 清除商户登录信息
* @author terrfly
* @site https://www.jeequan.com
* @date 2021/7/27 9:23
*/
@Slf4j
@Component
public class CleanMchLoginAuthCacheMQReceiver implements CleanMchLoginAuthCacheMQ.IMQReceiver {

    @Override
    public void receive(CleanMchLoginAuthCacheMQ.MsgPayload payload) {

        log.info("成功接收删除商户用户登录的订阅通知, msg={}", payload);
        // 字符串转List<Long>
        List<Long> userIdList = payload.getUserIdList();
        // 删除redis用户缓存
        if(userIdList == null || userIdList.isEmpty()){
            log.info("用户ID为空");
            return ;
        }
        for (Long sysUserId : userIdList) {
            Collection<String> cacheKeyList = RedisUtil.keys(CS.getCacheKeyToken(sysUserId, "*"));
            if(cacheKeyList == null || cacheKeyList.isEmpty()){
                continue;
            }
            for (String cacheKey : cacheKeyList) {
                // 删除用户Redis信息
                RedisUtil.del(cacheKey);
                continue;
            }
        }
        log.info("无权限登录用户信息已清除");
    }
}

mq支付订单商户通知

@Component
@ConditionalOnProperty(name = MQVenderCS.YML_VENDER_KEY, havingValue = MQVenderCS.RABBIT_MQ)
@ConditionalOnBean(PayOrderMchNotifyMQ.IMQReceiver.class)
public class PayOrderMchNotifyRabbitMQReceiver implements IMQMsgReceiver {

    @Autowired
    private PayOrderMchNotifyMQ.IMQReceiver mqReceiver;

    /** 接收 【 queue 】 类型的消息 **/
    @Override
    @Async(MqThreadExecutor.EXECUTOR_PAYORDER_MCH_NOTIFY)
// @Async中的参数用于指定使用哪一个线程池执行
    @RabbitListener(queues = PayOrderMchNotifyMQ.MQ_NAME)
    public void receiveMsg(String msg){
        mqReceiver.receive(PayOrderMchNotifyMQ.parse(msg));
    }

}

PayOrderMchNotifyMQ:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PayOrderMchNotifyMQ extends AbstractMQ {

    /** 【!重要配置项!】 定义MQ名称 **/
    public static final String MQ_NAME = "QUEUE_PAY_ORDER_MCH_NOTIFY";

    /** 内置msg 消息体定义 **/
    private MsgPayload payload;

    /**  【!重要配置项!】 定义Msg消息载体 **/
    @Data
    @AllArgsConstructor
    public static class MsgPayload {

        /** 通知单号 **/
        private Long notifyId;

    }

    @Override
    public String getMQName() {
        return MQ_NAME;
    }

    /**  【!重要配置项!】 **/
    @Override
    public MQSendTypeEnum getMQType(){
        return MQSendTypeEnum.QUEUE;  // QUEUE - 点对点 、 BROADCAST - 广播模式
    }

    @Override
    public String toMessage() {
        return JSONObject.toJSONString(payload);
    }

    /**  【!重要配置项!】 构造MQModel , 一般用于发送MQ时 **/
    public static PayOrderMchNotifyMQ build(Long notifyId){
        return new PayOrderMchNotifyMQ(new MsgPayload(notifyId));
    }

    /** 解析MQ消息, 一般用于接收MQ消息时 **/
    public static MsgPayload parse(String msg){
        return JSON.parseObject(msg, MsgPayload.class);
    }

    /** 定义 IMQReceiver 接口: 项目实现该接口则可接收到对应的业务消息  **/
    public interface IMQReceiver{
        void receive(MsgPayload payload);
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值