| package cn.my.server.clientdemo.yuer; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitHandler; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.retry.RecoveryCallback; import org.springframework.retry.RetryCallback; import org.springframework.retry.RetryContext; import org.springframework.retry.backoff.FixedBackOffPolicy; import org.springframework.retry.policy.SimpleRetryPolicy; import org.springframework.retry.support.RetryTemplate; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.rabbitmq.client.Channel; /** * 余额宝端消息监听 */ @Component public class YuErBaoMessageListeners { private final Logger log = LoggerFactory.getLogger(this.getClass()); private ObjectMapper mapper = new ObjectMapper(); /** * 监听消息队列 * @param message 消息内容 * @param channel 消息渠道 * @throws IOException 异常 */ @RabbitListener(queues = "money") @RabbitHandler public void receiveQueue(Message message, Channel channel) throws IOException { String msg = ""; try { // 业务处理逻辑 msg = new String(message.getBody()); Map data = mapper.readValue(msg, HashMap.class); retry(data); channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);// 手动应答消息已经处理 } catch (Exception e) { log.error("MQ接收消息内容[" + msg + "],后处理异常:" + e); channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);// 手动应答消息已经处理 } } /** * 更新余额宝账户金额 * * @param map */ @Transactional public void bizOp(Map<String, Object> map) { // B_MESSAGE交易流水表,主键是【交易编号】,重复插入会报异常,类似于幂等操作 System.out.println( "【余额宝记账操作】重复插入会报异常,类似于幂等操作 insert into B_MESSAGE(transId,userId,money) values (000001,1,10000)"); // 更新余额宝账户金额 System.out.println("【余额宝账户入款】 update B set amount = amount +10000 where userId = 1"); } /** * 一直报错,重试次数用完了,保存如下信息供人工干预 * * @param map * @throws JsonProcessingException */ public void failed(Map<String, Object> map) throws JsonProcessingException { System.out.println("一直报错,重试次数用完了,保存如下信息供人工干预:\n" + mapper.writeValueAsString(map)); } /** * 异常时最多重试 3次,成功为止 * * @param map * 输入参数 */ private void retry(Map<String, Object> map) { // 构建重试模板实例 RetryTemplate retryTemplate = new RetryTemplate(); // 设置重试次数 SimpleRetryPolicy policy = new SimpleRetryPolicy(3, Collections.<Class<? extends Throwable>, Boolean>singletonMap(Exception.class, true)); // 设置重试间隔时间 FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); fixedBackOffPolicy.setBackOffPeriod(100); retryTemplate.setRetryPolicy(policy); retryTemplate.setBackOffPolicy(fixedBackOffPolicy); // 编写业务处理代码逻辑 final RetryCallback<Object, Exception> retryCallback = new RetryCallback<Object, Exception>() { public Object doWithRetry(RetryContext context) throws Exception { System.out.println("第" + (1 + context.getRetryCount()) + "次处理"); try { bizOp(map); } catch (Exception e) { e.printStackTrace(); throw new Exception("捕捉到业务处理异常,需要抛出");// 这个点特别注意,重试的根源通过Exception返回 } return null; } }; // 重试次数执行完依然报错,走如下逻辑 final RecoveryCallback<Object> recoveryCallback = new RecoveryCallback<Object>() { public Object recover(RetryContext context) throws Exception { failed(map); return null; } }; try { // 由retryTemplate 执行execute方法开始逻辑执行 retryTemplate.execute(retryCallback, recoveryCallback); } catch (Exception e) { e.printStackTrace(); } } } |