我目前正在开发应至少24个月运行24/7的Angular应用程序(制造软件)。 客户端仅接受浏览器每月仅重启一次(维护间隔)。 我们正在实现的第一个用例仅包含一个向用户显示一些信息的组件。 此时没有用户交互!信息从服务器推送到客户端。 目前,我只是从服务器上轮询数据更新,并向用户显示信息。
当前200ms的间隔仅用于研究目的,在实际情况下为1000ms。 下面的代码导致Chrome在3个小时内的内存增加了大约40MB,并且CPU的使用率增加了50%(消耗了两个内核之一)。
用于推送通知的目标技术和SignalR。 由于我已经使用SignalR发现了内存问题,因此这里提供的轮询实现用于调查SignalR库是否是问题。 不幸的是,我在这里遇到同样的问题。
当然,每30分钟执行一次window.location.reload()可以“解决”问题,但这不是一个好的解决方案。 如果我在3小时后执行重新加载,则页面只会崩溃,Chrome会显示“哦,不...崩溃”。 我使用的是Chrome 73和Edge,Edge的内存增加量明显高于Chrome。 使用Angular 7.2
<div *ngIf="info" style="width: 100%; height: 100%; margin: 0 auto;">
<div class="info status{{ info.Status }}">
<div class="location"><p class="font2">{{ info.Location }}</p></div>
<!-- ... further div elements here but no other *ngIf or *ngFor -->
<div *ngIf="info?.Details" class="no-padding">
<div class="column1 no-padding" *ngFor="let item of info.Details">
<div class="inverse"><p class="font1">{{ item.Name }}</p></div>
</div>
</div>
<img class="icon" src="assets/Icon{{info.Icon}}.png"/>
</div>
</div>
</div>
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription, interval, Subject } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { Info } from '../data/info';
@Component({
selector: 'app-info',
templateUrl: './info.component.html',
styleUrls: ['./info.component.scss']
})
export class InfoComponent implements OnInit, OnDestroy {
subscribetimer: Subscription;
subscribelistener: Subscription;
listener: Subject<Info>;
info: Info;
constructor(private http: HttpClient) { }
ngOnInit() {
this.listener = new Subject<Info>();
this.subscribelistener = this.listener.subscribe(unit => this.info = unit);
this.subscribetimer = interval(200)
.subscribe(data => {
this.http.get<Info>(`http://localhost:5000/poll`)
.subscribe(res => this.listener.next(res));
});
}
ngOnDestroy() {
this.subscribetimer.unsubscribe();
this.subscribelistener.unsubscribe();
}
}
我希望我可以将这个小型应用程序24/7至少运行一个月,而不会出现内存和cpu消耗问题。
答案 0 :(得分:1)
目前尚不清楚泄漏的是什么(以及是否泄漏),因此很难建议一些特殊的东西。但是这里有一些提示:
1)尝试删除不必要的Subjects
,您只需向视图公开可观察的info$
:
export class AppComponent {
info$: Observable<IData>;
constructor(private http: HttpClient) { }
ngOnInit() {
this.info$ = interval(200)
.pipe(
exhaustMap(() =>
this.http.get<Info>(`http://localhost:5000/poll`)
)
);
}
}
在视图中,类似:
<div *ngIf="info$ | async as info">
<div *ngFor="let item of info.items">
{{item}}
</div>
</div>
2),您可能在http-get中存在大量超时,例如您的计时器每隔200ms
滴答一次,http-get可能需要500ms
。 exhaustMap
将处理反向压力,但您应添加一个timeout
以限制请求时间,并确定添加一些错误处理,因为http-get上将出现错误。一个非常基本的例子:
this.http.get<Info>(`http://localhost:5000/poll`).pipe(
// killing requests taking too long
timeout(400),
// some error handling logic should go here
catchError(error => {
return EMPTY;
})
)
更复杂的方法可能是将timeout
与retry
结合起来。
除此之外,http响应本身可能是错误的或不是json,这将导致错误。因此,必须在这里进行错误处理。
这里有一个more detailed overview of error handling in rxjs。
Stackblitz example for above said。
侧面注意事项:您不知道24/7全天候运行一个月会发生什么。因此,您当然也想在系统中添加一些日志记录。只是为了学习,如果失败了。
答案 1 :(得分:0)
根据rxjs docs:
interval返回一个Observable,它发射无限个递增的整数序列,并在这些发射之间选择一个固定的时间间隔。第一次发射不会立即发送,而只会在第一个周期过去之后发送。默认情况下,该运算符使用异步SchedulerLike来提供时间概念,但是您可以将任何SchedulerLike传递给它。
我认为问题出在这里:
this.subscribetimer = interval(200)
.subscribe(data => {
this.http.get<Info>(`http://localhost:5000/poll`)
.subscribe(res => this.listener.next(res));
});
基本上,每200
毫秒就要创建一个新的subscribe
函数。这些对象很可能永远不会进行垃圾处理,因此它们加起来会占用内存。
我建议查看代码,以便在收集到回复后正确地退订。
或者,如果您可以控制API服务器,那么我一定会使用Web套接字。看看socket.io,它使启动简单的套接字服务器变得非常简单。