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

Way to prioritize specific API calls with multithreads or priority queue?

在我的应用程序中,我的 servlet(运行 on tomcat)接收一个 doPost 请求,它 returns 一个 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 调用。

对您来说最好的选择似乎是使用 "two threads per client" 解决方案。或者更确切地说是它的变体。

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

此外,您可能会迟早达到 I/O-Limits,具体取决于您的计算时间密集程度。这意味着您应该对后台 API 调用有一个内在限制,初始请求应该没有任何内在限制。因此,您的服务中的 fixed-size ThreadPool seems to be the perfect solution. Exposing it as a static ExecutorService 应该是最简单的解决方案。

因此,我建议您公开一个静态服务,它将 matchList 作为参数,然后 "does it's thing"。

它可能看起来像这样:

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,如果您使用 Java 7

,将提交的 lambda 转换为 Runnable 应该很简单