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();
            }
        }
    }


}

結果:

頁面上的金額數字不斷髮生變化

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