写一个项目去并发处理DB任务
1. 应用的场景
- 某公司互动娱乐事业部需要把他们的DB记录进行处理,经过一些列轮转最终反馈给客户
2. 业务特色
- 自动扫描DB的任务
- 需要优雅的关闭我们处理DB的任务
- 高QPS,需要异步处理
- 批次处理任务需要同时完成了才能进行下一步
3. 代码实战
@Data
public class Message {
private String orderId;
}
@Slf4j
public abstract class TemplateTask<T> {
private int poolSize = 5;
private int splitSize = 5;
protected volatile boolean terminal = false;
public void setPoolSize(int poolSize) {
this.poolSize = Math.min(10, poolSize);
}
public void setSplitSize(int splitSize) {
this.splitSize = Math.min(10, splitSize);
}
public abstract List<T> queryData();
public abstract void execute(T data);
public abstract int inter();
public abstract String serviceName();
public void doExecute() {
int i = 0;
try {
while (true) {
log.info("{} 任务第 {} 次执行",serviceName(),i);
List<T> datas = queryData();
taskExecute(datas);
log.info("{}任务第{}次执行结束",serviceName(),i);
if (terminal) {
break;
}
if (inter() > 0){
Thread.sleep(inter());
}
i++;
}
}catch (Exception e){
throw new RuntimeException(e);
}finally {
TaskProcessUtil.releaseExecutors(serviceName());
}
}
public void terminal() {
terminal = true;
log.info("{} shut down",serviceName());
}
private void doProcessData(List<T> datas, CountDownLatch latch) {
try {
for (T cat : datas) {
execute(cat);
}
} catch (Exception e) {
log.error(Arrays.toString(e.getStackTrace()));
} finally {
if (latch != null) {
latch.countDown();
}
}
}
private void taskExecute(List<T> data) {
if (CollectionUtils.isEmpty(data)) {
return;
}
splitSize = Math.min(this.splitSize, data.size());
List<List<T>> splitData = Lists.partition(data, splitSize);
final CountDownLatch latch = new CountDownLatch(splitData.size());
for (final List<T> datas : splitData) {
ExecutorService executorService = TaskProcessUtil.getOrInitExecutors(serviceName(), poolSize);
executorService.submit(() -> { doProcessData(datas, latch); });
}
try {
latch.await();
} catch (Exception e) {
log.error(Arrays.toString(e.getStackTrace()));
}
}
}
@Component
public class ConcreteTask extends TemplateTask<Message> {
@Override
public List<Message> queryData() {
Message message = new Message();
message.setOrderId("0001");
List<Message> messages = Arrays.asList(message);
return messages;
}
@Override
public void execute(Message data) {
System.out.println("每条信息的处理"+data.getOrderId());
}
@Override
public int inter() {
return 1000;
}
@Override
public String serviceName() {
return "ConcreteTask1";
}
}
public class TaskProcessUtil {
private static Map<String, ExecutorService> executorMap = new ConcurrentHashMap<>();
private static ExecutorService init(String poolName, int poolSize) {
return new ThreadPoolExecutor(poolSize, poolSize,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
new ThreadFactoryBuilder().setNameFormat("Pool-" + poolName).setDaemon(false).build(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
public static ExecutorService getOrInitExecutors(String poolName,int poolSize) {
ExecutorService executorService = executorMap.get(poolName);
if (null == executorService) {
synchronized (TaskProcessUtil.class) {
executorService = executorMap.get(poolName);
if (null == executorService) {
executorService = init(poolName, poolSize);
executorMap.put(poolName, executorService);
}
}
}
return executorService;
}
public static void releaseExecutors(String poolName) {
ExecutorService executorService = executorMap.remove(poolName);
if (executorService != null) {
executorService.shutdown();
}
}
}
@Component
public class TaskManager implements InitializingBean {
@Autowired
List<TemplateTask> templateList;
public void initTasks() {
for (TemplateTask childTask : templateList) {
ExecutorService commonTaskExecutor = TaskProcessUtil.getOrInitExecutors("COMMON_TASK", 10);
commonTaskExecutor.submit(childTask::doExecute);
}
}
public void shutdownTasks() {
if (!CollectionUtils.isEmpty(templateList)) {
for (TemplateTask childTask : templateList) {
childTask.terminal();
}
}
}
public void shutdownTaskByName(String serviceName) {
Optional<TemplateTask> first = templateList.stream().filter(service -> service.serviceName().equals(serviceName)).findFirst();
first.ifPresent(TemplateTask::terminal);
}
@Override
public void afterPropertiesSet() throws Exception {
initTasks();
}
}