Angular Universal 应用避免 SSR hang 的一些指导方针

简介: Angular Universal 应用避免 SSR hang 的一些指导方针

当某些异步任务永远挂起时,Angular SSR 渲染可能永远不会完成,例如对后端 API 的 http 调用。


目前 Spartacus SSR 实现里,已经采取了一些措施来改善渲染挂起时的监控体验:


我们添加了配置 SsrOptimizationOptions.maxRenderTime.


在 maxRenderTime 时间之后,我们登录控制台,能看到显示渲染时间过长并且可能挂起的提示消息,但我们对此无能为力。


理想情况下,我们应该尽可能避免 render 进程出现 hang 的情况。


首先,最好调查一下 SSR 中出现 never-ending 的异步操作的最常见原因是什么,然后尝试阻止它们。


一种合理的猜测是,在大多数情况下,这是由于一个永不响应的 HTTP API 调用造成的。

如果情况确实如此,在 SSR 模式下,我们可以为所有 API 调用引入通用的 http 超时,因此它们最终会返回控制权给 SSR 服务器,因此 SSR 服务器可以继续进行其他请求的响应工作。


Angular Universal 的一个局限就在于,无法以编程方式中止挂起的渲染并释放分配的资源。


一种解决方案是,我们可以引入一个 Angular HTTP_INTERCEPTOR,它可以使长时间未决的网络请求超时,因此在服务器端启动的应用程序的生命周期更短。


换句话说:因此 SSR 渲染不会因为等待来自网络的缓慢 API 响应而“挂起”。 但是,这可能需要在应用程序代码甚至 SSR 代码中添加额外的逻辑,这样此类格式错误的渲染(基于不完整的数据)不会在 SSR 响应中返回。 在这种情况下,最好回退到具有无缓存标头的 CSR 应用程序,而不是允许发送格式错误的呈现 html 作为响应(并可能由 CDN 缓存)。


下面是一个具体的例子:


import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { RequestTimeoutHttpInterceptor, DEFAULT_TIMEOUT } from './interceptors';
import { AppComponent } from './app.component';
@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule,
  ],
  declarations: [
    AppComponent,
  ],
  providers: [
    { provide: HTTP_INTERCEPTORS, useClass: RequestTimeoutHttpInterceptor, multi: true },
    { provide: DEFAULT_TIMEOUT, useValue: 5000 },
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
import { Injectable, InjectionToken, Inject } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { empty, TimeoutError } from 'rxjs';
import { timeout, catchError } from 'rxjs/operators';
export const DEFAULT_TIMEOUT = new InjectionToken<number>('defaultTimeout');
@Injectable({
  providedIn: 'root'
})
export class RequestTimeoutHttpInterceptor implements HttpInterceptor {
  constructor(
    @Inject(DEFAULT_TIMEOUT) protected defaultTimeout: number,
  ) { }
  intercept(req: HttpRequest<any>, next: HttpHandler) {
    const modified = req.clone({
      setHeaders: { 'X-Request-Timeout': `${this.defaultTimeout}` }
    });
    return next.handle(modified).pipe(
      timeout(this.defaultTimeout),
      catchError(err => {
        if (err instanceof TimeoutError)
          console.error('Timeout has occurred', req.url);
        return empty();
      })
    );
  }
}
相关文章
|
12月前
|
开发框架 前端开发 测试技术
什么是 Angular 企业级应用开发中的 Breaking Change
什么是 Angular 企业级应用开发中的 Breaking Change
|
5月前
|
缓存 JavaScript 前端开发
Angular PWA 应用什么情况下会出现 504 error
Angular PWA 应用什么情况下会出现 504 error
|
11月前
|
JavaScript 前端开发 API
关于 Angular Universal 应用渲染两次的问题
关于 Angular Universal 应用渲染两次的问题
|
11月前
|
JSON 搜索推荐 数据格式
Angular SSR 应用中 serverApp-state script 的工作原理介绍
Angular SSR 应用中 serverApp-state script 的工作原理介绍
|
缓存 监控 API
Angular Universal 应用避免 SSR hang 的一些指导方针
Angular Universal 应用避免 SSR hang 的一些指导方针
|
12月前
|
缓存 前端开发 JavaScript
Commands and Queries 在 Angular 应用开发中的使用场合
Commands and Queries 在 Angular 应用开发中的使用场合
|
12月前
|
存储 JavaScript 前端开发
关于 Subscription 在 Angular 开发中的应用
关于 Subscription 在 Angular 开发中的应用
|
存储 缓存 JavaScript
Angular Universal 学习笔记
Angular Universal 学习笔记
|
JavaScript
使用 Angular Universal 进行服务器端渲染避免 window is not defined 的错误消息
使用 Angular Universal 进行服务器端渲染避免 window is not defined 的错误消息
使用 Angular Universal 进行服务器端渲染的防御性编程思路
使用 Angular Universal 进行服务器端渲染的防御性编程思路