All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yoloho.schedule.extension.task.AbstractRedisQueueTask Maven / Gradle / Ivy

The newest version!
package com.yoloho.schedule.extension.task;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.yoloho.enhanced.common.annotation.NonNull;
import com.yoloho.enhanced.data.cache.lock.DistributedLock;
import com.yoloho.enhanced.data.cache.redis.api.RedisService;

/**
 * 任务的抽象类,提供以redis为队列的简单抽象
* 这里因为需要适配两个redisService(一个是uic自己写的redisService接口类),所以要多一点处理逻辑
* 另外为了使用便利些,暂时适配的是redisService而不是redisTemplate
* 另外为了实现时防止业务方癔想出来的一些坑,这里对于模板方法的调用都是直接调用而没有缓存其值,所以需要尽量保持模板方法的高效 * * @author jason * */ public abstract class AbstractRedisQueueTask { private final Logger logger = LoggerFactory.getLogger(getClass().getSimpleName()); /** * 上次获取重试数据的毫秒时间戳 */ private long lastRetry = 0; private volatile DistributedLock lock = null; /** * 绑定的redis * * @return */ protected abstract RedisService getRedisService(); /** * 队列名 * * @return */ protected abstract String getQueueName(); @PostConstruct public void init() { Preconditions.checkNotNull(getRedisService(), "getRedisService() not implemented a none null"); } /** * 重试队列名,默认是主队列后加尾巴"_retry" * * @return */ protected String getRetryQueueName() { return getQueueName() + "_retry"; } /** * 将redis里每个元素做类型解析 * * @param item * @return null for fail or invalid */ protected abstract T convertType(String item); /** * 将元素转换回redis里的单条,比如转换成json * * @param item * @return */ protected abstract String convertTypeRevert(T item); /** * 是否启用retry功能,不需要的可以覆盖本方法禁用 * * @return */ protected boolean enableRetry() { return true; } /** * 稍后重试某item
* 注意,如果需要设置最大重试次数,这个目前需要具体逻辑自行实现(因为无法判定唯一识别方式) * * @param item */ protected void retry(T item) { String val = convertTypeRevert(item); if (StringUtils.isEmpty(val)) { return; } getRedisService().listPush(getRetryQueueName(), val); logger.info("任务数据回重试队列:{}", val); } private void initLock() { if (lock != null) { return; } synchronized (this) { if (lock != null) { return; } lock = new DistributedLock<>(getRedisService(), "AbstractRedisQueueTask", 60); } } /** * @param queueName * @param count * @return */ @NonNull private List getDataList(String queueName, int count) { initLock(); String lockKey = getQueueName(); List result = new ArrayList<>(10); try { lock.lock(lockKey, 1, TimeUnit.SECONDS); try { List list = getRedisService().listRange(queueName, 0, count); if (list != null) { getRedisService().listTrim(queueName, list.size(), -1); result.addAll(list); } } finally { lock.unlock(lockKey); } } catch (TimeoutException | InterruptedException e) { logger.info("遇到并发未获得锁,跳过本次取数步骤"); } return result; } protected List getTaskList(int capacity) { List list = getDataList(getQueueName(), capacity); if (enableRetry()) { long now = System.currentTimeMillis(); // 这里为了避免外部服务或逻辑出问题时,非常频繁地取重试队列再塞回去的性能问题,所以规定了一个每10秒取一次的限制 // 带来的另外一个问题,就是这样的机制并不针对特别大量的需要重试的场景,要注意 if (now - lastRetry > 10000) { lastRetry = now; List retryList = getDataList(getRetryQueueName(), capacity); if (retryList.size() > 0) { list.addAll(retryList); } } } if (list.isEmpty()) { return Collections.emptyList(); } List result = new ArrayList<>(list.size()); for (String str : list) { if (StringUtils.isEmpty(str)) { continue; } try { T item = convertType(str); if (item != null) { result.add(item); } } catch (Exception e) { logger.warn("类型转换失败", e); } } logger.info("获取到了 {} 条待处理数据", result.size()); return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy