[toc]
前言
之前无聊做了一款工具app,需要使用到流式响应
因为想着快速启动,所以选择了客户端用uniapp 服务端用uncloud云开发
后来在实现unicloud流式响应给uniapp时,发现没法自定义响应,unicloud的函数只能返回数据,响应动作只能由框架操作,将你云函数响应的数据进行send。
方案一:Uni-push
后来查官网,发现也是有解决方案就是开通他们提供的uni-push服务,其中有一个信道对象ssechannel
我们可以在客户端创建信道,给信道绑定监听几种监听事件函数,比如信道接收消息事件、信道结束事件。
绑定了事件函数后,我们就可以把信道传给后端,后端拿到反序列化后,就可以通过send发送消息,通过end结束信道
这样在客户端那边的两个事件就可以接收到。
整天来说使用也很简单,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| export default { data() {}, onLoad() {}, methods: { async testSSE() { const channel = new uniCloud.SSEChannel() channel.on('message', (message) => { console.log('on message', message); }) channel.on('end', (message) => { console.log('on end', message); }) await channel.open() const res = await uniCloud.callFunction({ name: 'myapi', data: { channel: channel } }) } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| exports.main = async (event, context) => { const channel = uniCloud.deserializeSSEChannel(event.channel) await channel.write({ a: 1 }) await channel.write({ a: 2 }) await channel.write({ a: 3 }) await channel.write({ a: 4 }) await channel.end({ a: 5 }) return {} };
|
在使用之后,也是实现了我的功能,接口实时写入,客户端可以相对实时接收。
但是后来发现一个问题:可能是且后台或者锁屏,或者就在使用中,各种各样的情况吧,可能会出现客户端接收不到了的情况,而且一但出现了这种情况后,客户端再第二次第三次触发testSSE…..也就是后续使用均不会收到消息。testSSE被触发,依然是新建信道传递给后端api,api依然进行写入没有任何异常,但是客户端就是监听不到,只能重启就好了。一但出现问题又只能重启,猜测可能是这个组件依赖在全局上有什么东西,一但断开就只能重启了。
方案二:接口迁移腾讯云开发
断断续续查了几周没解决,想到迁移接口吧,于是乎到腾讯云也是开了一个云开发,接口的逻辑代码都调通,准备进行流式响应时,又又遇到了之前的问题,它没给我原始响应对象,不能进行自定义响应。于是乎问了下客户客服说不支持(这点比起uncloud还是很好的,有技术在线可以问)

方案三:接口迁移Serverless
于是乎我又去开通了serverless之前没用过,发现它好像介于传统开发和云开发之间,它的代码就是原技术的代码,比如你选着nodejs 或者 java 得到的就是你正常开发的目录结构,不像云开发还会封装一层有一定学习成本。它控制台就是一个在线vscode,代码以及目录结构就是你选用的技术,写完直接部署就行了。会给一个公网地址映射到你的项目服务端口。因为就是原代码不像云开发的封装了一层,所以很简单就能实现流式响应标准接口,不管是什么后端技术本身都是支持http全部类型的,只是云开发封装了一层没给你原response对象
本来以为到这里就结束了,但是没想到客户端又不支持,本来从来没有想过客户端不支持的,以为解决了接口就行了。
客户端使用了三种,第一种uniapp提供的uni.request有个enableChunked: true的配置开启,但是好像只是支持小程序。到app就找不到监听函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const response = uni.request({ url: 'Your requested address', method: "Request method", data: data, header: { "Accept": 'text/event-stream', "Connection": "keep-alive" }, responseType: "text", enableChunked: true, success: (res) => { resolve(res) }, fail: (err) => { reject(err) }, }) response.onHeadersReceived((e)=>{ console.log(e); })
response.onChunkReceived((e)={ console.log(e); })
|
第二种是fetch也是js自带的,在uniapp环境还是找不到
第三种用xhr完全没反应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| const xhr = new XMLHttpRequest(); let buffer = '';
xhr.open('POST', url, true); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader('Accept', 'text/event-stream');
xhr.onprogress = function() { const newData = xhr.responseText.substr(buffer.length); console.log(newData); buffer += newData; console.log(buffer); };
xhr.onreadystatechange = function () { if (xhr.readyState === XMLHttpRequest.LOADING) { console.log(xhr.responseText) } }
xhr.onload = function() { if (xhr.status === 200) { resolve(buffer); } else { reject(new Error(`HTTP error! status: ${xhr.status}`)); } };
xhr.onerror = function() { console.log("error") };
xhr.send(data ? JSON.stringify(data) : null);
|
后两种基本上就是想尝试用原生js的方式,也是失败告终.
方案四:换协议Websocket
既然不行的话(虽然很离谱不应该不支持的呀),现在就只能用websocket了,这个uniapp客户端应该支持吧。OKOK那再把接口改成websocket的,然后把输出的地方都替换。
然后改下客户端连接websocket,不同的平台uni提供的接口函数可能都不一样,目前测试在鸿蒙上ok
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| let socket = uni.connectSocket({ url: 'wss://you_api_url', success: function (res) { console.log('WebSocket 连接创建成功'); }, fail: function (err) { console.error('WebSocket 连接创建失败', err); }, complete: function () { console.log('WebSocket 连接创建完成'); } }); socket.onOpen(function () { console.log("连接到了") socket.send({ data: 'Hello from uni-app!' }); });
socket.onMessage(function (res) { console.log('收到服务器内容:' + JSON.stringify(res)); });
socket.onClose(function (res) { console.log('WebSocket 已关闭!'); });
socket.onError(function (res) { console.error('WebSocket 连接错误!'); });
|
总结
总体来说有点沙雕,既然用websocket的话,那之前的云开发后端也应该可以,等于说绕了一大圈最终其实也可以不迁移这个接口。但问题是本来我的需要并不是一定要用websocket只是单向流式传输就行了,现在因为客户端的问题必须要用websocket,要是一开始无脑用websocket反而不用卡那么久有点逗了