范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

SSE(ServerSendEvents)实践

  介绍
  HTTP 是客户端-服务器计算模型中的请求-响应协议。要开始交换,客户端向服务器提交请求。为了完成交换,服务器向客户端返回响应。服务器只能向一个客户端发送响应 (发出请求的那个) 。在 HTTP 协议中,客户端是消息交换的发起者。
  有些场景需要由服务端主动推送消息给客户端。实现这一点的方法之一是允许服务器在 发布/订阅 计算模型中向客户端推送消息。要开始交换,客户端从服务器订阅消息。在交换期间,服务器向许多订阅的客户端发送消息(一旦它们可用)。
  服务器发送事件 (SSE) 是一种简单的技术,用于为特定的 Web 应用程序实现服务器到客户端的异步通信。 概述
  有多种技术允许客户端从服务器接收有关异步更新的消息。它们可以分为两类: 客户端拉取 和 服务器推送 。 客户端拉取
  在客户端拉取技术中,客户端会定期向服务器请求更新。服务器可以使用更新或尚未更新的特殊响应进行响应。有两种类型的客户端拉取:短轮询和长轮询。 短轮询
  客户端定期向服务器发送请求。如果服务器有更新,它会向客户端发送响应并关闭连接。如果服务器没有更新,它也会向客户端发送一个响应并关闭连接。 长轮询
  客户端向服务器发送请求。如果服务器有更新,它会向客户端发送响应并关闭连接。如果服务器没有更新,它会保持连接直到更新可用。当更新可用时,服务器向客户端发送响应并关闭连接。如果更新在某个超时时间内不可用,服务器会向客户端发送响应并关闭连接。 服务端推送
  在服务器推送技术中,服务器在消息可用后立即主动向客户端发送消息。其中,有两种类型的服务器推送:SSE和 WebSocket。 SSE(Server-Send Events)
  SSE 是一种在基于浏览器的 Web 应用程序中仅从服务器向客户端发送文本消息的技术。SSE基于 HTTP 协议中的持久连接, 具有由 W3C 标准化的网络协议和 EventSource 客户端接口,作为 HTML5 标准套件的一部分。 WebSocket
  WebSocket 是一种在 Web 应用程序中实现同时、双向、实时通信的技术。WebSocket 基于 HTTP 以外的协议(TCP),因此可能需要额外设置网络基础设施(代理服务器、NAT、防火墙等)。
  客户端通过Http协议请求,在握手阶段升级为WebSocket协议。 SSE 网络协议
  要订阅服务器事件,客户端发出 GET 请求带有指定的header: Accept: text/event-stream 表示可接收事件流类型 Cache-Control: no-cache 禁用任何的事件缓存 Connection: keep-alive 表示正在使用持久连接 GET /sse HTTP/1.1
  Accept: text/event-stream
  Cache-Control: no-cache
  Connection: keep-alive
  服务器应该使用带有标题的响应来确认订阅: Content-Type: text/event-stream;charset=UTF-8  表示标准要求的事件的媒体类型和编码 Transfer-Encoding: chunked  表示服务器流式传输动态生成的内容,因此内容大小事先未知 HTTP/1.1 200
  Content-Type: text/event-stream;charset=UTF-8
  Transfer-Encoding: chunked
  订阅后,服务端在消息可用时立即发送给客户端。事件是采用 UTF-8 编码的文本消息。事件之间由两个换行符分隔  。每个事件由一个或多个名称:值字段组成,由单个换行符  分隔。
  在数据字段中,服务器可以发送事件数据 data: The first event.
  data: The second event.
  服务器可以发送唯一的事件标识符(id字段)。如果连接中断,客户端会 自动重新连接 并发送最后接收到的带有header的 Last-Event-ID 的事件 ID。
  在事件字段中,服务器可以发送事件类型。服务器可以在同一个订阅中发送不同类型的事件,也可以不发送任何类型的事件。 event: type1
  data: An event of type1.
  event: type2
  data: An event of type2.
  data: An event without any type.
  在重试字段中,服务器可以发送超时(以毫秒为单位),之后客户端应在连接中断时自动重新连接。如果未指定此字段,则标准应为 3000 毫秒。 retry: 1000
  如果一行以冒号字符 : 开头,客户端应该忽略它。这可用于从服务器发送评论或防止某些代理服务器因超时关闭连接。
  : pingSSE 客户端: EventSource 接口
  要打开连接,应创建一个 EventSource 对象。 var eventSource = new EventSource("/sse);
  尽管 SSE 旨在将事件从服务器发送到客户端,但可以使用 GET 查询参数将数据从客户端传递到服务器。 var eventSource = new EventSource("/sse?event=type1);
  ...
  eventSource.close();
  eventSource = new EventSource("/sse?event=type1&event=type2);
  ...
  要关闭连接,应调用方法 close()。
  eventSource.close();
  有表示连接状态的 readyState 属性: EventSource.CONNECTING = 0 - 连接尚未建立,或已关闭且客户端正在重新连接 EventSource.OPEN = 1 - 客户端有一个打开的连接并在接收到事件时处理它们 EventSource.CLOSED = 2- 连接未打开,并且客户端未尝试重新连接,要么出现致命错误,要么调用了 close() 方法
  要处理连接的建立,它应该订阅 onopen 事件处理程序。 eventSource.onopen = function () {
  console.log("connection is established");
  };
  为了处理连接状态的一些异常或致命错误,它应该订阅 onerrror 事件处理程序。 eventSource.onerror = function (event) {
  console.log("connection state: " + eventSource.readyState + ", error: " + event);
  };
  客户端接收消息并处理他们,可以使用onmessage方法eventSource.onmessage = function (event) {
  console.log("id: " + event.lastEventId + ", data: " + event.data);
  };
  SSE可被大多数浏览器支持:
  SSE Java 服务端: Spring Web MVC介绍
  Spring Web MVC 框架 5.2.0 是基于 Servlet 3.1 API 且用线程池实现异步应用程序. 所以应用能够被使用在 Servlet 3.1+ 的容器,比如:Tomcat 8.5 和 Jetty 9.3. 概述
  使用Spring MVC来发送事件: 使用 @RestController 注解创建一个控制器类(Controller) 创建一个方法来创建一个客户端连接,它返回一个  SseEmitter ,处理 GET 请求并产生( produces )文本/事件流  (text/event-stream) 创建一个新的 SseEmitter, 保存它并从方法中返回 在另一个线程中异步发送事件, 先拿到保存的 SseEmitter  并根据需要多次调用 调用SseEmitter.send 方法 完成事件发送, 调用  SseEmitter.complete() 方法 要异常完成发送事件,请调用 SseEmitter.completeWithError() 方法
  示例: @RestController public class SseWebMvcController      private SseEmitter emitter;      @GetMapping(path="/sse", produces=MediaType.TEXT_EVENT_STREAM_VALUE)     SseEmitter createConnection() {         emitter = new SseEmitter();         return emitter;     }      // in another thread     void sendEvents() {         try {             emitter.send("Alpha");             emitter.send("Omega");              emitter.complete();         } catch(Exception e) {             emitter.completeWithError(e);         }     } }
  处理短暂的周期性事件流
  在这个例子中,服务器每秒发送一个持续时间短的周期性事件流 - 一个有限的词流,直到词完成。
  示例: @Controller @RequestMapping("/sse/mvc") public class WordsController {     private static final String[] WORDS = "The quick brown fox jumps over the lazy dog.".split(" ");     private final ExecutorService cachedThreadPool = Executors.newCachedThreadPool();     @GetMapping(path = "/words", produces = MediaType.TEXT_EVENT_STREAM_VALUE)    SseEmitter getWords() {        SseEmitter emitter = new SseEmitter();         cachedThreadPool.execute(() -> {            try {                for (int i = 0; i < WORDS.length; i++) {                    emitter.send(WORDS[i]);                    TimeUnit.SECONDS.sleep(1);                }                 emitter.complete();            } catch (Exception e) {                emitter.completeWithError(e);            }        });         return emitter;    } }
  运行效果:
  客户端示例(words.html):          Server-Sent Events client example with EventSource     
  运行效果:
  处理长期持续的周期性事件
  在此示例中,服务器发送持久的周期性事件流 - 每秒可能无限的服务器性能信息流: import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;  import javax.annotation.PostConstruct; import java.io.IOException; import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit;  @RestController @RequestMapping("/sse/mvc") public class LongEventController {      private final ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1);      private SseEmitter emitter;      @PostConstruct     public void init() {         scheduledThreadPool.scheduleAtFixedRate(() -> {             try {                 if (emitter != null) {                     emitter.send(UUID.randomUUID().toString());                 }             } catch (IOException e) {                 e.printStackTrace();             }         }, 0, 1, TimeUnit.SECONDS);     }      @GetMapping(path = "/getEvents", produces = MediaType.TEXT_EVENT_STREAM_VALUE)     public SseEmitter getEvents() {         emitter = new SseEmitter();         return emitter;     } }
  效果预览(每秒输出一次):
  处理非周期性事件
  非周期性是指没有固定的时间周期,可能由其他因素在任意时刻都可能触发,下面示例通过spring event来模拟触发因子。@RestController @RequestMapping("/sse/mvc") public class EventController {       private SseEmitter emitter;     @Autowired     private ApplicationContext applicationContext;      /**      *  订阅事件通道      * @return      */     @GetMapping(path = "/event", produces = MediaType.TEXT_EVENT_STREAM_VALUE)     public SseEmitter event() {         emitter = new SseEmitter();         return emitter;     }      /**      *  模拟某一事件触发动作      * @param eventType      */     @GetMapping(path = "/trigger")     public void trigger(String eventType) {         applicationContext.publishEvent(new MyEvent(eventType));     }      /**      *  监听动作,发送给客户端数据      */     @EventListener(classes = MyEvent.class)     public void triggerEvent(MyEvent event) throws IOException {         emitter.send(event);     } }
  效果:
  模拟触发动作:调用 http://localhost:8080/sse/mvc/trigger?eventType=customer
  客户端收到数据:
  SSE Java 服务端: Spring Web Flux介绍
  Spring Web Flux 框架 5.2.0 是基于 Reactive Streams API 且使用  event-loop  计算模型来实现异步java应用程序。 此类应用程序可以在非阻塞 Web 服务器(例如 Netty 4.1 和 Undertow 1.4)和 Servlet 3.1+ 容器(例如 Tomcat 8.5 和 Jetty 9.3)上运行。 概述
  使用 Spring Web Flux 框架实现发送事件: 使用 @RestController 注解创建一个控制器类(Controller) 创建一个方法来创建一个客户端连接,它返回一个  Flux ,处理 GET 请求并产生( produces )文本/事件流  (text/event-stream) 创建一个新的 Flux对象且在方法中返回它
  简单示例: @RestController public class ExampleController      @GetMapping(path="/sse", produces=MediaType.TEXT_EVENT_STREAM_VALUE)     public Flux createConnectionAndSendEvents() {         return Flux.just("Alpha", "Omega");     } }
  处理短暂的周期性事件流
  和上面spring mvc的示例一样,也是每秒输出数据,实现如下: @GetMapping(path = "/words", produces = MediaType.TEXT_EVENT_STREAM_VALUE)     public Flux getWords() {         return Flux                 .zip(Flux.just(WORDS), Flux.interval(Duration.ofSeconds(1)))                 .map(Tuple2::getT1);     }Flux.interval(Duration.ofSeconds(1)) 表示 每秒钟发出一次递增的 long 值 通过 zip 方法将它们组合在一起以输入 map(Tuple2::getT1) 表示 提取元组的第一个元素
  效果:
  处理长期持续的周期性事件
  对比spring mvc的实现,我们改为flux实现,如下: @GetMapping(path = "/getEvents", produces = MediaType.TEXT_EVENT_STREAM_VALUE)     public Flux getEvents() {         return Flux                 .interval(Duration.ofSeconds(1))                 .map(sequence -> UUID.randomUUID().toString());     }
  效果和上面是一样的,可以看出,reactive api是非常的简洁。
  SSE的缺点或限制它是单向的,只能由服务端发送给客户端 只发送文本消息;尽管可以使用 Base64 编码和 gzip 压缩来发送二进制消息,但效率很低。 许多浏览器允许打开数量非常有限的 SSE 连接(Chrome、Firefox 每个浏览器最多 6 个连接)

美元强势是暂时的,人民币汇率必回升今年,随着美联储多次激进加息导致强势美元回归,世界各国货币都呈现了不同程度的贬值,英镑欧元兑美元汇率都出现历史新低。近日,人民币汇率也跌破7整数关口,达到7。2,创近20年新低,刷上诉落败!Google将面临41亿美元罚款去年一场轰轰烈烈的开放网络运动,将GoogleChrome浏览器取消Cookie的计划推上风口浪尖。如今,欧盟普通法院驳回了谷歌因安卓系统垄断市场而被处罚数十亿欧元的上诉,将支付4新华财经新能源配储能经济账怎么算?新华社北京10月3日电(记者安娜陈云富)当前我国储能正呈快速多元化发展趋势。与此同时,成本与商业模式问题依然困扰行业。新能源配储能经济账应该怎么算?记者近期对此展开调查。储能正从电露脐上衣加浅灰色打底裤,越来越受欢迎了,小姐姐性感指数爆表上身穿的是款豆沙色的短袖,豆沙色这个颜色现在越来越受到小姐姐们的喜爱,因为它有着非常好的衬托肤色的作用,特别显白。圆领的短袖,款式比较普通,但短款的设计,使得小姐姐露出了略微有点肉36!20冠王倒下,哈兰德创52年纪录追平C罗,曼城创历史超梅西2日晚,80多岁的老爵爷现身英超,他心系曼联,特意来看比赛,弗格森曾带领曼联拿过13次英超冠军奖杯,而曼联也曾豪取英格兰顶级联赛20次冠军,本场比赛,曼联的对手是曼城,滕哈赫和瓜迪国庆假期首日,韶山各景点接待游客逾万人次一切为了人民主题展成新景国庆假期首日,韶山各景点接待游客逾万人次华声在线10月1日讯(全媒体记者蒋睿通讯员王无冕)10月1日,韶山毛泽东广场红旗猎猎,游人如织。广场一侧设立的一切为吉利集团收购阿斯顿马丁7。6股份!打造国产新能源超跑?说起国内芸芸汽车集团之中,富有国际观的汽车品牌,吉利汽车必然是其中之一,只因旗下除了拥有路特斯沃尔沃极星Polestar以及英伦电动汽车LEVC等品牌以外,也是梅赛德斯奔驰的第二大湘财证券肝病创新疗法破局在即掘金巨大Unmet蓝海市场智通财经APP获悉,湘财证券发布研究报告称,肝病因病理复杂十几年来治疗药物更新节奏缓慢,甚至在NASH领域治疗药物仍为空白,肝病领域存在巨大的Unmet临床需求。同时因产品管线老化多地晒出国庆首日旅游成绩单本地人游本地成主流国庆小长假进入第二天。澎湃新闻(www。thepaper。cn)注意到,10月2日,多地发布了假期首日的旅游成绩单。据川观新闻报道,2022年国庆假期首日,四川省统筹抓好安全生产疫现场直击!国庆首日,市民竞相广场打卡留影北京日报客户端记者魏婧我爱你祖国!祖国万岁!清晨6时许,伴随雄壮的国歌和东方一缕朝霞,数万名市民游客齐聚天安门广场,共同见证五星红旗冉冉升起,喜迎祖国73岁华诞。6时11分,国旗升喜迎二十大科技创新指标十年大幅跃升!经开区打造具有全球影响力的技术创新示范区全球首个14价HPV疫苗全球首个获批进入临床试验的干细胞外用药全球首个开源开放智能网联路侧单元操作系统中国首个本土孵化和获批的全球首创原研药党的十八大以来越来越多全球全国首创正在北
新生儿居家护理皮肤1胎脂宝宝出生后,皮肤覆盖一层灰白色胎脂,有保护皮肤保暖的作用。生后数小时会被吸收,褶皱处胎脂宜用温开水轻轻擦去。2新生儿红斑常在生后12天内出现,皮疹呈大小不等边缘不清的斑丘疹,奋战203天!云天化10万吨年电池用磷酸二氢铵项目产品正式下线近日,云天化年产10万吨年电池用磷酸二氢铵项目建设现场传来捷报,装置全系统化工投料试车成功,实现电池用磷酸二氢铵合格产品正式下线,标志着云天化在融入发展新战略迈入新能源发展赛道实施国产户外羽绒服棉服冲锋衣哪家强?带你全面了解10款国货服装产品上周还是秋季气温的长江中下游地区,一夜突然降温,最低温度达到零度左右,有些地区还下起了零星小雪。一线大牌的羽绒服棉服冲锋衣,价格都几千起步,甚至上万元,性价比不高。咱们大众消费者可贝利夸他是天才,四届世界杯他打进16球,马特乌斯坦言他不可抵挡卡塔尔世界杯,小组赛中,德国队不敌日本队,这让无数德国球迷愤怒,高配置的德国战车怎么如此不堪一击?这让无数德国球迷想到了他们曾经引以为傲的足球英雄米洛斯拉夫。克洛泽。有克洛泽在,德英格兰vs塞内加尔静态基本面星光熠熠的三狮军团风头正劲,在小组赛中未损一兵一卒轻松晋级,经历了过往两届大赛打磨,英格兰成绩有目共睹。相比较仅能凭借小组末轮使出浑身解数才压哨晋级的塞内加尔。索斯盖特运国乒女神因恋爱被开除,如今男方即将接班李隼,她却成了无名氏国乒女神因恋爱被开除,如今男方即将接班李隼,她却成了无名氏不允许队员之间恋爱是国乒队一条极其严格的队规,但青春懵懂的年轻男女每日都重复着枯燥的训练生活,怎么可能不衍生出一些其他的情压力山大33分雷霆7人上双135128战胜森林狼队亚历山大,NBA第三大得分手,场均31分,本周早些时候因为臀部受伤缺席了一场比赛。但在对阵森林狼的比赛中,他的身体素质毫不费力,17投10中,12罚全中。乔希吉迪得到21分和12次肖申克的救赎永远要在绝境中寻找希望2022年世界杯开幕已经有几天时间了,但开幕式摩根弗里曼的出场,让很多球迷们眼前一亮。这个80多岁的老演员,承包了几代人的荧幕印象。而最让我印象深刻的,还是那部由他主演的肖申克的救赵本山老了搂不住了?他的又一徒弟离婚,且是二度离婚在国内有两大传统艺术团体,一个是赵本山旗下的本山传媒,一个是郭德纲旗下的德云社,前者以二人转著称,后者以相声闻名。在师父的带领下,两大艺术团体的徒弟们都赚的盆满钵满,且成为国内知名曹格前妻离婚后首晒儿女近照,母子母女欢度周末,一切如常超惬意12月3日下午,曹格前妻吴速玲在个人社交平台分享了一组和儿女欢度周末的温馨合照秀幸福。这也是她官宣与曹格离婚后,首度晒与孩子们的生活点滴,备受外界关注。吴速玲还发文写道周末最爱叫妹SpringBoot结合XXLJOB实现定时任务前言上篇文章我们介绍了Quartz的使用,当时实现了两个简单的需求,不过最后我们总结的时候也提到Quartz有不少缺点,代码侵入太严重,所以本篇将介绍xxljob这个定时任务框架。