【问题标题】:Angular2/RXJS - Handling Potentially Long QueriesAngular2/RXJS - 处理潜在的长查询
【发布时间】:2016-07-17 15:50:11
【问题描述】:

目标:应用程序的前端允许用户从本地机器中选择文件,并将文件名发送到服务器。然后,服务器将这些文件名与位于服务器上的文件进行匹配。然后服务器将返回所有匹配文件的列表。

问题:如果您的用户选择的文件少于几百个,这会非常有用,否则会导致响应时间过长。我不想限制用户可以选择的文件数量,也不想担心前端的 http 请求超时。

到目前为止的示例代码:

//html on front-end to collect file information
<div>
    <input (change)="add_files($event)" type="file" multiple>
</div>

//function called from the front-end, which then calls the profile_service add_files function
//it passes along the $event object
add_files($event){

    this.profile_service.add_files($event).subscribe(
        data => console.log('request returned'),
        err => console.error(err),
        () => //update view function
    );       
}

//The following two functions are in my profile_service which is dependency injected into my componenet
//formats the event object for the eventual query
add_files(event_obj){

        let file_arr = [];
        let file_obj = event_obj.target.files;

        for(let key in file_obj){
            if (file_obj.hasOwnProperty(key)){
                file_arr.push(file_obj[key]['name'])
            }
        }

        let query_obj = {files:title_arr};

        return this.save_files(query_obj)
}

//here is where the actual request to the back-end is made
save_files(query_obj){

    let payload = JSON.stringify(query_obj);
    let headers = new Headers();

    headers.append('Content-Type', 'application/json');
    return this.http.post('https://some_url/api/1.0/collection',payload,{headers:headers})
        .map((res:Response) => res.json())
}

可能的解决方案:

  1. 批量处理请求。重新编写代码,以便一次仅使用 25 个文件调用配置文件服务,并在每次响应时再次使用接下来的 25 个文件调用配置文件服务。如果这是最好的解决方案,是否有一种优雅的方法可以用 observables 做到这一点?如果没有,我将使用应该可以正常工作的递归回调。

  2. 让端点立即返回通用响应,例如“文件匹配正在上传并保存到您的配置文件”。由于所有匹配的文件都保存在后端的数据库中,这将起作用,然后我可以让前端每隔一段时间查询数据库以获取匹配文件的当前列表。这看起来很难看,但我想我会把它扔在那里。

欢迎任何其他解决方案。如果能以一种优雅的方式使用 angular2/observables 处理这种类型的持久查询,那就太好了。

【问题讨论】:

  • 瓶颈在哪里?定位文件的过程?还是响应的大小太大?
  • 瓶颈是查找匹配文件并发送响应所需的时间。如果用户尝试匹配几千个文件,则可能需要几分钟时间。
  • 如果瓶颈在文件匹配过程中,何不试试缓存、多线程等加速呢?如果响应很大,即您实际上返回了所有文件,那么我可能会尝试分页或加载更多机制?还是不接受分页?
  • 你可以使用 websockets 吗?没有超时。
  • 让我看看 websockets 看看这是否是一个可行的选择。 @sdfacre 谢谢你的建议,但我的目标是找到一种方法来处理较长的响应时间,而不是找到最小化响应时间的方法。我过度简化了问题以使其保持主题,但实际上查找实际上是一组复杂的数据库查询,已经进行了相当多的优化。此外,响应对象包含文件名,而不是文件本身,因此大小不是问题。

标签: javascript api http angular rxjs


【解决方案1】:

我建议改用 websocket 连接,因为它们不会超时。

另请参阅 - https://www.npmjs.com/package/angular2-websocket - http://mmrath.com/post/websockets-with-angular2-and-spring-boot/ - http://www.html5rocks.com/de/tutorials/websockets/basics/

另一种方法是轮询,客户端在定义的时间间隔内重复请求以从服务器获取当前处理状态。

发送多个请求并等待所有请求完成

getAll(urls:any[]):Observable {
  let observables = [];
  for(var i = 0; i < items.length; i++) {
    observables.push(this.http.get(urls[i]));
  }
  return Observable.forkJoin(observables);
}

someMethod(server:string) {
  let urls = [
   '${server}/fileService?somedata=a',
   '${server}/fileService?somedata=b',
   '${server}/fileService?somedata=c'];

  this.getAll(urls).subscribe(
      (value) => processValue(val),
      (err) => processError(err),
      () => onDone());
}

【讨论】:

  • 同意,websockets 将解决超时问题,因此这是一个有效的建议。但是在前端批量处理请求,或者使用websockets和批量处理的组合也是如此。一个很好的答案是解释处理这些请求的各种方法,并包含一个代码示例,展示如何使用 angular2/observables 在前端实现。
  • 其实我认为这个问题对于 StackOverflow 来说太宽泛了。
  • 同意,这可能有点过于宽泛了。至少,我愿意接受一个答案,该答案显示了一种使用可观察对象发送多个请求的好方法和/或显示如何在 Angular2 中创建一个服务来处理多个请求并在收到所有数据时通知组件。跨度>
【解决方案2】:

我建议您将搜索的文件数量分解为可管理的批次,然后在返回结果时处理更多文件,即解决方案 #1。以下是未经测试但我认为完成此任务的相当优雅的方式:

add_files(event_obj){

    let file_arr = [];
    let file_obj = event_obj.target.files;

    for(let key in file_obj){
        if (file_obj.hasOwnProperty(key)){
            file_arr.push(file_obj[key]['name'])
        }
    }

    let self = this;
    let bufferedFiles = Observable.from(file_arr)
        .bufferCount(25); //Nice round number that you could play with

    return bufferedFiles

       //concatMap will make sure that each of your requests are not executed
       //until the previous completes. Then all the data is merged into a single output
       .concatMap((arr) => {

         let payload = JSON.stringify({files: arr});
         let headers = new Headers();
         hearders.append('Content-Type', 'application/json');

         //Use defer to make sure because http.post is eager
         //this makes it only execute after subscription
         return Observable.defer(() => 
            self.post('https://some_url/api/1.0/collection',payload, {headers:headers})
       }, resp => resp.json());
}

concatMap 将阻止您的服务器执行超过缓冲区大小的任何内容,方法是在前一个请求返回之前阻止新请求。如果您希望它们全部并行执行,您也可以使用mergeMap,但如果我没记错的话,似乎服务器是这种情况下的资源限制。

【讨论】:

  • 处理批处理请求的非常有趣的方式。我目前已按照以下链接进行设置:stackoverflow.com/questions/36432280/…。您能否确认 concatMap 方法是否比使用 flatmap 更好?代码的结构看起来确实更简单,更容易理解。
  • 是的,此解决方案比您链接的解决方案更可取。我看到另一个答案来自哪里,它试图保持接近您的原始代码,但同时它试图强制 RxJS 适应不同的模式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-15
  • 2017-08-05
  • 1970-01-01
  • 1970-01-01
  • 2016-10-25
相关资源
最近更新 更多