如何使用SSE进行通信
作者:小鹿清孑明, 格物钛前端部门,前端工程师
前段时间在Tensorbay数据集管理平台前端开发过程中,需要实现一个action 中的 activity log 实时输出的feature,很突然的接触到了一个新技术,迫不及待地想要和大家分享一下!
WHAT?什么是SSE?
SSE全称Server-Sent Events,字面意思就是服务器向客户端推送信息。
我们知道,客户端和服务器端通信一般是通过http请求,而http请求无法做到服务器主动推送信息。
但是一种情况除外,那就是客户端告诉服务器端接下来请求的这个信息是“流信息”,一个比较容易理解的例子就是在浏览器中下载文件,当在客户端中出发了一个下载请求之后,客户端会告诉服务端接下来请求“流信息”,服务端会向客户端发送持续不断的信息,而客户端也不会关闭连接,一直等待接收信息。
SSE就是利用这样一种机制,实现了由服务器端向客户端推送信息的功能。
SSE听起来和WebSocket有点像,但是细心的朋友可以发现,SSE是单向通道,只能实现服务端向客户端发送信息,客户端在第一次触发了SSE请求以后就只能接收,并不能回复。
下图表示了SSE的大致流程(笔者亲手临摹作品)
HOW?SSE如何使用?
了解了SSE以后,我们要怎么使用呢?
在客户端中,SSE的所有API都定义在EventSource对象中!
EventSource 是服务器推送的一个网络事件接口。一个EventSource实例会对HTTP服务开启一个持久化的连接,以text/event-stream 格式发送事件, 会一直保持开启直到被要求关闭。一旦连接开启,来自服务端传入的消息会以事件的形式分发至你代码中。如果接收消息中有一个事件字段,触发的事件与事件字段的值相同。如果没有事件字段存在,则将触发通用事件。
构造函数
EventSource()
pc = new EventSource(url, configuration)
url为远程资源位置,configuration为配置新连接提供选项。
属性
- EventSource.onerror: 是一个event handler, 当发生错误的时候被调用
- EventSource.onmessage: 是一个event handler, 当收到一个message的时候被调用
- EventSource.onopen: 是一个event handler, 当建立一个链接的时候被调用
- EventSource.close: 如果链接存在,关闭连接,readyState设为已关闭
- EventSource.readyState: 返回一个只读值, 获取当前链接的状态
- EventSource.url: 返回一个只读值, 获取当前链接的URL
用SSE实现 action 中的 activity log 实时输出
了解了SSE在客户端中的API以后,结合实时输出log的需求,我们可以尝试着使用一下啦。
首先剖析一下需求,需要在选中某个activity的时候,在屏幕右边实时渲染出其log。
那么显然,我们需要准备以下内容:
- 最重要的服务器端的URL
- 获取到message以后的onmessage回调
准备好以后,整理一下逻辑
我们会在页面中维护两个state,一个是EventSource对象,另一个是需要展示出来的log内容data
const [data, setData] = React.useState
(''); const [event, setEvent] = React.useState (null); 在log页面被渲染的时候和后端建立sse连接
const newEvent = new EventSourcePolyfill(`urlValue`, { headers });
当后端推送了message回来的时候,我们改变data的值,实现实时输出的效果
newEvent.onmessage = (e: any) => { logs = logs.concat(`${e.data}\n`); setData(logs); }; newEvent.onerror = () => { newEvent.close(); };
看到这里细心的朋友可能会注意到一个细节!
那就是!
我根本没用到上面提到的EventSource()啊!
用的是一个好像被封装过的和EventSource()
很像但是却不一样的EventSourcePolyfill()啊!
这又是为何啊?!
答案就是
EventSource不支持传入headers,也就无法传给服务端token。那么对于需要token的本资源来讲,不传token等于无证驾驶,不给不给滴,所以我就在万能的gayhub上调研到了EventSourcePolyfill,使用它就可以!传给!服务端!token了!
最终的curl如下:
curl 'https://gas.fat.graviti.cn/gatewayv2/venom-daemon/v1/activity/83639073cb30496c8a5794e9f9ad9276/logs/?&node_id=wf-i5ruzz-qsclf-1529545071&container=main&follow=true' \
-H 'authority: gas.fat.graviti.cn' \
-H 'sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"' \
-H 'accept: text/event-stream' \
-H 'x-token: 7cd82d49be4b468eb746a283121574e9' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36' \
-H 'sec-fetch-site: same-origin' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-dest: empty' \
-H 'referer: https://gas.fat.graviti.cn/dataset/5c2d5481/zj_test/actions/wf/i5ruzz-qsclf?tab=log' \
-H 'accept-language: zh-CN,zh;q=0.9,en;q=0.8' \
-H 'cookie: gr_user_id=a7f2dde3-1fb7-4f4e-8ef4-adf94a0f611e; Hm_lvt_88214dfcc23a7cf3e22c61fc2032b3cc=1624525026; _ga=GA1.1.588941179.1620461955; Hm_lpvt_88214dfcc23a7cf3e22c61fc2032b3cc=1625802074; Hm_lvt_16e30fb4f44e9e645bce6c970883ebce=1625197778,1625638587,1625712443,1625802597; _ga_JMKPTKFK1Q=GS1.1.1625824946.4.0.1625824946.0; Hm_lpvt_16e30fb4f44e9e645bce6c970883ebce=1625985701; X-Token=7cd82d49be4b468eb746a283121574e9; 9383fdc1263bf861_gr_last_sent_sid_with_cs1=46cc67df-d7aa-4e46-9d61-75332ce7016c; 9383fdc1263bf861_gr_last_sent_cs1=bf3f0b777057a26f52b661c1f19eaa86; 9383fdc1263bf861_gr_session_id=46cc67df-d7aa-4e46-9d61-75332ce7016c; 9383fdc1263bf861_gr_session_id_46cc67df-d7aa-4e46-9d61-75332ce7016c=true; 9383fdc1263bf861_gr_cs1=bf3f0b777057a26f52b661c1f19eaa86' \
--compressed
最不一样的就是这里啦:
-H 'accept: text/event-stream' \
-H 'x-token: 7cd82d49be4b468eb746a283121574e9' \
好滴感谢大家耐心读完,走过路过的顺便都点个小爱心吧❤️
下次再见啦!
【关于格物钛】:
格物钛智能科技定位为面向机器学习的数据平台,致力于为 AI开发者打造下一代新型基础设施,从根本上改变其与非结构化数据的交互方式。通过非结构化数据管理工具TensorBay和开源数据集社区Open Datasets,帮助机器学习团队和个人降低数据获取、存储和处理成本,加速AI开发和产品创新,为人工智能赋能千行百业、驱动产业升级提供坚实基础。