1. 每行读取csv文件(不再一次性读取)

2. 读取后的内容做batch(为后续调用批量接口服务)--实现生产者与消费者
pull/4/head
3y 3 years ago
parent 7b7bd8483a
commit d3e6265270

@ -0,0 +1,48 @@
package com.java3y.austin.cron.config;
import com.google.common.base.Throwables;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 线@Async
*
* @author 3y
*/
@Slf4j
@Configuration
@EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {
@Bean("austinExecutor")
public ThreadPoolTaskExecutor executor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(30);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setThreadNamePrefix("austinAsyncExecutor-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(10);
executor.initialize();
return executor;
}
@Override
public Executor getAsyncExecutor() {
return executor();
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, params) -> log.error("austinExecutor execute fail!method:{},params:{},ex:{}", method, params, Throwables.getStackTraceAsString(ex));
}
}

@ -0,0 +1,31 @@
package com.java3y.austin.cron.constants;
/**
* @author 3y
* @date 2022/2/13
* pending
*/
public class PendingConstant {
/**
*
*/
public static final Integer QUEUE_SIZE = 100;
/**
*
*/
public static final Integer NUM_THRESHOLD = 100;
/**
* batch
*/
public static final Long TIME_THRESHOLD = 1000L;
/**
* 线
*/
public static final Integer THREAD_NUM = 2;
}

@ -24,7 +24,7 @@ public class CrowdInfoVo implements Serializable {
/** /**
* id * id
*/ */
private String id; private String receiver;
/** /**
* *

@ -24,9 +24,8 @@ public class CronTaskHandler {
*/ */
@XxlJob("austinJob") @XxlJob("austinJob")
public void execute() { public void execute() {
log.info("XXL-JOB, Hello World."); log.info("CronTaskHandler#execute messageTemplateId:{} cron exec!", XxlJobHelper.getJobParam());
Long messageTemplateId = Long.valueOf(XxlJobHelper.getJobParam()); Long messageTemplateId = Long.valueOf(XxlJobHelper.getJobParam());
taskHandler.handle(messageTemplateId); taskHandler.handle(messageTemplateId);
} }

@ -0,0 +1,39 @@
package com.java3y.austin.cron.pending;
import com.java3y.austin.cron.domain.CrowdInfoVo;
import com.java3y.austin.support.pending.BatchPendingThread;
import com.java3y.austin.support.pending.Pending;
import com.java3y.austin.support.pending.PendingParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.List;
/**
*
*
* @author 3y
*/
@Component
@Slf4j
public class CrowdBatchTaskPending extends Pending<CrowdInfoVo> {
@Override
public void initAndStart(PendingParam pendingParam) {
threadNum = pendingParam.getThreadNum() == null ? threadNum : pendingParam.getThreadNum();
queue = pendingParam.getQueue();
for (int i = 0; i < threadNum; ++i) {
BatchPendingThread<CrowdInfoVo> batchPendingThread = new BatchPendingThread();
batchPendingThread.setPendingParam(pendingParam);
batchPendingThread.setName("batchPendingThread-" + i);
batchPendingThread.start();
}
}
@Override
public void doHandle(List<CrowdInfoVo> list) {
log.info("theadName:{},doHandle:{}", Thread.currentThread().getName(), list.size());
}
}

@ -2,18 +2,21 @@ package com.java3y.austin.cron.service.impl;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON; import com.java3y.austin.cron.constants.PendingConstant;
import com.java3y.austin.cron.domain.CrowdInfoVo; import com.java3y.austin.cron.domain.CrowdInfoVo;
import com.java3y.austin.cron.pending.CrowdBatchTaskPending;
import com.java3y.austin.cron.service.TaskHandler; import com.java3y.austin.cron.service.TaskHandler;
import com.java3y.austin.cron.utils.ReadFileUtils; import com.java3y.austin.cron.utils.ReadFileUtils;
import com.java3y.austin.support.dao.MessageTemplateDao; import com.java3y.austin.support.dao.MessageTemplateDao;
import com.java3y.austin.support.domain.MessageTemplate; import com.java3y.austin.support.domain.MessageTemplate;
import com.java3y.austin.support.pending.PendingParam;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.HashMap;
import java.util.concurrent.LinkedBlockingQueue;
/** /**
* @author 3y * @author 3y
@ -24,23 +27,42 @@ import java.util.List;
public class TaskHandlerImpl implements TaskHandler { public class TaskHandlerImpl implements TaskHandler {
@Autowired @Autowired
private MessageTemplateDao messageTemplateDao; private MessageTemplateDao messageTemplateDao;
@Autowired
private CrowdBatchTaskPending crowdBatchTaskPending;
@Override @Override
@Async @Async
public void handle(Long messageTemplateId) { public void handle(Long messageTemplateId) {
log.info("start:{}", Thread.currentThread().getName());
MessageTemplate messageTemplate = messageTemplateDao.findById(messageTemplateId).get(); MessageTemplate messageTemplate = messageTemplateDao.findById(messageTemplateId).get();
if (messageTemplate == null || StrUtil.isBlank(messageTemplate.getCronCrowdPath())) { if (messageTemplate == null || StrUtil.isBlank(messageTemplate.getCronCrowdPath())) {
log.error("TaskHandler#handle crowdPath empty!"); log.error("TaskHandler#handle crowdPath empty! messageTemplateId:{}", messageTemplateId);
return; return;
} }
List<CrowdInfoVo> csvRowList = ReadFileUtils.getCsvRowList(messageTemplate.getCronCrowdPath());
if (CollUtil.isNotEmpty(csvRowList)) { // 初始化pending的信息
PendingParam<CrowdInfoVo> pendingParam = new PendingParam<>();
pendingParam.setNumThreshold(PendingConstant.NUM_THRESHOLD)
.setQueue(new LinkedBlockingQueue(PendingConstant.QUEUE_SIZE))
.setTimeThreshold(PendingConstant.TIME_THRESHOLD)
.setThreadNum(PendingConstant.THREAD_NUM)
.setPending(crowdBatchTaskPending);
crowdBatchTaskPending.initAndStart(pendingParam);
} // 读取文件得到每一行记录给到队列做batch处理
ReadFileUtils.getCsvRow(messageTemplate.getCronCrowdPath(), row -> {
if (CollUtil.isEmpty(row.getFieldMap())
|| StrUtil.isBlank(row.getFieldMap().get(ReadFileUtils.RECEIVER_KEY))) {
return;
}
HashMap<String, String> params = ReadFileUtils.getParamFromLine(row.getFieldMap());
CrowdInfoVo crowdInfoVo = CrowdInfoVo.builder().receiver(row.getFieldMap().get(ReadFileUtils.RECEIVER_KEY))
.params(params).build();
crowdBatchTaskPending.pending(crowdInfoVo);
});
log.info("csv info:", JSON.toJSONString(csvRowList));
} }
} }

@ -2,14 +2,14 @@ package com.java3y.austin.cron.utils;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.csv.CsvData; import cn.hutool.core.text.csv.*;
import cn.hutool.core.text.csv.CsvRow;
import cn.hutool.core.text.csv.CsvUtil;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.java3y.austin.cron.domain.CrowdInfoVo; import com.java3y.austin.cron.domain.CrowdInfoVo;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.io.FileReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -23,7 +23,47 @@ import java.util.Map;
public class ReadFileUtils { public class ReadFileUtils {
/** /**
* csv * csv
*/
public static final String RECEIVER_KEY = "userId";
/**
* csv csvRowHandler
*
* @param path
* @param csvRowHandler
*/
public static void getCsvRow(String path, CsvRowHandler csvRowHandler) {
try {
// 把首行当做是标题获取reader
CsvReader reader = CsvUtil.getReader(new FileReader(path),
new CsvReadConfig().setContainsHeader(true));
reader.read(csvRowHandler);
} catch (Exception e) {
log.error("ReadFileUtils#getCsvRow fail!{}", Throwables.getStackTraceAsString(e));
}
}
/**
* params
* [{key:value},{key:value}]
* @param fieldMap
* @return
*/
public static HashMap<String, String> getParamFromLine(Map<String, String> fieldMap) {
HashMap<String, String> params = MapUtil.newHashMap();
for (Map.Entry<String, String> entry : fieldMap.entrySet()) {
if (!ReadFileUtils.RECEIVER_KEY.equals(entry.getKey())) {
params.put(entry.getKey(), entry.getValue());
}
}
return params;
}
/**
* csv
* 1. (id,paramsKey1,params2Key2)Id * 1. (id,paramsKey1,params2Key2)Id
* 2. * 2.
* 3. * 3.
@ -46,12 +86,14 @@ public class ReadFileUtils {
for (int j = 1; j < headerInfo.size(); j++) { for (int j = 1; j < headerInfo.size(); j++) {
param.put(headerInfo.get(j), row.get(j)); param.put(headerInfo.get(j), row.get(j));
} }
result.add(CrowdInfoVo.builder().id(row.get(0)).params(param).build()); result.add(CrowdInfoVo.builder().receiver(row.get(0)).params(param).build());
} }
} catch (Exception e) { } catch (Exception e) {
log.error("TaskHandler#getCsvRowList fail!{}", Throwables.getStackTraceAsString(e)); log.error("ReadFileUtils#getCsvRowList fail!{}", Throwables.getStackTraceAsString(e));
} }
return result; return result;
} }
} }

@ -44,7 +44,7 @@ public class Task implements Runnable {
return; return;
} }
// 1.平台通用去重 test // 1.平台通用去重
deduplicationRuleService.duplication(taskInfo); deduplicationRuleService.duplication(taskInfo);
// 2. 真正发送消息 // 2. 真正发送消息

@ -0,0 +1,64 @@
package com.java3y.austin.support.pending;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 线
*
*
* @author 3y
*/
@Data
@Accessors(chain = true)
@Slf4j
public class BatchPendingThread<T> extends Thread {
private PendingParam<T> pendingParam;
/**
*
*/
private List<T> tasks = new ArrayList<>();
/**
*
*/
private Integer total = 0;
/**
*
*/
private Long lastHandleTime = System.currentTimeMillis();
@Override
public void run() {
while (true) {
try {
T obj = pendingParam.getQueue().poll(pendingParam.getTimeThreshold(), TimeUnit.MILLISECONDS);
if (null != obj) {
tasks.add(obj);
}
// 处理条件1. 数量超限 2. 时间超限
if ((tasks.size() >= pendingParam.getNumThreshold())
|| (System.currentTimeMillis() - lastHandleTime >= pendingParam.getTimeThreshold())) {
List<T> taskRef = tasks;
tasks = Lists.newArrayList();
lastHandleTime = System.currentTimeMillis();
pendingParam.getPending().handle(taskRef);
}
} catch (Exception e) {
log.error("BatchPendingThread#run failed:{}", Throwables.getStackTraceAsString(e));
}
}
}
}

@ -0,0 +1,79 @@
package com.java3y.austin.support.pending;
import com.google.common.base.Throwables;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.concurrent.BlockingQueue;
/**
* -
*
* @author 3y
*/
@Slf4j
@Data
public abstract class Pending<T> {
/**
* 使线
*/
public static final int DEFAULT_THREAD_NUM = Runtime.getRuntime().availableProcessors();
/**
*
*/
protected BlockingQueue<T> queue;
/**
* 线 = 使线
*/
protected Integer threadNum = DEFAULT_THREAD_NUM;
/**
*
*
* @param t
*/
public void pending(T t) {
try {
queue.put(t);
} catch (InterruptedException e) {
log.error("Pending#pending error:{}", Throwables.getStackTraceAsString(e));
}
}
/**
*
*
* @param t
*/
public void handle(List<T> t) {
if (t.isEmpty()) {
return;
}
try {
doHandle(t);
} catch (Exception e) {
log.error("Pending#handle failed:{}", Throwables.getStackTraceAsString(e));
}
}
/**
*
*
* @param pendingParam
*/
public abstract void initAndStart(PendingParam pendingParam);
/**
*
*
* @param list
*/
public abstract void doHandle(List<T> list);
}

@ -0,0 +1,48 @@
package com.java3y.austin.support.pending;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.concurrent.BlockingQueue;
/**
* @author 3y
* pending
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Accessors(chain = true)
public class PendingParam<T> {
/**
*
*/
private BlockingQueue<T> queue;
/**
* batch
*/
private Integer numThreshold;
/**
* batch
*/
private Long timeThreshold;
/**
* pending
*/
private Pending pending;
/**
* 线
*/
protected Integer threadNum;
}
Loading…
Cancel
Save