Как определить утечку памяти в очень маленьком угловом приложении - PullRequest
2 голосов
/ 12 апреля 2019

В настоящее время я работаю над приложением Angular, которое должно работать 24 часа в сутки как минимум месяц (производство программного обеспечения). Клиент принимает только то, что перезапуск браузера происходит только раз в месяц (интервал обслуживания). Первый вариант использования, который мы реализуем, содержит только один компонент, который отображает некоторую информацию для пользователя. В данный момент нет взаимодействия с пользователем! Информация передается с сервера на клиент. В настоящее время я просто опрашиваю обновления данных с сервера и показываю информацию пользователю.

Текущий интервал 200 мс только для исследовательских целей, в реальном сценарии он будет 1000 мс. Приведенный ниже код вызывает увеличение памяти примерно на 40 МБ в течение 3 часов в Chrome, а также увеличение использования процессора до 50% (при использовании одного из двух ядер).

Целевой технологией для push-уведомлений является SignalR. Поскольку я обнаружил проблему с памятью с помощью SignalR, представленная здесь реализация опроса используется для выяснения, является ли библиотека SignalR проблемой. К сожалению, у меня та же проблема.

Конечно, выполнение window.location.reload () каждые 30 минут "решает" проблему, но это не очень хорошее решение. Если я выполню перезагрузку через 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 часа в сутки, по крайней мере, месяц, не имея проблем с памятью и использованием процессора.

Ответы [ 2 ]

1 голос
/ 12 апреля 2019

Непонятно, что течет (и если оно течет), поэтому трудно посоветовать что-то конкретное. Но вот несколько советов:

1) Попробуйте удалить ненужные Subjects, вы можете просто выставить Observable 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, чтобы ограничить время запроса, и определенно добавить некоторую обработку ошибок, cz будут ошибки на 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, что приведет к ошибкам. Поэтому обработка ошибок здесь обязательна.

Вот более подробный обзор обработки ошибок в rxjs .

Пример Stackblitz для вышеупомянутого .

ПОБОЧНОЕ ПРИМЕЧАНИЕ: Вы не знаете, что может произойти, если вы работаете круглосуточно в течение месяца. Так что вы определенно захотите добавить некоторые записи в вашу систему. Просто чтобы иметь возможность учиться, если это не удается.

0 голосов
/ 12 апреля 2019

Согласно документам rxjs :

Интервал

возвращает 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, я бы определенно использовал вместо этого веб-сокет. Взгляните на socket.io , это позволяет очень просто запустить простой сервер сокетов.

...