正如我在标题中所写的,我们需要在项目中通知或执行某个线程的方法。此实现是长轮询的一部分。在下面的文本中描述并显示我的实现。
所以要求是:
UserX 在收到前一个响应时立即从客户端向服务器发送请求(轮询操作)。在服务中执行 spring 异步方法,其中线程立即检查缓存中是否有一些新数据。我知道缓存通常用于特定输入是预期特定输出的方法。事实并非如此,因为我使用缓存来减少数据库调用,并且我的方法的输出总是不同的。因此,缓存可以帮助我存储通知,如果我是否应该检查数据库。此检查在 while 循环中运行,当线程找到通知以读取缓存中的数据库或时间过期时结束。
假设 UserX 线程(轮询操作)当前处于 while 循环和检查缓存中。
在那一刻,UserY(推送操作)将一些数据发送到服务器,数据存储在数据库中的分离线程中,并且缓存中还存储了接收者的userId。
因此,当用户X检查缓存时,他找到了收件人的id(在这种情况下,收件人的id ==他的id),然后中断循环并获取这些数据。
所以在我的实现中,我使用谷歌番石榴缓存,它提供手动写入。
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) 长轮询的应用程序设计是否正确或存在更好的方法吗?关键点是,当用户调用轮询请求时,此请求应保留一段时间以获取新数据,而不是立即响应。我在上面显示的这个解决方案有效,但问题是它是否也适用于许多用户(1000+)。我担心它,因为暂停线程应该在池中没有线程可用时发出更慢的另一个请求。感谢您的努力。
检查 Web 套接字。Spring 从版本 4 开始支持它。它不需要客户端启动轮询,而是服务器将数据实时推送到客户端。
检查以下内容:
https://spring.io/guides/gs/messaging-stomp-websocket/
http://www.baeldung.com/websockets-spring
注意 - Web 套接字在客户端和服务器之间打开持久连接,因此在大量用户的情况下可能会导致更多的资源使用。因此,如果您不是在寻找实时更新并且可以延迟一些,那么轮询可能是一种更好的方法。此外,并非所有浏览器都支持 Web 套接字。
Web 套接字与间隔轮询
长轮询与网络套接字
在什么情况下,AJAX 长/短轮询比 HTML5 WebSocket 更受欢迎?
在当前的方法中,如果您担心服务器上为多个用户运行大量线程,则可以每次都从前端触发轮询。这样,只会从 UI 触发短期请求线程,以查找缓存中的任何更新。如果有更新,可以进行另一个调用来检索数据。但是,不要像您这样做那样每隔一秒就点击一次服务器,否则您将具有很高的 CPU 利用率,并且用户请求线程也可能受到影响。您应该对时间进行一些优化。
您可以通过分析一段时间内的缓存/数据库更新模式来应用智能算法,而不是在 1 秒延迟 1 秒后命中缓存 100 次。
通过了解模式,您可以以指数退避方式触发轮询,以便在最有可能预期更新时命中缓存。这样,您将更频繁地、更准确地访问缓存。