8 用redis解决重复消费(幂等问题)


在发送消息之后,并没有接到消息确认。他就会再次发送。则同一个消息他发送了两次,也会重复消费两次

或者 消费重试也可能造成幂等性问题

1 生产者

1.1 修改controller层

在这里插入图片描述

1.2 修改补偿机制的代码

在这里插入图片描述

package fastwave.cloud.demo.fastwavebizpublisher.job;

import fastwave.cloud.demo.fastwavebizpublisher.domain.MsgLogDO;
import fastwave.cloud.demo.fastwavebizpublisher.services.MsgLogService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
@EnableScheduling
public class MQMessageJob {
    @Autowired
    private MsgLogService msgLogService;

    @Resource(name = "TemplateReliable")
    private RabbitTemplate reliableTemplate;

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

    //定时扫描记录表,将发送状态为-1且未超重发次数的消息再次发送,超过重发次数,必要时人工干预,生产环境中可以单独部署定时任务
    @Scheduled(cron ="10/10 * * * * ?" )
    public void scanNoConfirmMsg(){
        Map<String, Object> searchParams = new HashMap<String, Object>();
        searchParams.put("status", -1);
        searchParams.put("tryCount", 3);

        try {
            List<MsgLogDO> list = msgLogService.list(searchParams);
            for(MsgLogDO item : list)
            {
                item.setTryCount(item.getTryCount() + 1);
                msgLogService.update(item);

                Message message = MessageBuilder.withBody(item.getMsg().getBytes()).setMessageId(item.getMsgId()).build();

                reliableTemplate.convertAndSend(item.getExchange(), item.getRoutingKey(), message, new CorrelationData(item.getMsgId()));
            }
        }
        catch(Exception e)
        {
            logger.error("扫描作业出错,信息:" + e.getMessage());
        }
    }
}

2 消费者

2.1 引入redis的依赖和配置

2.2 引入redis工具类

package fastwave.cloud.demo.fastwavebizsubscriber.common;

import java.io.Serializable;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;


@Component
public class CacheUtil {
	@Autowired
	private RedisTemplate redisTemplate;

	/**
	 * @Description: 批量删除缓存
	 * @Author: hj
	 * @Date: 17:13 2017/10/24
	 */
	public void remove(final String... keys) {
		for (String key : keys) {
			remove(key);
		}
	}

	/**
     * @Description: 删除缓存
     * @Author: hj
     * @Date: 16:51 2017/10/24
     */
    public void remove(final String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }

	/**
     * @Description: 判断缓存中是否有对应的value
     * @Author: hj
     * @Date: 16:50 2017/10/24
     */
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }

	/**
     * @Description: 读取缓存
     * @Author: hj
     * @Date: 16:49 2017/10/24
     */
    public Object get(final String key) {
        return redisTemplate.opsForValue().get(key);
    }

	/**
     * @Description: 写入缓存
     * @Author: hj
     * @Date: 16:48 2017/10/24
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            redisTemplate.opsForValue().set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

	/**
     * @Description: 写入缓存(可以配置过期时间)
     * @Author: hj
     * @Date: 16:46 2017/10/24
     */
    public boolean set(final String key, Object value, Long expireTime) {
        boolean result = false;
        try {
            redisTemplate.opsForValue().set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

2.3用redis解决重复消费

package fastwave.cloud.demo.fastwavebizsubscriber.services;

import com.alibaba.fastjson.JSONObject;
import fastwave.cloud.demo.fastwavebizsubscriber.common.CacheUtil;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class EmailReceiver {
    @Autowired
    CacheUtil cacheUtil;

//    @RabbitListener(queues = "EmailQueue")
//    public void receiver(String msg) throws Exception {
//
//
//        System.out.println("收到的消息是:" + msg);
////        throw new Exception("消费消息出现异常");
//    }

    @RabbitListener(queues = "EmailQueue")
    public void receiver(Message message) throws Exception {
        String messageId = message.getMessageProperties().getMessageId();
        if(messageId != null && !cacheUtil.exists(messageId))
        {
            String msg = new String(message.getBody());
            Map<String, Object>  map = JSONObject.parseObject(msg, Map.class);
            System.out.println("开始开送邮件:" + msg);
            cacheUtil.set(messageId, true);
        }else
        {
            System.out.println("已经消费过了");
        }
        throw new Exception("消费消息出现异常");
    }

}

发布了142 篇原创文章 · 获赞 3 · 访问量 5366

猜你喜欢

转载自blog.csdn.net/Insist___/article/details/105301632