通用技术 ajax 轮询在压测平台中的应用

虫兵 · 2017年11月13日 · 最后由 虫兵 回复于 2017年11月14日 · 1504 次阅读

背景介绍

由于近期压测平台的开发,期中设计到的一个功能点就是要实时监控任务的运行状态一旦改变要及时在页面更新状态,首先压测用例的运行状态是保存在服务端那么客户端如何做到实时监控呢?初步想来无非两种式:

  1. 服务端主动推数据
  2. 客户端每隔一段时间主动拉数据 ### 服务端主动推数据 这种方式主要是给予 HTTP 的长连接的 ‘服务器推’ 技术,能使服务器主动异步的方式向客户端推数据,我研究过以下的实现方式
    • ajax 的长轮询(long-polling)方式
    • 服务器端会阻塞请求直到有数据传递或超时才返回。
    • 客户端 JavaScript 响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接
    • 当客户端处理接收的数据、重新建立连接时,服务器端可能有新的数据到达;这些信息会被服务器端保存直到客户端重新建立连接,客户端会一次把当前服务器端所有的信息取回。
    • 实现模型如图

代码实现

@app.route("/getRunStatus",methods = [ "POST", "GET" ] )
def get_run_status():
    try:
        #服务端每隔3s返回一次数据,此时线程一直是挂起状态直到有结果数据返回给客户端
        while 1:
            rid =request.form.get( "id", "")
            time.sleep(3)
            #获取客户端需要返回的信息
            results = RelayPressCaseSql().select_press_cases_by_sid(rid)
            if results.run_status == 2 or  results.run_status==3 or results.run_status==1:
                 return jsonify(msg="success",rstatus=results.run_status)
    except Exception as e:
        return jsonify(msg="get_run_status fail")
        logger.exception('get_run_status fail')

前端代码

//前端ajax 请求
function setTimePostInfo(){
    $.ajax({
          url : "/getRunStatus",
          type : "post",
          dataType : "json",
          data: {
              id:rid
          },
          success : function(data) {
                var resData = data.rstatus
                //一旦服务端有响应数据,此时模拟处理客户端逻辑
                alertresData 
            },
            error:function(data){
                alertmsg(data.msg);
                console.log(data.msg);
            }
        });
}

遇到的问题
此种方式我在项目中遇到的弊端就是在同一个 Html 页面中发起一个长轮询请求后服务端会挂起当前线程,此时客户端一直处于等待响应阶段,由于 html 刷新机制是单线程,所以就会影响同一个 html 页面中的其他请求都会被挂起如图所示

客户端定时拉数据

  1. 所谓的 Ajax 轮询,其实就是定时的通过 Ajax 查询服务端,客户端按规定时间定时像服务端发送 ajax 请求,服务器接到请求后马上返回响应信息并关闭连接。这种技术方式实现起来非常简单,但是这种方式会有非常严重的问题,就是需要不断的向服务器发送消息询问,这种方式会对服务器造成极大的性能浪费。
  2. 还有一个类似的轮询是使用 JSONP 跨域请求的方式轮询,在实现起来有差别,但基本原理都是相同的,都是客户端不断的向服务器发起请求。
  3. 优点 实现简单。
  4. 缺点 这是通过模拟服务器发起的通信,不是实时通信,不顾及应用的状态改变而盲目检查更新,导致服务器资源的浪费,且会加重网络负载,拖累服务器。
  5. 但是 简单看似没有什么优点的东西,存在即是合理,就像 TCP 和 UDP 俩哥俩一样你能说 UDP 是没用的嘛?答案是否定的,所以基于我项目情况虑,有以下几点,我选择这种方式:
    • 压测平台压没有并发用户的场景
    • 数据更新时延没有严格要求-,10s 的时延我都可以接受,嘿嘿嘿~~
    • 完美解决了我同一个 html 页面处理其他请求的需求
    • 每隔 10 几秒让服务器处理个任务简直就可以忽略不计~~因为我的施压服务和被压服务都是和策略服务分离的~欧耶~
  6. 实现模型
  7. 代码实现

    @app.route("/getRunStatus",methods = [ "POST", "GET" ] )
    def get_run_status():
    try:
        #服务端每隔3s返回一次数据
    
      rid =request.form.get( "id", "")
      time.sleep(3)
      #获取客户端需要返回的信息
      results = RelayPressCaseSql().select_press_cases_by_sid(rid)
      if results.run_status == 2 or  results.run_status==3 or results.run_status==1:
        return jsonify(msg="success",rstatus=results.run_status)
    except Exception as e:
        return jsonify(msg="get_run_status fail")
        logger.exception('get_run_status fail')
    

    前端代码

    //前端ajax 请求
    function setTimePostInfo(){
    $.ajax({
          url : "/getRunStatus",
          type : "post",
          dataType : "json",
          data: {
              id:rid
          },
          success : function(data) {
                var resData = data.rstatus
                //一旦服务端有响应数据,此时模拟处理客户端逻辑
                if (resData ==1){
                      //模拟轮询处理结束逻辑
                    }else{
                        //递归实现客户端定时轮询
                        setTimeout(function(){ setTimePostInfo(); }, 8000);
                    }
            },
            error:function(data){
                alertmsg(data.msg);
                console.log(data.msg);
            }
        });
    }
    

    总结

    其实还有很多其他的实现方式,下面只做一个方向总结,具体问题还是要具体分析,具体技术选择,如下:

    • websocket 方式(WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议)
    • Comet 的方式 优点: 实时性好(消息延时小);性能好(能支持大量用户) 缺点: 长期占用连接,丧失了无状态高并发的特点。
    • 基于 Iframe 及 htmlfile 的流(streaming)方式

博主信息

签名:一个喜欢阿伦艾弗森的测试开发工程师
QQ:294040201
喜欢就点个赞,转个发,关个注吧~
博主博客:http://blog.csdn.net/a11en_03

共收到 7 条回复 时间 点赞

自顶

不知道想表达什么。。。实时推送不管是轮询还是双工通信的 ws 都已经很成熟了,对于测试平台这种级别的用谁都是差不多的效果。
就你所谓的 ajax 轮询 其实可以优化的地方太多。 比如可以判断 API Page Visibility 来使 用户没有置顶你的页面的时候不做无用的数据拉取,还有就是通过长轮询(延迟返回)而不是 setTimeout 来设置轮询间隔。

反正建议你还是找一些开源的轮询库使用比较妥当

心向东 回复

首先多些建议 ~不过请仔细阅读 我的场景设计 ws 的我知道 之前没用过为了快点结束 所以学习 至于长轮询我已经提到了我弃用的理由,工具的初衷是适用面对的是内部用户所以没做更多的用户 目的是尽快的投入使用和提高效率 只要能快速有效的解决我们的问题我觉得都是最好的 技术没有什么简单和难都是用来解决问题的~ 谢谢

虫兵 回复

ws 只是用 http 的一部分实现的(第一次握手),不会阻塞你其他请求的。

Karaser 回复

是的 没去看 ws 相关的东西 只为尽快投入使用~ 下来可以去看看

可能我语文水平实在是不咋地。 我没太看明白。 首先既然已经前后端分离了,为什么不用 vue,react 或者 angular2 呢?这些前端框架里有大把的数据绑定机制辅助你做页面动态展示。 我个人用的是 angular2,非常好用。 还有轮询为什么不用 setInterval,页面销毁的时候直接 clearInterval 就好了, 搞 setTimeout 是为什么呢,我没搞懂。 一般的 task 类任务都是后端异步处理,前端动态轮询。 api 层 sleep 3 秒是为啥。。。 我实在是没太看懂。。。 我自己用 angular2 做前端 flask 做 api 层的时候这种问题都不是问题啊。。。为啥你要搞这么复杂

孙高飞 回复

说的对 可能其他前端框架 确实很好的处理这个问题 只是为了快速把它搞定 没有去学习,于是就用了最简单有效的方式!加不加 sleep 完全看你心情 我不需要很高的实时性所以加了,至于 setTimeout 和 setInterval 一个是延迟执行一个是一段时间自动循环执行,这个到是在我实现功能上没有决定性 必须用那个

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册