创建一个定时任务,每一次执行完后间隔一定时间就会扫描缓存,缓存中一旦添加了任务,就会被扫描到,然后发送到消息队列,监听器一旦监听到消息就会进行处理,如果处理失败,则再次生成任务(次数加1,时间戳会根据规则增加),到达规定次数后则不在执行
首先创建一个执行完后间隔’${webhook.fixedDelay}’后执行下一次,从缓存中取出数据,一旦取到数据就发送到消息队列中,并且删除掉缓存里面的数据
@Scheduled(fixedDelayString = "${webhook.fixedDelay}")
public void process() {
// ZRANGEBYSCORE 取score小于等于当前时间的数据
Date date = new Date();
Set<String> list = stringRedisTemplate.opsForZSet().rangeByScore(key, 0, date.getTime());
if (list == null || list.size() == 0) {
return;
}
try {
// 发送队列
for (String json : list) {
amqpTemplate.convertAndSend(routingKey, json);
}
// ZREMRANGEBYSCORE() 删除score小于等于当前时间的数据
stringRedisTemplate.opsForZSet().removeRangeByScore(key, 0, date.getTime());
} catch (Exception e) {
// TODO: handle exception
logger.error("队列发送失败", e);
}
}
监听器一旦监听到消息就会进行处理,如果处理失败,则再次生成任务(次数加1,时间戳会根据规则增加),到达规定次数后则不在执行
@RabbitListener(queues = "${webhook.queue}")
// 参数中使用@Header获取mesage
public void helloReply(String json) {
logger.debug("task:{}", json);
Task task = gson.fromJson(json, Task.class);
try {
handler.handler(task.getData());
} catch (Exception e) {
if (webhookUtil.getInterval().size() > task.getTimes()) {
Task next = new Task();
next.setTimes(task.getTimes() + 1);
next.setTimestamp(task.getTimestamp() + webhookUtil.getInterval().get(task.getTimes())*1000);
next.setData(task.getData());
webhookUtil.addNext(next);
if (logger.isDebugEnabled()) {
logger.debug("处理失败,添加到下次执行:" + gson.toJson(task), e);
}
} else {
logger.error("处理{}次失败,停止处理", webhookUtil.getInterval().size(), e);
}
}
}
向缓存中添加一条数据,定时任务会到时间自动检测到这条数据
public boolean add(String data) {
Task task = new Task();
task.setData("data");
task.setTimes(0);
task.setTimestamp(new Date().getTime() + getInterval().get(task.getTimes())*1000);
try {
stringRedisTemplate.opsForZSet().add(key, gson.toJson(task), task.getTimestamp());
return true;
} catch (Exception e) {
logger.error("redis新增失败", e);
return false;
}
}
spring-mq.xml
<rabbit:connection-factory id="connectionFactory"
addresses="${rabbitmq.addresses}" username="${rabbitmq.username}"
password="${rabbitmq.password}" channel-cache-size="${rabbitmq.channel.cache.size}" />
<rabbit:admin connection-factory="connectionFactory" />
<rabbit:queue name="${webhook.queue}" />
<rabbit:direct-exchange name="${rabbitmq.direct.exchange}">
<rabbit:bindings>
<rabbit:binding queue="${webhook.queue}" key="${webhook.routing.key}" />
rabbit:bindings>
rabbit:direct-exchange>
<rabbit:annotation-driven />
<bean id="rabbitListenerContainerFactory"
class="org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory">
<property name="messageConverter" ref="messageConverter" />
<property name="connectionFactory" ref="connectionFactory" />
<property name="concurrentConsumers" value="3" />
<property name="maxConcurrentConsumers" value="10" />
bean>
<bean id="messageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter" />
<rabbit:template id="amqpTemplate" message-converter="messageConverter"
connection-factory="connectionFactory" reply-timeout="2000" retry-template="retryTemplate"
exchange="${rabbitmq.direct.exchange}" />
<bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
<property name="initialInterval" value="500" />
<property name="multiplier" value="10.0" />
<property name="maxInterval" value="10000" />
bean>
property>
bean>
webhook.properties
#数据存入缓存中的名字
webhook.key=eisp_webhook_key
#消息队列的key值
webhook.routing.key=eisp_webhook_routing_key
#消息队列的队列名
webhook.queue=webhook_queue
#下次请求的时间间隔(单位:秒)
webhook.interval=5, 20,30
#定时器每次执行完后的间隔时间(单位:毫秒)
webhook.fixedDelay=1000
其中’webhook.interval=5, 20,30’是以数组的方式注入
@Value("#{'${webhook.interval}'.split(',')}")
private List<Integer> interval;