轮询机制解决后端任务回调问题

更新日期: 2018-11-10阅读: 3.4k标签: 机制

问题

同事大概描述了这样一个需求:

现在有一个需求,前端有一个按钮,点击以后会调用后端一个接口,这个接口会根据用户的筛选条件去hadoop上跑任务,将图片的base64转为img然后打包成zip,生成一个下载连接返回给前端,弹出下载框。

hadoop上的这个任务耗时比较久,一般都是10s以上,也就是说如果一直让前端等待,会出现请求超时的问题。


需求现阶段实现

以下是现阶段的实现状况:

用户点击下载按钮后,会把以下筛选条件传到后端,如图:
<

后端在收到请求后,在去hadoop上跑任务前会向数据库插入这么一条数据:

idnamestatusurl
114969800626520

name - 以生成任务时的时间戳命名
status - 任务状态, 0代表任务正常执行,1代表任务执行完成
url - 打包后zip的下载地址, status为0时,url为空,status为1时,url有对应下载地址

同事做到这里就卡住了。


解决方案

以生成任务时的时间戳命名真的好吗?

以生成任务时的时间戳命名使得生成的任务和任务的内容没有任何联系,举个例子:同样的筛选条件,用户每次去下载的时候都会在hadoop上重新跑一个任务,生成一条新数据。实际上,同样的筛选条件打包出来的数据应该是一样的,没必要每次下载都去重跑一次任务,造成不必要的时间损耗。更直观来讲,也就是说2个用户使用相同的筛选条件,但是他们创建下载任务的时间不同,导致了他们都在hadoop创建了一个任务和数据库插入了一条数据,实际上他们各自插入的数据除了虽然name和url不同,但是url下载下来的文件中的内容是一样的。

我的解决方案是将所有筛选条件拼接在一起,然后encodeURIComponent后作为唯一标志。

let name = [startTime, endTime, sid, client, sign, cuid, number, result];
name = encodeURIComponent(name.join(''));

这样做也就是说同样筛选条件下,name的标志具有唯一性,避免任务重跑。
如果用户在下载zip的时候之前有人创建过相同条件的任务,那么则无许等待,直接就可以进行下载。


后端接口部分设计

再来设计后端接口,根据分析任务一共有三种状态,对应status值如下

  • 未建立任务————- -1
  • 任务运行中————- 0
  • 任务结束,生成url—- 1

接口伪代码如下

public function download () {
  $startTime = $this->input->post('startTime');
  $endeTime = $this->input->post('endeTime');
  $sid = $this->input->post('sid');
  $client = $this->input->post('client');
  $sign = $this->input->post('sign');
  $cuid = $this->input->post('cuid');
  $number = $this->input->post('number');
  $result = $this->input->post('result');
  $name = $this->input->post('name');
  $status = getTaskStatus($name); 
  $data = array(
    'status' => $status,
    'url'    => ''
  );
  if ($status == -1) {
    establishTask($startTime, $endTime, $sid, $cuid, $number, $result); // 去hadoop上跑任务
    insertSQL($name, 0); 
  } else if ($status === 0) {
  } else if ($status === 1) {
    $data['url'] = getTaskUrl($name);
  }
  echo json_encode($data);
}

getTaskStatus用于从数据库中获取任务状态,当数据库中没有与$name相匹配的数据时则返回-1,有则返回对应的status。

接下来进行判断:

若$status为-1,表明该筛选条件的请求是第一次出现,establishTask创建hadoop任务的同时insertSQL在数据库中插入一条name为$name,status为0的数据。(establishTask在任务结束时会自动修改数据库中的status为1,同时插入url)

若$status为0,表明该筛选条件对应的任务正在hapdoop上运行。

若$status为1,表明该筛选条件对应的任务已经完成,通过getTaskUrl从数据库中取得url。

最后将data返回给前端。


前端轮询机制

前端需要做的就是根据后端返回的结果来判定是否需要继续请求,也就是所谓的轮询,根据setInterval来进行实现。

伪代码如下

const download = () => {
  let interval = setInterval(() => {
    let {
      startTime,
      endTime,
      sid,
      client,
      sign,
      cuid,
      number,
      result
    } = $scope;
    let name = [startTime, endTime, sid, client, sign, cuid, number, result];
    name = encodeURIComponent(name.join(''));
    let data = {
      startTime,
      endTime,
      sid,
      client,
      sign,
      cuid,
      number,
      result,
      name
    };
    $http({
      method: 'POST',
      url: '/downlaod',
      data: data,
      headers:{'Content-Type': 'application/x-www-form-urlencoded'},    
      transformRequest: function (data) {
        return $.param(data);
      }
    })
    .then(res => {
      if (res.status === 1) {
        clearInterval(interval);
        window.location.href = res.url;
      }
    }, error => {
      console.log(error);
    })
  }, 2000);
}

时间设置为2s一次轮询,当response中的status为1即获得下载地址,此时通过clearInterval取消interval,关闭轮询。


页面刷新带来的影响

如果hadoop 速度极慢,长时间没反应,用户可能会以为页面卡顿了,从而进行页面的刷新。

由于采取了上面同筛选条件下任务标识唯一的方法,即使刷新页面后,用户再点击下载相同条件的任务后也不会再去handoop上重新跑任务以及插入新的数据,如果任务还在running则等待,任务已经结束则直接下载。

如果以最初的时间戳为name,则会导致任务重跑,用户得重新开始等待。


关于为什么不引入socket.io?

交流中我曾询问过目前项目中是否还有其余与此相似的功能,但据了解暂时只有这一个需求,所以虽然socket.io相比轮询来说更节约性能,但是没有必要为了一个功能而引入一个库,这样做的感觉是得不尝试。这样做的行为类似于你为了使用underscore中的某个方法而引入整个underscore。


关于轮询机制和websocket的形象对比

轮询机制

客户端:服务器,你有没有消息要给我啊?
服务器:有。
客户端:服务器,你有没有消息要给我啊?
服务器:没有。
——————————无限重复————————————
客户端:服务器,你有没有消息要给我啊?
服务器:没有。
客户端:服务器,你有没有消息要给我啊?
服务器:有


websocket

客户端:服务器,你有我的消息了记得call我。
服务器:OK!
——————————当有消息的时候————————
服务器:有你的消息了,客户端。
客户端:收到。


来源:https://blog.shanamaid.top/2017/06/07/轮询机制解决后端任务回调问题/


链接: https://www.fly63.com/article/detial/1310

浅析前端页面渲染机制

作为一个前端开发,最常见的运行环境应该是浏览器吧,为了更好的通过浏览器把优秀的产品带给用户,也为了更好的发展自己的前端职业之路,有必要了解从我们在浏览器地址栏输入网址到看到页面这期间浏览器是如何进行工作的

这一次,彻底弄懂 JavaScript 执行机制

javascript是一门单线程语言,Event Loop是javascript的执行机制.牢牢把握两个基本点,以认真学习javascript为中心,早日实现成为前端高手的伟大梦想!

创建js hook钩子_js中的钩子机制与实现

钩子机制也叫hook机制,或者你可以把它理解成一种匹配机制,就是我们在代码中设置一些钩子,然后程序执行时自动去匹配这些钩子;这样做的好处就是提高了程序的执行效率,减少了if else 的使用同事优化代码结构

小程序的更新机制_如何实现强制更新?

在讲小程序的更新机制之前,我们需要先了解小程序的2种启动模式,分别为:冷启动和热启动。小程序不同的启动方式,对应的更新情况不不一样的。无论冷启动,还是热启动。小程序都不会马上更新的,如果我们需要强制更新,需要如何实现呢?

基于JWT的Token认证机制实现及安全问题

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。其JWT的组成:一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。

web前端-JavaScript的运行机制

本文介绍JavaScript运行机制,JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。

JavaScript预解释是一种毫无节操的机制

js代码执行之前,浏览器首先会默认的把所有带var和function的进行提前的声明或者定义:1.理解声明和定义、2.对于带var和function关键字的在预解释的时候操作不一样的、3.预解释只发生在当前的作用域下

js对代码解析机制

脚本执行js引擎都做了什么呢?1.语法分析 2.预编译 3.解释执行。在执行代码前,还有两个步骤;语法分析很简单,就是引擎检查你的代码有没有什么低级的语法错误 ,查找全局变量声明(包括隐式全局变量声明,省略var声明),变量名作全局对象的属性,值为undefined

web认证机制

以前对认证这方面的认识一直不太深刻,不清楚为什么需要token这种认证,为什么不简单使用session存储用户登录信息等。最近读了几篇大牛的博客才对认证机制方面有了进一步了解。

浏览器JS事件触发机制

事件会从最外层开始发生,直到最具体的元素,也就是说假如父元素与子元素都绑定有点击事件,又互相重叠,那么先出发的会是父元素的事件,然后再传递到子元素。事件会从最内从的元素开始发生,再向外传播,正好与事件捕获相反。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!