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 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 map)
throws
JsonProcessingException {
System.out.println(
"一直报错,重试次数用完了,保存如下信息供人工干预:\n"
+ mapper.writeValueAsString(map));
}
/**
* 异常时最多重试 3次,成功为止
*
* @param map
* 输入参数
*/
private
void
retry(Map map) {
// 构建重试模板实例
RetryTemplate retryTemplate =
new
RetryTemplate();
// 设置重试次数
SimpleRetryPolicy policy =
new
SimpleRetryPolicy(
3
,
Collections.extends
Throwable>, Boolean>singletonMap(Exception.
class
,
true
));
// 设置重试间隔时间
FixedBackOffPolicy fixedBackOffPolicy =
new
FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(
100
);
retryTemplate.setRetryPolicy(policy);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
// 编写业务处理代码逻辑
final
RetryCallback retryCallback =
new
RetryCallback() {
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 recoveryCallback =
new
RecoveryCallback() {
public
Object recover(RetryContext context)
throws
Exception {
failed(map);
return
null
;
}
};
try
{
// 由retryTemplate 执行execute方法开始逻辑执行
retryTemplate.execute(retryCallback, recoveryCallback);
}
catch
(Exception e) {
e.printStackTrace();
}
}
}