graviti
产品公开数据集应用市场解决方案知识库关于我们

如何使用SSE进行通信

发布于2021-08-18

作者:小鹿清孑明,格物钛前端部门,前端工程师

前段时间在Tensorbay数据集管理平台前端开发过程中,需要实现一个action 中的 activity log 实时输出的feature,很突然的接触到了一个新技术,迫不及待地想要和大家分享一下!

也就拖了2个月不到吧,SSE这不就来了嘛!

请添加图片描述

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<string>('');
  const [event, setEvent] = React.useState<any>(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' \

效果图这里这里:

请添加图片描述

好滴感谢大家耐心读完,

下次再见啦!