java常用代码架构示例

1、前言

本文针对特定业务,以一种灵活的方式来设计代码结构,使得代码具有较好的可扩展性和可读性。主要实现的是消息队列的功能,业务类为一个常驻线程,每隔一段时间去数据库(可以认为是队列)中获取可以读取的文件,然后进行相应处理。

业务场景:业务方不定时会产生很多文件,这些文件需要我们处理,文件的路径会通过shell推送到数据库中,业务代码会一直监听数据库中是否有可以读取的文件,如果有则读取文件,并处理每一行。


2、Service层

a、 接口

import java.util.concurrent.Callable;

public interface CallableTask extends Callable<String> {

    String readAndSendData2ws(String content);
}

b、抽象类

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);

}

c、具体实现类

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);
                }
            });
        }
    }

}

3、工具类

a、线程池代理类

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);
    }
}

4、业务层

a、业务常量

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;

}

b、业务抽象类


@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();
}

c、具体业务


@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);
    }

}

5、总结

本文主要阐述了针对特定业务场景下的代码架构设计,业务不具备通用性,但是设计思想具有通用性,针对不同业务场景设计灵活、可扩展性的业务框架是工程师提高自我能力的方式之一。这也需要在日常工作中不断总结和学习。最后希望本文对你有所帮助。

你可能感兴趣的:(数据处理,架构设计)