做项目之前有个需求,就是浏览器端消息通知。一开始使用SSE技术实现,但是在真正的操作过程中,SSE技术可能不太友好:无法定义请求头(无法在请求头传入认证信息)、还有NG针对SSE特定设置、甲方服务器的安全扫描是否允许这个Conetext-Type。后来大哥给另一个哥们展示了一下他的长轮训的方案,我觉得也挺好的,就写一个这样一篇文章了。

介绍下这2个技术实现方案、以及区别

  1. 轮询(Polling)
    • 在轮询中,客户端定期向服务器发送请求以获取更新的数据。客户端以固定的时间间隔发出请求,无论服务器是否有新数据可用。
    • 如果服务器有新数据,它会立即响应并返回数据。如果没有新数据可用,服务器也会返回一个空响应。
    • 轮询的缺点是会浪费带宽和资源,因为即使没有新数据,客户端也会频繁地向服务器发送请求。
  2. 长轮询(Long Polling)
    • 在长轮询中,客户端发送一个持续打开的请求到服务器,服务器保持连接打开直到有新数据可用或超时。
    • 如果服务器有新数据,它会立即响应并返回数据,客户端处理完响应后会立即发送下一个请求,保持连接打开。
    • 如果服务器没有新数据,它会保持连接打开一段时间,直到有新数据可用或达到超时时间,然后返回一个空响应,客户端再次发起长轮询请求。
    • 长轮询减少了不必要的请求次数,降低了带宽和资源的浪费,但是仍然需要维持长时间的连接。

总的来说,轮询适用于需要频繁更新的数据且实时性要求不高的场景,但会导致资源浪费;长轮询适用于实时性要求较高的场景,能够减少不必要的请求次数,但会增加服务器维护长连接的负担。选择使用哪种方式取决于具体的应用场景和需求。

轮询就是前端不停的间隔一定时间,不停的调用。所以,后端无需特除处理接口。

Redis实现长轮询

我们都知道Redis有个命令 BLPOP:https://redis.io/commands/blpop/

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LongPollingController {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @GetMapping("/subscribe")
    public String subscribe() {
        // 使用Redis的BLPOP命令来阻塞等待消息
        String message = redisTemplate.opsForList().leftPop("messageQueue", 30, TimeUnit.SECONDS);
        return message != null ? message : "No new messages";
    }

    @GetMapping("/publish")
    public String publish(String message) {
        // 将消息发布到Redis的消息队列
        redisTemplate.opsForList().rightPush("messageQueue", message);
        return "Message published: " + message;
    }
}

这里其实会有问题的,这里介绍一下Spring项目的线程分别是干嘛的

在一个典型的Spring项目中,通常会有多个线程在运行,每个线程负责不同的任务。以下是一些常见的线程及其用途:

  1. 主线程(Main Thread)
    • 主线程是应用程序启动时创建的线程,负责启动Spring容器和处理应用程序的生命周期事件。
  2. Servlet容器线程
    • 在Web应用程序中,Servlet容器(如Tomcat、Jetty等)会为每个请求创建一个线程,用于处理HTTP请求和响应。
  3. Spring容器线程
    • Spring容器本身也会创建一些线程来管理Bean的生命周期、处理事件监听器等。
  4. 异步线程(Async Threads)
    • 当使用Spring的异步特性时,会创建额外的线程来处理异步任务,例如使用@Async注解或Spring的TaskExecutor接口。
  5. 定时任务线程(Scheduled Task Threads)
    • 当应用程序使用Spring的定时任务功能时(如@Scheduled注解),会创建线程来执行定时任务。
  6. 数据库连接池线程
    • 如果应用程序使用数据库连接池(如HikariCP、Tomcat JDBC等),这些连接池会管理一组线程来处理数据库连接的请求。
  7. 消息队列消费者线程
    • 如果应用程序使用消息队列(如RabbitMQ、Kafka等),会有一些线程用于消费消息并处理相应的逻辑。
  8. 其他自定义线程
    • 应用程序中可能会创建其他自定义线程来处理特定的任务,如后台处理、数据处理等。

这些线程在Spring项目中协同工作,以处理请求、执行任务和管理应用程序的各个方面。在设计应用程序时,需要考虑到线程的管理、安全性和性能等方面,以确保应用程序的稳定性和可靠性。

所以需要加入异步注解@Async,来将

    @Async
    @GetMapping("/subscribe")
    public CompletableFuture<String> subscribe() {
        String message = redisTemplate.opsForList().leftPop("messageQueue", 30, TimeUnit.SECONDS);
        return CompletableFuture.completedFuture(message != null ? message : "No new messages");
    }

当一个带有@Async注解的方法被调用时,Spring会将该方法的执行委托给异步任务执行器来处理,而不是在当前的Servlet线程中执行。这样可以避免阻塞Servlet容器的线程,提高系统的并发性能。

特殊说明:
上述文章均是作者实际操作后产出。烦请各位,请勿直接盗用!转载记得标注原文链接:www.zanglikun.com
第三方平台不会及时更新本文最新内容。如果发现本文资料不全,可访问本人的Java博客搜索:标题关键字。以获取全部资料 ❤