服务器推送技术之短轮询、长轮询、SSE和Websocket

服务器推送技术

服务器推送技术干嘛用?就是让用户在使用网络应用的时候,不需要一遍又一遍的去手动刷新就可以及时获得更新的信息。大家平时在上各种视频网站时,对视频节目进行欢乐的吐槽和评论,会看到各种弹幕,当然,他们是用flash技术实现的,对于我们没有用flash的应用,一样可以实现弹幕。又比如在股票网站,往往可以看到,各种股票信息的实时刷新,上面的这些都是基于服务器推送技术。

Ajax短轮询

Ajax短轮询就是用一个定时器不停的去网站上请求数据。

在这里插入图片描述

下面的代码实现浏览器页面实时显示服务器的当前时间。

服务器端的实现如下:

package com.morris.websocket.shortpoll;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.time.LocalDateTime;

@Controller
public class TimeController {

    @RequestMapping("getTime")
    @ResponseBody
    public String getTime() {
        return LocalDateTime.now().toString();
    }

}

html页面实现如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Show Time</title>
</head>
<body>
当前时间为:<span id="timeSpan"></span>

<script type="text/javascript" src="/js/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
    function showTime(){
        $.get("/getTime",function (data) {
            console.log(data);
            $("#timeSpan").html(data);
        })
    }

    setInterval(showTime, 1000);
</script>
</form>
</body>
</html>

Comet

基于HTTP长连接、无须在浏览器端安装插件的“服务器推”技术为“Comet”,comet有下面两种实现方式:

  • 基于AJAX的长轮询(long-polling)方式。
  • 基于长连接的服务器推模型 Server-sent-events(SSE)。

基于AJAX的长轮询(long-polling)方式。

服务器端实现长轮询可以使用异步任务,这里使用的是Spring MVC对Servlet3.0规范所支持的异步请求方式。

在这里插入图片描述

下面的代码实现服务器端向浏览器页面实时推送新闻。

服务器端的实现如下:

package com.morris.websocket.longpoll;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

@Controller
public class PushNewsController {

    @RequestMapping("realTimeNews")
    @ResponseBody
    public Callable<String> realtimeNews() {
        Callable<String> callable = () -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int index = new Random().nextInt(Const.NEWS.length);
            return Const.NEWS[index];
        };
        return callable;
    }

}

html页面实现如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>新闻推送</title>
</head>
<body>
<h1>每日头条</h1>
<div>
    <div>
        <h2>每日头条新闻实时看</h2>
        <div style="color:#F00"><b><p id="realTimeNews"></p></b></div>
    </div>
</div>
<script type="text/javascript" src="/js/jquery-1.9.1.min.js"></script>
<script type="text/javascript">

    longLoop();

    function longLoop() {
        $.get("/realTimeNews", function (data) {
            console.log(data);
            $("#realTimeNews").html(data);
            longLoop();
        })

    }
</script>
</body>
</html>

基于长连接的服务器推模型Server-sent-events(SSE)。

严格地说,HTTP协议无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息(streaming)。也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。

本质上,这种通信就是以流信息的方式,完成一次用时很长的下载。

SSE就是利用这种机制,使用流信息向浏览器推送信息。它基于HTTP协议,目前除了IE/Edge,其他浏览器都支持。

在这里插入图片描述

下面的代码实现服务器端向浏览器页面实时推送贵金属最新价格。

服务器端的实现如下:

package com.morris.websocket.sse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Random;

@Controller
public class NobleMetalController {

    @RequestMapping("needPrice")
    public void pushRight(HttpServletResponse response){
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("utf-8");
        Random r = new Random();
        try {
            PrintWriter pw = response.getWriter();
            int i = 0;
            while(i<10){
                if(pw.checkError()){
                    System.out.println("客户端断开连接");
                    return;
                }
                Thread.sleep(1000);
                pw.write(makeResp(r));
                pw.flush();
                i++;
            }
            System.out.println("达到阈值,结束发送.......");
            pw.write("data:stop\n\n");
            pw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /*业务方法,生成贵金属的实时价格*/
    private String makeResp(Random r){
        StringBuilder stringBuilder = new StringBuilder("");
        stringBuilder.append("retry:2000\n")
                .append("data:")
                .append(r.nextInt(100)+50+",")
                .append(r.nextInt(40)+35)
                .append("\n\n");
        return stringBuilder.toString();
    }
}

html页面实现如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>贵金属</title>
</head>
<body>
<h1>贵金属</h1>
<div>
    <div>
        <h2>贵金属列表</h2>
    </div>
    <div>
        <h2 id="hint"></h2>
    </div>
    <hr>
    <div>
        <div><p>黄金</p>
            <p id="c0" style="color:#F00"></p><b><p id="s0">历史价格:</p></b></div>
        <div><p>白银</p>
            <p id="c1" style="color:#F00"></p><b><p id="s1">历史价格:</p></b></div>
    </div>
    <hr>

</div>
<script type="text/javascript" src="/js/jquery-1.9.1.min.js"></script>
<script type="text/javascript">

    function showPrice(index, data) {
        $("#c" + index).html("当前价格:" + data);
        var s = $("#s" + index).html();
        $("#s" + index).html(s + data + " ");
    }

    if (!!window.EventSource) {
        var source = new EventSource('/needPrice');
        source.onmessage = function (e) {
            var dataObj = e.data;
            var arr = dataObj.split(',');
            $.each(arr, function (i, item) {
                showPrice(i, item);
            });
            $("#hint").html("");
        };

        source.onopen = function (e) {
            console.log("Connecting server!");
        };

        source.onerror = function () {
            console.log("error");
        };

    } else {
        $("#hint").html("您的浏览器不支持SSE!");
    }

</script>
</body>
</html>

各种服务器推送技术的比较

短轮询长轮询SSEWebSocket
浏览器支持度最高很高中(IE和Edge均不支持)
实时性最低较高很高
代码实现复杂度最容易较容易容易
连接性质短连接长连接长连接
适用需要服务极大量或极小量的用户,实时性要求不高准实时性的应用,比较关注浏览器的兼容性实时,基本都是文本交互的应用

应用场景

服务器推送技术常用于二维码登录,二维码支付等场景。

在这里插入图片描述

淘宝登录用的什么?Ajax短轮询,这说明什么?这些技术并没有什么优劣之分,只有合不合适业务的问题。淘宝的痛点是什么?要用有限的资源来为千万级甚至上亿的用户提供服务,如果是用长连接,对于接入的服务器,比如说 Nginx,是很大的压力,光是为用户维持这个长连接都需要成百上千的Nginx的服务器,这是很划不来的。

  • 5
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

morris131

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值