在弹簧休止请求中保持线程以进行长轮询

时间:2018-04-17 13:05:29

标签: java spring long-polling spring-rest java-threads

正如我在标题中所写,我们需要在项目中通过另一个线程通知或执行一些线程的方法。此实现是长轮询的一部分。在下面的文字中描述并展示我的实施。

所以要求是: 当UserX得到之前的响应时,UserX立即从客户端向服务器发送请求(轮询操作)。在服务中执行spring async方法,如果数据库中有一些新数据,线程会立即检查缓存。我知道缓存通常用于特定输入是特定输出的方法。这不是那种情况,因为我使用缓存来减少数据库调用,并且我的方法的输出总是不同的。因此,如果我应该检查数据库,缓存可以帮助我存储通知。此检查在while循环中运行,当线程查找通知以读取缓存中的数据库或时间到期时结束。

假设UserX线程(轮询操作)当前处于while循环并检查缓存。

在那一刻UserY(推送动作)向服务器发送一些数据,数据以分离的线程存储在数据库中,并且缓存中也存储了收件人的userId。

因此,当UserX检查缓存时,他发现了收件人的id(收件人的ID ==在这种情况下他的id),然后中断循环并获取这些数据。

因此,在我的实现中,我使用google guava缓存来提供手动编写。

private static Cache<Long, Long> cache = CacheBuilder.newBuilder()
        .maximumSize(100)
        .expireAfterWrite(5, TimeUnit.MINUTES)
        .build();

在create方法中,我存储了应该读取这些数据的用户ID。

public void create(Data data) {
    dataRepository.save(data);
    cache.save(data.getRecipient(), null);
    System.out.println("SAVED " + userId + " in " + Thread.currentThread().getName());
}

这是轮询数据的方法:

@Async
public CompletableFuture<List<Data>> pollData(Long previousMessageId, Long userId) throws InterruptedException {
    // check db at first, if there are new data no need go to loop and waiting
    List<Data> data = findRecent(dataId, userId));

    data not found so jump to loop for some time
    if (data.size() == 0) {
        short c = 0;
        while (c < 100) {

            // check if some new data added or not, if yes break loop
            if (cache.getIfPresent(userId) != null) {
                break;
            }
            c++;
            Thread.sleep(1000);
            System.out.println("SEQUENCE:  " + c + " in " +  Thread.currentThread().getName());
        }

        // check database on the end of loop or after break from loop
        data = findRecent(dataId, userId);
    }

    // clear data for that recipient and return result
    cache.clear(userId);
    return CompletableFuture.completedFuture(data);
}

用户X收到回复后,再次发送轮询请求,重复整个过程。

你能告诉我这个应用程序设计是否在java(spring)中进行长轮询是正确的还是存在更好的方法?关键点在于,当用户呼叫轮询请求时,该请求应该在一段时间内保留新数据,而不是立即响应。我在上面展示的这个解决方案有效,但问题是它是否也适用于许多用户(1000+)。我担心它是因为暂停线程,当池中没有线程可用时,应该使另一个请求变慢。感谢您的努力建议。

1 个答案:

答案 0 :(得分:0)

检查Web套接字。 Spring在病房版本4上支持它。它不需要客户端启动轮询,而是服务器实时将数据推送到客户端。

检查以下内容:

https://spring.io/guides/gs/messaging-stomp-websocket/

http://www.baeldung.com/websockets-spring

注 - Web套接字在客户端和服务器之间打开持久连接,因此在大量用户的情况下可能导致更多的资源使用。因此,如果您不是在寻找实时更新,并且有一些延迟,那么轮询可能是更好的方法。此外,并非所有浏览器都支持Web套接字。

Web Sockets vs Interval Polling

Longpolling vs Websockets

In what situations would AJAX long/short polling be preferred over HTML5 WebSockets?

在您当前的方法中,如果您担心在服务器上为多个用户运行大量线程,那么您可以每次都从前端触发轮询。这样,只有短暂的请求线程才会从UI中触发,以查找缓存中的任何更新。如果有更新,则可以进行另一次调用以检索数据。但是,如果不这样做,请不要每隔一秒击中服务器,否则您的CPU利用率会很高,用户请求线程也会受到影响。你应该对你的时间做一些优化。

您可以通过分析一段时间内缓存/数据库更新的模式来应用智能算法,而不是在延迟1秒后缓存100次。

通过了解模式,您可以以指数退避方式触发轮询,以便在最有可能发生更新时命中缓存。这样,您将更频繁,更准确地访问缓存。