import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Created by Sequarius on 2016/9/7.
*/
@Slf4j
public abstract class BaseRetry<E, T> {
@Resource
private ExpiresMessageController expiresMessageController;
@Resource
private RedisTemplate redisTemplate;
@Resource
private StringRedisTemplate stringRedisTemplate;
public Class<E> clazz;
// 重试尝试间隔为1s,10s,1min,10min,1h,3h,6h一次递增
private final List<Long> RETRY_TABLE = new ArrayList<Long>() {
{
add(0L);
add(1L);
add(10L);
add(60L);
add(10 * 60L);
add(60 * 60L);
add(3 * 60 * 60L);
add(6 * 60 * 60L);
}
};
public BaseRetry(Class<E> clazz) {
this.clazz = clazz;
}
public void retry(E entity, T tag) {
RetryEntity retryEntity = new RetryEntity(entity, tag).invoke();
String timeKey = retryEntity.getTimeKey();
String entryKey = retryEntity.getEntryKey();
StringBuffer entityTag = retryEntity.getEntityTag();
String memoryValue = stringRedisTemplate.opsForValue().get(timeKey);
int lastRetryTime = (memoryValue == null) ? 0 : Integer.valueOf(memoryValue);
//first Retry save entity
if (lastRetryTime == 0) {
redisTemplate.setDefaultSerializer(new FastJsonSerializer<>(clazz));
redisTemplate.opsForValue().set(entryKey, entity);
}
//max Retry delete entity
if (lastRetryTime >= RETRY_TABLE.size() - 1) {
log.warn("retry in max times entityTag=={}", entityTag.toString());
log.warn("retry entity=={}",stringRedisTemplate.opsForValue().get(entryKey));
redisTemplate.delete(entryKey);
stringRedisTemplate.delete(timeKey);
throw new RetryTimeOutOfRangeException();
}
log.debug("time key={};entrykey={};lastRetryTime={}", timeKey, entryKey, lastRetryTime);
//add 1 times
stringRedisTemplate.opsForValue().increment(timeKey, 1);
stringRedisTemplate.opsForValue().set(entityTag.insert(0, Constant.PREFIX_PAYMENT_KEY).toString(),
"notify_key", RETRY_TABLE.get(lastRetryTime + 1), TimeUnit.SECONDS);
}
//
protected void addListenner(Class<?> clazz, SubMessageListener listener) {
expiresMessageController.addSubMessageListener(clazz, listener);
}
public abstract void setMessageListener();
public void retrySuccess(E entity, T tag){
RetryEntity retryEntity = new RetryEntity(entity, tag).invoke();
redisTemplate.delete(retryEntity.getTimeKey());
redisTemplate.delete(retryEntity.getEntryKey());
}
private class RetryEntity {
private E entity;
private T tag;
private StringBuffer entityTag;
private String timeKey;
private String entryKey;
public RetryEntity(E entity, T tag) {
this.entity = entity;
this.tag = tag;
}
public StringBuffer getEntityTag() {
return entityTag;
}
public String getTimeKey() {
return timeKey;
}
public String getEntryKey() {
return entryKey;
}
public RetryEntity invoke() {
entityTag = new StringBuffer(entity.getClass().getName())
.append(":").append(tag.toString());
//retry times key
timeKey = new StringBuffer(Constant.PREFIX_PAYMENT_TIME_KEY).append(entityTag).toString();
//retry entity key
entryKey = new StringBuffer(Constant.PREFIX_PAYMENT_ENTITY_KEY).append(entityTag).toString();
return this;
}
}
}