8、基于长轮询的SSE(server send event)

注:源代码来自享学课堂,略有修改,学习之后所做笔记,方便回顾,也给大家一个参考 


SSE和DeferredResult不同之处在于:


DeferredResult需要页面重复发送多个请求,来刷新页面,原理是使用Servlet3的异步任务,有个缺点,下次发送请求的时候,还是会有个时间空格,消息会有一点延迟;SSE是页面只发送一次请求到服务器,然后服务器自动多次返回结果,服务器发送的数据是一个流数据,不断发送,不让客户端关闭连接。

SSE更适合金融实时数据。

 

页面处理

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

    if(!!window.EventSource){//判断浏览器支持度
        //拿到sse的对象
        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.οnerrοr=function () {
            console.log("error");
        };

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

接口逻辑处理

/*不完美的用法*/
    @RequestMapping(value="/needPrice",produces = "text/event-stream;charset=UTF-8")
    @ResponseBody
    public String push(){
        Random r = new Random();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        StringBuilder stringBuilder = new StringBuilder("");
        stringBuilder.append("retry:1\n")
                .append("data:")
                .append((r.nextInt(100)+50)+",")
                .append((r.nextInt(80)+45)+",")
                .append((r.nextInt(60)+40)+",")
                .append((r.nextInt(40)+35))
                .append("\n\n");

        logger.info("当前股票信息:"+stringBuilder.toString());
        return stringBuilder.toString();
    }

还可以子服务器自定义一个counter,多少次之后会关闭长链接

    @RequestMapping(value="needPrice")
    @ResponseBody
    public void push(HttpServletResponse response){
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("utf-8");
        Random r = new Random();
        int sendCount = 0;/*服务器数据发送完*/
        try {
            PrintWriter pw = response.getWriter();
            while(true){
                if(pw.checkError()){
                    System.out.println("客户端断开连接");
                    return;
                }
                Thread.sleep(1000);
                //字符串拼接,使用\n\n代表当前数据包结束,页面可以处理了
                StringBuilder sb = new StringBuilder("");
                sb//.append("retry:2000\n")
                        .append("data:")
                        .append((r.nextInt(1000)+50)+",")
                        .append((r.nextInt(800)+100)+",")
                        .append((r.nextInt(2000)+150)+",")
                        .append((r.nextInt(1500)+100)+",")
                        .append("\n\n");

                pw.write(sb.toString());
                pw.flush();
                sendCount++;
                if(sendCount>=100){
                    return;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 

使用spring提供的方法,模拟支付,使用spring提供的SseEmitter

页面调用支付接口之后,页面“订单提交中”->“支付完成”

@Controller
public class SseController {
    private static Logger logger = LoggerFactory.getLogger(SseController.class);

    private static Map<String,SseEmitter> sseEmitters
            = new ConcurrentHashMap<>();
    private ExecutorService executorService
            = Executors.newFixedThreadPool(2);

    @RequestMapping("/weChatPay")
    public String stock(){
        return "weChatPay";
    }

    @RequestMapping(value="/payMoney")
    @ResponseBody
    public SseEmitter pay(String weCharId){
        SseEmitter emitter = new SseEmitter();
        sseEmitters.put(weCharId,emitter);
        executorService.submit(new Pay(weCharId) );
        return emitter;
    }

    private static class Pay implements Runnable{

        private String weCharId;

        public Pay(String weCharId) {
            this.weCharId = weCharId;
        }

        @Override
        public void run() {
            SseEmitter sseEmitter = sseEmitters.get(weCharId);
            try {
                logger.info("联系支付服务,准备扣款");
                Thread.sleep(500);
                sseEmitter.send("支付完成");
                logger.info("准备通知自动售货机");
                Thread.sleep(1500);//售货机的动作
                sseEmitter.send("已通知自动售货机C9出货,请勿走开!");
                sseEmitter.complete();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


}

结果:

页面上的金额数字不断发生变化

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章