美文网首页项目经验
websocket实现进度条

websocket实现进度条

作者: 修行者12138 | 来源:发表于2020-11-14 15:40 被阅读0次

背景

一键导出功能,一次可以导出大量数据,最终导出形式为压缩包。后端需要把生成压缩包的进度实时发给前端

痛点

1.如果用户导出的数据量过大,后端生成压缩包时间过长,超出http请求的时间限制,前端会断开连接
2.用户需要等待较长时间,体验较差

技术方案

1.前端生成uuid,使用uuid作为websocket的userId,开启websocket
2.后端使用ConcurrentHashMap保存userId与对应的session,key为userId,value为session
3.在导出文件的http请求的参数中加入uuid,后端根据uuid找到session,并把文件导出进度通过websocket实时发送到前端

问题

以上方案,在单机系统没有问题,但如果是分布式系统,会有问题。
后端服务通过容器部署,假设是双实例,前端websocket连接请求发到实例A,但是http请求发到了实例B,实例B上找不到userId对应的session,因此发送消息给前端失败。
系统已经做了会话保持,但是是通过cookie(JSESSIONID)实现的,而不是nginx的iphash,因此只能控制http请求发到同一个实例,无法控制websocket连接请求和http请求发到同一个实例

解决方案一

整个导出功能使用websocket进行通信,不使用http请求,所有请求参数通过websocket传输。
注意事项
websocket消息有长度限制,需要修改org.apache.tomcat.websocket.textBufferSize

@Configuration
public class WebAppRootContext implements ServletContextInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        servletContext.addListener(WebAppRootListener.class);
        // websocket消息有长度限制,默认限制是8k个字符,这里改成1024k
        servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize", String.valueOf(1024 * 1024));
    }
}

解决方案二

使用nginx配置iphash实现会话保持

解决方案三

使用kafka解决websocket多实例问题(实例指部署后端服务的容器实例)
1.每个实例维护一个ConcurrentHashMap,保存连接到该实例的userId和session
2.每个实例把要发给客户端的消息(导出进度),通过kafka广播模式发给所有kafka消费者
3.消费者收到消息后,从消息中提取出userId,判断该userId对应的session是否存在于本实例,若存在,则把消息通过websocket发到前端,若不存在,直接舍弃消息(因为session在其他实例)

使用nginx代理websocket的注意事项

1.客户端与服务端之间有nginx时,nginx是七层负载均衡,即在应用层代理真实客户端转发http请求,并代理真实服务端把响应结果返回给客户端,nginx与真实服务端的读超时时间限制默认是60s,也就是超过60s会断开websocket长连接,需要把该时间限制改大一点

#改成10分钟
proxy_read_timeout 600s

2.需要配置把http协议升级为ws协议
Error during WebSocket handshake: Unexpected response code: 404

websocket进度条准确性控制

技术方案

在执行具体业务逻辑之前,就计算好任务量,假设一次导出请求,包含ABCDE五个步骤,那就把任务量定为5,每执行完一个步骤,就把进度增加20%,全都执行完毕后,进度为100%。

问题1

假如五个步骤的耗时不同怎么办,比如ABCDE的耗时比例为6:1:1:1:1

解决方案

根据耗时比例,加权计算任务量,比如把任务量定为10,其中A占6个任务量,完成A后进度增加60%

问题2

假设A步骤耗时1分钟,那么用户看到进度条1分钟不动,然后突然升到60%,体验会非常差。

解决方案

需要把A步骤继续拆分,比如,根据时间区间等入参,count查询可以确定A步骤需要查1w条数据,需要分页查询10次,那么可以把A步骤分成10个子任务,每个任务执行完成后,进度增加60% / 10 = 6%

问题3

问题2的解决方案中,A步骤具体需要怎么拆分,很难在一开始确定,需要执行到具体的代码段(比如count查询)才能确定

解决方案(分治算法)

一开始计算任务量的时候,还是分配6个任务量给A,至于A怎么分配这6个任务量,由A执行到具体代码段的时候再决定。比如执行count查询后发现需要分页查10次,那么每一次分页查询后进度增量为60% / 10 = 6%

相关文章

网友评论

    本文标题:websocket实现进度条

    本文链接:https://www.haomeiwen.com/subject/aqvnbktx.html