在发送消息之后,并没有接到消息确认。他就会再次发送。则同一个消息他发送了两次,也会重复消费两次
或者 消费重试也可能造成幂等性问题
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("消费消息出现异常");
}
}