java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java 服务端消息推送

Java 服务端消息推送的实现小结

作者:专注于Java中间件的程序员木木

本文主要介绍了Java 服务端消息推送的实现小结,主要包括四种常见的消息实时推送方案:短轮询、长轮询、SSE 和 WebSocket,具有一定的参考价值,感兴趣的可以了解一下

前言:当构建实时消息推送功能时,选择适合的方案对于开发高效的实时应用至关重要。消息的推送无非就推、拉两种数据模型。本文将介绍四种常见的消息实时推送方案:短轮询(拉)、长轮训(拉)、SSE(Server-Sent Events)(推)和WebSocket(推),并以Spring Boot作为技术底座,展示如何在Java全栈开发中实现这些功能。

1. 短轮询(Short Polling)

什么是短轮询?

短轮询是一种简单的实时消息推送方案,其中客户端通过定期向服务器发送请求来获取最新的消息。服务器在接收到请求后立即响应,无论是否有新消息。如果服务器没有新消息可用,客户端将再次发送请求。

短轮询的实现

在Spring Boot中,可以通过HTTP接口和定时任务来实现短轮询。下面是一个简单的示例:

// -- 后端接口
@GetMapping("/short")
public String  getShort() {
    long l = System.currentTimeMillis();
    if((l&1) == 1) {
        return "ok";
    }
    return "fail";
}
// --- 前端页面
@org.springframework.stereotype.Controller
public class Controller {
    @GetMapping("/s")
    public String s() {
        return "s";
    }
}
// -- s.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>短轮询</title>
</head>
<body>
<p>msg=<span id="message"></span></p>
<script>
    function pollMessage() {
// 发送轮询请求
        const xhr = new XMLHttpRequest();
        xhr.open("GET", "/short", true);
        xhr.onreadystatechange = function () {
            if (xhr.readyState === XMLHttpRequest.DONE) {
                if (xhr.status === 200) {
                    document.getElementById("message").innerHTML = xhr.responseText;
                }
            }
        };
        xhr.send();
    }
    setInterval(()=>{
        pollMessage()
    }, 1000)
</script>
</body>
</html>

在上述示例中,getShort()方法用于返回消息,而s方法用于渲染s.html。客户端可以定期调用getShort()接口来获取最新的消息。

短轮询的特点与限制

短轮询的实现简单,但存在一些特点和限制:

2. 长轮询(Long Polling)

什么是长轮询?

长轮询是改进的轮询方法,它在没有新消息时会保持请求挂起,直到有新消息到达或超时。相比于短轮询,长轮询可以更快地获取新消息,减少了不必要的请求。

长轮询的实现

在Spring Boot中,可以使用异步请求和定时任务来实现长轮询。下面是一个简单的示例:

// -- 请求接口
/**
 * 长轮询
 * @return
 */
@GetMapping("/long")
public DeferredResult<String> getLong() {
    DeferredResult<String> deferredResult = new DeferredResult<>();
    if (latestMessage != null) {
        deferredResult.setResult(latestMessage);
    } else {
        // 使用定时任务设置超时时间
        TimerTask timeoutTask = new TimerTask() {
            @Override
            public void run() {
                deferredResult.setResult(null);
            }
        };
        Timer timer = new Timer();
        timer.schedule(timeoutTask, 5000); // 设置超时时间为5秒
        // 设置回调函数,在消息到达时触发
        deferredResult.onTimeout(() -> {
            timer.cancel();
            deferredResult.setResult(null);
        });
        deferredResult.onCompletion(timer::cancel);
    }
    return deferredResult;
}
/**
 * 设置消息
 * @param message
 */
@PostMapping("/send-message")
public void sendMessage(@RequestBody String message) {
    latestMessage = message;
}
// -- 前端请求
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>长轮询</title>
</head>
<body>
<p>msg=<span id="message"></span></p>
<p>请求次数:<span id="cnt"></span></p>
<script>
    var cnt = 0
    function pollMessage() {
// 发送轮询请求
        const xhr = new XMLHttpRequest();
        xhr.open("GET", "/long", true);
        xhr.onreadystatechange = function () {
            if (xhr.readyState === XMLHttpRequest.DONE) {
                if (xhr.status === 200) {
                    document.getElementById("message").innerHTML = xhr.responseText;
                }
            }
        };
        xhr.send();
    }
    setInterval(()=>{
        ++cnt;
        document.getElementById('cnt').innerHTML = cnt.toString()
        pollMessage()
    }, 5000)
</script>
</body>
</html>

在上述示例中,getLong()方法返回一个DeferredResult对象,它会在有新消息到达时触发回调函数。如果在超时时间内没有新消息到达,DeferredResult对象将返回null

长轮询的特点与限制

长轮询具有以下特点与限制:

3. SSE(Server-Sent Events)

在这里插入图片描述

什么是SSE?

当使用Server-Sent Events(SSE)时,客户端(通常是浏览器)与服务器之间建立一种持久的连接,使服务器能够主动向客户端发送数据。这种单向的、服务器主动推送数据的通信模式使得实时更新的数据能够被实时地传送到客户端,而无需客户端进行轮询请求。

SSE的工作原理如下:

SSE的实现

在Spring Boot中,可以使用SseEmitter类来实现SSE。下面是一个简单的示例:

@RestController
public class SSEController {
    private SseEmitter sseEmitter;
    @GetMapping("/subscribe")
    public SseEmitter subscribe() {
        sseEmitter = new SseEmitter();
        return sseEmitter;
    }
    @PostMapping("/send-message")
    public void sendMessage(@RequestBody String message) {
        try {
            if (sseEmitter != null) {
                sseEmitter.send(SseEmitter.event().data(message));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
// - s.html
<!DOCTYPE html>
<html>
<head>
    <title>SSE Demo</title>
</head>
<body>
<h1>SSE Demo</h1>
<div id="message-container"></div>
<script>
    // 创建一个EventSource对象,指定SSE的服务端端点
    var eventSource = new EventSource('/subscribe');
    console.log("eventSource=", eventSource)
    // 监听message事件,接收从服务端发送的消息
    eventSource.addEventListener('message', function(event) {
        var message = event.data;
        console.log("message=", message)
        var messageContainer = document.getElementById('message-container');
        messageContainer.innerHTML += '<p>' + message + '</p>';
    });
</script>
</body>
</html>

在上述示例中,客户端可以通过访问/subscribe接口来订阅SSE事件,服务器会返回一个SseEmitter对象。当有新消息到达时,调用SseEmitter对象的send()方法发送消息。

在这里插入图片描述

SSE的特点与限制

SSE具有以下特点与限制:

4. WebSocket

在这里插入图片描述

什么是WebSocket?

WebSocket是一种双向通信协议,允许在单个持久连接上进行全双工通信。与之前介绍的方案不同,WebSocket提供了双向通信的能力,可以实现实时的双向数据传输。

WebSocket的实现

在Spring Boot中,可以使用Spring WebSocket模块来实现WebSocket功能。下面是一个简单的示例:

1. 创建一个WebSocket处理器:

@Component
public class WebSocketHandler extends TextWebSocketHandler {
    private List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        sessions.add(session);
    }
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        for (WebSocketSession webSocketSession : sessions) {
            webSocketSession.sendMessage(message);
        }
    }
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        sessions.remove(session);
    }
}

2. 配置WebSocket端点:

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Autowired
    private WebSocketHandler webSocketHandler;
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(webSocketHandler, "/websocket").setAllowedOrigins("*");
    }
}

在上述示例中,WebSocketHandler处理器负责处理WebSocket连接、消息传递和连接关闭等事件。WebSocketConfig类用于配置WebSocket端点。

3. 前端实现

<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Demo</title>
</head>
<body>
<h1>WebSocket Demo</h1>
<div id="message-container"></div>
<script>
    // 创建WebSocket对象,并指定服务器的URL
    var socket = new WebSocket('ws://localhost:8080/websocket');
    // 监听WebSocket的连接事件
    socket.onopen = function(event) {
        console.log('WebSocket connected');
    };
    // 监听WebSocket的消息事件
    socket.onmessage = function(event) {
        var message = event.data;
        var messageContainer = document.getElementById('message-container');
        messageContainer.innerHTML += '<p>' + message + '</p>';
    };
    // 监听WebSocket的关闭事件
    socket.onclose = function(event) {
        console.log('WebSocket closed');
    };
    // 发送消息到服务器
    function sendMessage() {
        var messageInput = document.getElementById('message-input');
        var message = messageInput.value;
        socket.send(message);
        messageInput.value = '';
    }
</script>
<input type="text" id="message-input" placeholder="Enter message">
<button onclick="sendMessage()">Send</button>
</body>
</html>

在这里插入图片描述

WebSocket的特点与限制

WebSocket具有以下特点与限制:

总结

本文介绍了四种常见的消息实时推送方案:短轮询、长轮询、SSE 和 WebSocket,并以 Spring Boot 作为技术底座,展示了如何在 Java 全栈开发中实现这些功能。

选择合适的消息实时推送方案取决于具体的需求和场景。根据应用程序的要求和预期的用户体验,开发人员可以选择适当的方案来实现实时消息推送功能。

注意

以上实现均属于demo 级别,为了简单演示,将所有的服务保证措施都删除了,所以存在包括但不限于以下缺点

比如

 到此这篇关于Java 服务端消息推送的实现小结的文章就介绍到这了,更多相关Java 服务端消息推送内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文