使用多线程或优先级队列确定特定API调用的优先级的方法



在我的应用程序中,我的servlet(运行在tomcat上)接受doPost请求,并将api调用的初始值返回给用户以表示,然后在后面使用更多其他api调用进行大量数据分析。然后数据分析进入我的mongodb。当我想在批量api调用完成之前启动该进程时,问题就出现了。电话太多了,我至少需要20秒。我不希望用户为初始数据显示等待20秒,所以我希望数据分析暂停,让新请求调用用于显示的初始api。

这是我的函数在doPost之后的一般结构(异步的,所以这是在Runnable中)。它有点长,所以为了方便阅读,我缩写了一下:

private void postMatches(ServletRequest req, ServletResponse res) {
        ... getting the necessary fields from the req ...
        /* read in values for arrays */
        String rankQueue = generateQueryStringArray("RankedQueues=", "rankedQueues", info);
        String season = generateQueryStringArray("seasons=", "seasons", info);
        String champion = generateQueryStringArray("championIds=", "championIds", info);
        /* first api call, "return" this and then start analysis */
        JSONObject recentMatches = caller.callRiotMatchHistory(region, "" + playerId);
        try {
            PrintWriter out = res.getWriter();
            out.write((new Gson()).toJson(recentMatches));
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        /* use array values to send more api calls */
        JSONObject matchList = caller.callRiotMatchList(playerId, region, rankQueue, season, champion);
        ... do a ton more api calls with the matchList's id's...
    }

所以我的一个想法是

每个客户端有两个线程。

这样,就会有一个线程调用单个api调用,而另一个线程将调用其余的999个api调用。通过这种方式,单个api调用线程只需等待来自同一客户机的另一个doPost来并立即调用api,而随之而来的批量api调用将被附加到另一个线程。通过这样做,两个线程将并行计算。

有一个优先级队列,将初始呼叫置于高优先级

这样,每个URL都将通过队列传递,我可以选择特定URL的compareTo更大(可能将其包装在bean中)。然而,我不确定api调用者如何能够区分哪个调用是哪个,因为一旦url被添加到队列中,它就失去了身份。有什么办法可以补救吗?我知道回调在java中是不可用的,所以很难做到。

这两种想法有可能吗?不需要代码,但它会非常感激!

PS:我使用Jersey API调用。

最好的选择似乎是使用"每个客户端两个线程"的解决方案。或者说是它的一个变体。

我认为你正在调用的API将有一些速率限制,因此大量的调用将被自动阻止。这对您来说是有问题的,因为您同时处理的几个请求可能很容易达到该限制。

此外,您可能会很快达到I/o限制,这取决于您的计算时间密集程度。这意味着你应该对你的后台API调用有一个内在的限制,初始请求应该没有任何内在的限制。因此,固定大小的ThreadPool似乎是完美的解决方案。在你的服务中将它作为静态ExecutorService公开应该是最简单的解决方案。

因此,我建议您公开一个静态服务,它将matchList作为参数,然后"做它的事情"。

它可能看起来像这样:

public class MatchProcessor {
    private static final ExecutorService service = Executors.newFixedThreadPool(THREADS);
    public static void processMatchList(final JSONObject matchList) {
        service.submit(() -> runAnalysis(matchList));
    }
    private static void runAnalysis(final JSONObject matchList) {
       //processing goes here
    }
}
附注:这段代码使用java 8,它应该很容易将提交的lambda转换为Runnable,如果你在java 7

最新更新