java - 将线程保持在 Spring 休息请求中以进行长轮询

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

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

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

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

在那一刻,UserY(推送操作)向服务器发送一些数据,数据在单独的线程中存储在数据库中,并且在缓存中存储接收者的 userId。

因此,当 UserX 检查缓存时,他找到了收件人的 id(在本例中,收件人的 id == 他的 id),然后中断循环并获取这些数据。

因此,在我的实现中,我使用谷歌 Guava 缓存,它提供手动写入。

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

在创建方法中,我存储应该读取这些数据的用户 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)中进行长轮询的应用程序设计是否正确或者是否存在更好的方法?关键是当用户调用 poll 请求时,该请求应该为新数据保留一段时间,而不是立即响应。我上面展示的这个解决方案有效,但问题是它是否也适用于许多用户(1000+)。我担心这个问题,因为当池中没有可用线程时,暂停线程会使另一个请求变慢。感谢您的努力的建议。

最佳答案

检查 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 次。

通过了解该模式,您可以以指数退避方式触发轮询,以便在最有可能预期更新时命中缓存。这样您就会减少访问缓存的频率,并且更加准确。

关于java - 将线程保持在 Spring 休息请求中以进行长轮询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49878958/

相关文章:

java - 如何让jUnit类有参数

java - 是否可以在 spring batch 中跨单个文件进行分区?

ajax - a4j :push not using websockets and falling back to long-polling

haskell - Yesod的长期投票

java - Thread.sleep() 和 schedulerExecutorService.scheduleWithFixedDelay 的性能注意事项

java - Android 中的重叠按钮

java - 在 @ModelAttribute 注释方法中返回 Spring 模型

java - Spring 测试 NoMethod 错误

java - 如何在基于 Spring 的应用程序上使用所有 AspectJ 切入点指示符?

java - 通过 PHP 服务器将信息推送到 Android 应用程序?