本文针对特定业务,以一种灵活的方式来设计代码结构,使得代码具有较好的可扩展性和可读性。主要实现的是消息队列的功能,业务类为一个常驻线程,每隔一段时间去数据库(可以认为是队列)中获取可以读取的文件,然后进行相应处理。
业务场景:业务方不定时会产生很多文件,这些文件需要我们处理,文件的路径会通过shell推送到数据库中,业务代码会一直监听数据库中是否有可以读取的文件,如果有则读取文件,并处理每一行。
import java.util.concurrent.Callable;
public interface CallableTask extends Callable<String> {
String readAndSendData2ws(String content);
}
public abstract class AbstractDataTask implements CallableTask {
protected String filePath;
public AbstractDataTask(String path) {
this.filePath = path;
}
@Override
public String call() throws Exception {
return readAndSendData2ws(filePath);
}
@Override
public String readAndSendData2ws(String filePath) {
try {
File file = new File(filePath);
List lines = new ArrayList(1);
if (file != null && file.exists()) {
lines = Files.readLines(file, Charsets.UTF_8);
file.delete();
}
for (String line : lines) {
sendData2sw(line);
}
} catch (Exception e) {
e.printStackTrace();
return filePath;
}
return filePath;
}
public abstract void sendData2sw(String content);
}
public class UploadDataTask extends AbstractDataTask {
private UploadService uploadService;
public UploadDataTask(String path, UploadService uploadService) {
super(path);
this.uploadService = uploadService;
}
@Override
public void sendData2sw(String line) {
if (StringUtil.isNotBlank(line)) {
final String[] k_v = line.split("\\s+");
if (k_v.length < 2) {
return;
}
ThreadpoolProxy.executeTaskByWork(new Runnable() {
@Override
public void run() {
String key = k_v[0];
String value = k_v[1];
uploadService.sendData2TargetPlace(key, value);
}
});
}
}
}
public class ThreadpoolProxy {
// 阻塞队列
private static final BlockingQueue WORKER_BLOCKINGQUEUE = new ArrayBlockingQueue(50);
// 线程工厂
private static final ThreadFactory FACTORY =
new ThreadFactoryBuilder().setNameFormat("thread-%d").setDaemon(true).build();
// 核心线程池
private static final int CORE_WORKER_POOL_SIZE = 8;
// 最大线程池
private static final int MAX_WORKER_POOL_SIZE = 8;
// 线程保持时间
private static final int KEEP_ALIVE_TIME = 300;
// 工作线程池负责处理请求
private static final ExecutorService WORKER_POOL =
new ThreadPoolExecutor(CORE_WORKER_POOL_SIZE, MAX_WORKER_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS,
WORKER_BLOCKINGQUEUE,
FACTORY, new RejectedExecutionHandlerImpl());
// 阻塞队列
private static final BlockingQueue BOSS_BLOCKINGQUEUE = new ArrayBlockingQueue(1);
// 核心线程池
private static final int CORE_BOSS_POOL_SIZE = 1;
// 最大线程池
private static final int MAX_BOSS_POOL_SIZE = 1;
// boss线程池
private static final ExecutorService BOSS_POOL =
new ThreadPoolExecutor(CORE_BOSS_POOL_SIZE, MAX_BOSS_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS,
BOSS_BLOCKINGQUEUE,
FACTORY, new RejectedExecutionHandlerImpl());
// 监听boss线程池
private static final ListeningExecutorService LISTEN_BOSS_POOL =
MoreExecutors.listeningDecorator(BOSS_POOL);
public static void executeTaskByWork(Runnable task) {
WORKER_POOL.submit(task);
}
public static ListenableFuture executeTaskByBoss(Callable task) {
return LISTEN_BOSS_POOL.submit(task);
}
}
public interface DataExecutor {
// 默认休眠时间10s
static final int DEFAULT_SLEEP_SECOND = 10;
// 默认最大尝试次数 5次
static final int DEFAULT_MAX_TRYTIMES = 5;
// 默认超过最大尝试次数的休眠时间 10min
static final int DEFAULT_TRYTIME_OVERSLEEP = 1000 * 60 * 10;
// 最大阻塞时间
static final int MAX_BLOCKING_TIME = 1000 * 60 * 60;
}
@Component
public abstract class AbstractDataExecutor implements DataExecutor {
@Autowired
protected UploadMgr uploadMgr;
@Autowired
protected UploadService uploadService;
protected String getAndSend2TargetPlace(Callable task) {
ListenableFuture executeTaskByBoss = ThreadpoolProxy.executeTaskByBoss(task);
try {
long st = System.currentTimeMillis();
while (true) {
long timeWaite = System.currentTimeMillis() - st;
if (timeWaite >= MAX_BLOCKING_TIME) {
return null;
}
if (executeTaskByBoss.isDone()) {
return executeTaskByBoss.get();
}
}
} catch (Exception e) {
return null;
}
}
protected String getAndSetFileStatus2Used(int usedStatus, int unUsedStatus, int sleepSeconds, int maxTryTimes,
int tryTimeOverSleep) {
String path = null;
File file = null;
int tryTimes = 1;
while ((path = uploadMgr.getAndSetFileStatus2Used(usedStatus, unUsedStatus)) == null
|| ((file = new File(path)) == null) || !file.exists()) {
try {
Thread.sleep(1000 * (new Random().nextInt(10) + sleepSeconds));
if (tryTimes++ >= maxTryTimes) {
Thread.sleep(tryTimeOverSleep);
return null;
}
} catch (InterruptedException e) {
return null;
}
}
return path;
}
protected String getAndSetFileStatus2Used(int usedStatus, int unUsedStatus, int sleepSeconds, int maxTryTimes) {
return getAndSetFileStatus2Used(usedStatus, unUsedStatus, sleepSeconds, maxTryTimes, DEFAULT_TRYTIME_OVERSLEEP);
}
protected String getAndSetFileStatus2Used(int usedStatus, int unUsedStatus, int sleepSeconds) {
return getAndSetFileStatus2Used(usedStatus, unUsedStatus, sleepSeconds, DEFAULT_MAX_TRYTIMES);
}
protected String getAndSetFileStatus2Used(int usedStatus, int unUsedStatus) {
return getAndSetFileStatus2Used(usedStatus, unUsedStatus, DEFAULT_SLEEP_SECOND);
}
public abstract void startUp();
}
@Component
public class MrDataExecutor extends AbstractDataExecutor {
private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
private volatile AtomicBoolean hasFileExecute = new AtomicBoolean(true);
@PostConstruct
public void getAndSendStart() {
startUp();
}
@Override
public void startUp() {
EXECUTOR_SERVICE.submit(new Runnable() {
@Override
public void run() {
hasFileExecute.set(true);
while (true) {
if (hasFileExecute.get()) {
try {
String filePath = getFilePath();
if (StringUtils.isNotEmpty(filePath)) {
hasFileExecute.set(false);
String result = getExecuteResult(filePath);
}
} catch (Exception e) {
// TODO
} finally {
hasFileExecute.set(true);
}
}
}
}
});
}
private String getExecuteResult(String filePath) {
return getAndSend2TargetPlace(new UploadDataTask (filePath, uploadService));
}
private String getFilePath() {
return getAndSetFileStatus2Used(0, 1);
}
@Override
public String getAndSend2TargetPlace(Callable task) {
return super.getAndSend2TargetPlace(task);
}
@Override
protected synchronized String getAndSetFileStatus2Used(int usedStatus, int unUsedStatus) {
return super.getAndSetFileStatus2Used(usedStatus, unUsedStatus);
}
}
本文主要阐述了针对特定业务场景下的代码架构设计,业务不具备通用性,但是设计思想具有通用性,针对不同业务场景设计灵活、可扩展性的业务框架是工程师提高自我能力的方式之一。这也需要在日常工作中不断总结和学习。最后希望本文对你有所帮助。