首页
登录 | 注册

服务器端推送数据到浏览器实现方法之一Server-Sent Event

SSE 的使用方法可以参考:https://blog.csdn.net/xiewz1112/article/details/80591898

EventSource 对象的 API 文档地址:https://developer.mozilla.org/en-US/docs/Web/API/EventSource

 

SSE 的使用示例(主要代码来自《Java EE 开发的颠覆者 Spring Boot 实战》一书的 4.5.3 部分)

1. 页面sse.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>SSE Demo</title>
</head>
<body>
	<div id="msgFromPush"></div>
	<script type="text/javascript" src="<%=request.getContextPath()%>/assets/js/jquery-2.2.4.min.js"></script>
	<script type="text/javascript">
		if (window.EventSource) {
			console.log("该浏览器支持SSE");
			var source = new EventSource("ssePush");
			var s = "";
			
			source.addEventListener("open", function(e) {
				console.log("连接打开");
			}, false);
			
			source.addEventListener("message", function(e) {
				console.log("message: data=" + e.data + ", lastEventId=" + e.lastEventId);
				s += e.data + "<br>";
				$("#msgFromPush").html(s);
				if (e.data == "-1") {
					source.close();
				}
			});
			
			source.addEventListener("error", function(e) {
				if (e.readyState == EventSource.CLOSED) {
					console.log("连接关闭");
				} else {
					console.log(e.readyState);
				}
			}, false);
		} else {
			console.log("该浏览器不支持SSE");
		}
	</script>
</body>
</html>

2. SseData.java

package com.wisely.highlight_springmvc4.entity;

import java.util.Arrays;
import java.util.List;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;

public class SseData {
	private static final String CR = "\n";
	
	private String data = "";// 表示数据内容。属性来自于MessageEvent
	private String event = "message";// 自定义的事件类型,默认是message事件。
	private String id;// 数据标识符用id字段表示,相当于每一条数据的编号。属性来自于MessageEvent
	private long retry;// 指定浏览器重新发起连接的时间间隔。单位:ms
	private List<String> comments = Lists.newArrayList();
	
	public static class SseDataBuilder {
		private SseData sseData;
		private SseDataBuilder() {
			this.sseData = new SseData();
		}
		
		public static SseDataBuilder builder() {
			return new SseDataBuilder();
		}
		
		public SseData build() {
			return this.sseData;
		}
		
		public SseDataBuilder setData(String data) {
			Preconditions.checkNotNull(data);
			this.sseData.setData(data);
			return this;
		}
		public SseDataBuilder setEvent(String event) {
			Preconditions.checkNotNull(event);
			this.sseData.setEvent(event);
			return this;
		}
		public SseDataBuilder setId(String id) {
			Preconditions.checkNotNull(id);
			this.sseData.setId(id);
			return this;
		}
		public SseDataBuilder setRetry(long retry) {
			Preconditions.checkArgument(retry > 0L, "retry must be greater than 0");
			this.sseData.setRetry(retry);
			return this;
		}
		public SseDataBuilder setComments(String... comments) {
			Preconditions.checkArgument(comments != null && comments.length > 0, "comments must not be empty");
			this.sseData.setComments(Arrays.asList(comments));
			return this;
		}
	}
	

	public String getData() {
		return data;
	}
	public void setData(String data) {
		this.data = data;
	}
	public String getEvent() {
		return event;
	}
	public void setEvent(String event) {
		this.event = event;
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public long getRetry() {
		return retry;
	}
	public void setRetry(long retry) {
		this.retry = retry;
	}
	public List<String> getComments() {
		return comments;
	}
	public void setComments(List<String> comments) {
		this.comments = comments;
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		
		if (CollectionUtils.isNotEmpty(comments)) {
			for (String comment : comments) {
				if (comment != null) {
					builder.append(':').append(comment).append(CR);
				}
			}
		}
		builder.append("event:").append(StringUtils.defaultString(event, "message")).append(CR);
		if (id != null) {
			builder.append("id:").append(id).append(CR);
		}
		if (retry > 0L) {
			builder.append("retry:").append(retry).append(CR);
		}
		builder.append("data:").append(Strings.nullToEmpty(data)).append(CR).append(CR);
		
		return builder.toString();
	}
}

3. SseController.java

package com.wisely.highlight_springmvc4.ch4_5;

import java.util.Date;

import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.wisely.highlight_springmvc4.entity.SseData.SseDataBuilder;

@Controller
public class SseController {
	
	@RequestMapping(value="/ssePush", produces="text/event-stream")
	@ResponseBody
	public String push() {
		try {
			// 休眠2秒模拟处理过程
			Thread.sleep(2000L);
		} catch (Exception e) {
			// not handler
		}
		
		int num = RandomUtils.nextInt(1, 11);
		if (num % 10 == 0) {// 结束
			return SseDataBuilder.builder()
					.setComments("over")
					.setData("-1")
					.build()
					.toString();
		} else {
			return SseDataBuilder.builder()
					.setComments("comment " + num)
					.setId(String.valueOf(num))
					.setRetry(2000L)
					.setData("ssePush value[" + num + "] to browser at " + FastDateFormat.getInstance("HH:mm:ss").format(new Date()))
					.build().toString();
		}
	}
}

4. 浏览器 Console 控制台效果

服务器端推送数据到浏览器实现方法之一Server-Sent Event

5. 项目日志

服务器端推送数据到浏览器实现方法之一Server-Sent Event

 


相关文章

  • 文章目录前言数据探查服务的初始点:元数据的同步数据探查服务的分析:索引结构的重新构建数据探查服务的结果:汇聚表DB的存储数据探查服务的额外功能:节点级别的统计数据探查服务的外部展现:用户控制台引用 https://issues.apache ...
  • 像数据科学家一样思考:12步指南(下)
    第三阶段-完成 一旦产品构建完成,你仍然需要做一些事情来使项目更加成功并使你的未来生活更轻松.那么我们如何完成数据科学项目呢? 10-交付产品 完成阶段的第一步是产品交付.为了创建可以交付给客户的有效产品,首先必须了解客户的观点.其次,你需 ...
  • 前言 气象数据是一类典型的大数据,具有数据量大.时效性高.数据种类丰富等特点.气象数据中大量的数据是时空数据,记录了时间和空间范围内各个点的各个物理量的观测量或者模拟量,每天产生的数据量常在几十TB到上百TB的规模,且在爆发性增长.如何存储 ...
  • 一份还热乎的蚂蚁金服面经(已拿Offer)!附答案!!
    本文来自我的知识星球的球友投稿,他在最近的校招中拿到了蚂蚁金服的实习生Offer,整体思路和面试题目由作者--泽林提供,部分答案由Hollis整理自知识星球<Hollis和他的朋友们>中「直面Java」板块. 经历了漫长一个月的 ...
  • 源码|详解分布式事务之 Seata-Client 原理及流程
    前言 在分布式系统中,分布式事务是一个必须要解决的问题,目前使用较多的是最终一致性方案.自年初阿里开源了Fescar(四月初更名为Seata)后,该项目受到了极大的关注,目前已接近 8000 Star.Seata 以高性能和零侵入的特性为目 ...
  • 云原生时代来袭 下一代云数据库技术将走向何方?
    全面云化的时代已经到来,面对一系列的新技术和挑战,数据库市场将面临怎样的变革?作为云服务提供商,如何帮助更多的企业级用户把握"云"潮,提供最高效.最具价值的数据库解决方案? 日前,在阿里云峰会·北京站的数据库专场上,阿里 ...

2020 jeepshoe.net webmaster#jeepshoe.net
13 q. 0.341 s.
京ICP备10005923号