4

Hangfireでバックグラウンド ジョブをエンキューおよび処理するときに、再現可能なメモリ不足の例外が発生する可能性があります。

ジョブは単純なConsole.WriteLine呼び出しであるため、ヒープメモリがそのように増加するとは思いません。

構成が間違っていましたか、それとも問題を報告することを検討する必要がありますか?

結果 ( VMMap )

ジョブのバッキング ストレージとして Redis を使用する:

  • 開始時の総ヒープ = 29,088K。
  • 5,000 ジョブの後、938,672K。
  • 6,000 ジョブ、1,056,004K。
  • 7,000 ジョブ、1,219,296K。
  • 8,000 ジョブ、ヒープ値は存在しません。
  • さらに 100 ジョブ以内に、iisexpress.exeインスタンスがクラッシュします。

SQL ストレージを使用すると、制限はさらに高くなります ~= 15,000 ジョブ。

設定

  • 空の ASP.NET プロジェクト。
  • IIS ホスティングと Hangfire 用の Owin パッケージをインストールします。
  • スタートアップ クラスとコントローラー。

パッケージ

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Hangfire.Core" version="1.6.6" targetFramework="net452" />
  <package id="Hangfire.Pro" version="1.4.7" targetFramework="net452" />
  <package id="Hangfire.Pro.PerformanceCounters" version="1.4.7" targetFramework="net452" />
  <package id="Hangfire.Pro.Redis" version="2.0.2" targetFramework="net452" />
  <package id="Hangfire.SqlServer" version="1.6.6" targetFramework="net452" />
  <package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" />
  <package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
  <package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net452" />
  <package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="1.0.0" targetFramework="net452" />
  <package id="Microsoft.Net.Compilers" version="1.0.0" targetFramework="net452" developmentDependency="true" />
  <package id="Microsoft.Owin" version="3.0.1" targetFramework="net452" />
  <package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net452" />
  <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
  <package id="Owin" version="1.0" targetFramework="net452" />
  <package id="StackExchange.Redis" version="1.1.606" targetFramework="net452" />
</packages>

コントローラ

public class DefaultController : ApiController
{
    static int _;

    [HttpPost]
    public void Post(int count = 1000)
    {
        for (var i = 0; i < count; ++i)
        {
            BackgroundJob.Enqueue(() => Console.WriteLine(_));

            ++_;
        }
    }
}

起動

static class AppSettings
{
    internal static bool   HangfireUseRedis => true;
    internal static int    RedisDatabase    => 0;
    internal static string RedisConnection  => "localhost:6379";

    internal static string SqlConnection    => "Data Source=(localdb)\\v11.0;Initial Catalog=Hangfire";
}

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var config = new HttpConfiguration();

        config.Routes.MapHttpRoute(
            name: "Default",
            routeTemplate: "{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        if (AppSettings.HangfireUseRedis)
        {
            var redisOptions = new RedisStorageOptions
            {
                Database = AppSettings.RedisDatabase,
                Prefix   = "Foobar:"
            };

            GlobalConfiguration.Configuration.UseRedisStorage(AppSettings.RedisConnection, redisOptions);
        }
        else
        {
            GlobalConfiguration.Configuration.UseSqlServerStorage(AppSettings.SqlConnection);
        }

        JobHelper.SetSerializerSettings(new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });

        app.UseHangfireServer();
        app.UseHangfireDashboard();

        app.UseWebApi(config);
    }
}
4

2 に答える 2

9

ミニ ダンプ ファイル (1.2 GB) を受け取った後、プロセスのヒープに関する情報を取得できました。それらのほとんどには興味深いものは何も含まれておらず、サイズも比較的小さいため、最も重要なワンスの抜粋を次に示します。

GC Heap Size:    Size: 0x9f7eb8 (10452664) bytes.
Jit code heap:   Size: 0x1000 (4096) bytes total, 0x905a4d00 (2421837056) bytes wasted.

ご覧のとおり、GC ヒープ サイズは約 10 MB であり、サイズが比較的小さいため、.NET コード自体にリークはありません。しかし、Jit コード ヒープは非常に奇妙に見えるので、プロセスで使用されているモジュールを確認することにし、Stackify Profiler のものを見つけました。

6b0d0000 6b23a000   StackifyProfiler_x86   (deferred)

StackifyIsPrefix=1PEB は、 Stackify Prefix が使用されていることを示す環境変数を示しています。プロファイラーインストルメンテーション目的で JIT コードを変更する可能性があるため、問題を再現するために Stackify Prefix をインストールすることにしました。

簡単な MVC アプリケーションを作成し、Home/Indexアクションを変更して 10000 のバックグラウンド ジョブをキューに入れ、プロファイラーを有効にしました。このステップを実行した後、そのページを取得するのに時間がかかりすぎることがわかりました – 1.5 分、プロファイラーはデータを表示しませんでした. 長すぎました。そこで、プロファイラーをオフにしてタイミングを比較することにしました。わずか5 秒しかかかりませんでした。これは大きな違いですが、メモリの問題を再現できませんでした。

ジョブの数を 100 に減らし、プロファイラーをオンにして、Redis へのすべての呼び出しがカウントされていることに気付きました。Redis への呼び出しには数百のレコードがあります。それらをすべて保存するとメモリの問題が発生する可能性がありますが、Stackify Prefix にどのように保存されるのか正確にはわかりません。

Stackify プレフィックスのスクリーンショット

元のメモリの問題を再現できませんでした。ただし、Stackify Prefix は、その期間を長くすることで実行に大きな影響を与えます。Stackify Prefix プロファイラーを無効にして、テストを再実行しようとしましたか? 更新されたバージョンでは、メモリの問題も修正される場合があります。

于 2016-11-03T10:01:34.227 に答える