【ASP.NET MVC】 Azureで公開してるサイトのレスポンスがだいぶ良くなったのでやったことのメモ


ASP.NET MVCをAzure環境で使ってます

こんなサイトを作成しています。
しかし機能拡張をしていく中でどんどんサイトが重くなってしまいました。
このままでは遅いからだれにも必要とされないサイトになってしまいそうだったので低レイテンシをめざして改修しました。
目標はgoogleの推奨する200ms以下のディレイ。
(2018/05/02追記)PageSpeed Insightsで99/97達成したので追記します。adsense/analytics付きなのでここらが限界かなと思います。
(2018/05/18)Azure http2対応したのでサイトに反映。
TestMySiteで2秒がでるようになりました。
しかしPageSpeed Insightsで99/97達成出来なくなりましたorz

参考サイトを見ながら少しずつ進めました。

1. Linq to SQLでIncludeを使う

基本的なことですができてませんでした。。。
HogeのIndexページでHugaクラスの属性も表示する場合
以下のようにIncludeするとDBから一度にHogeとHugaを読み込むため
レスポンスが良くなります。

HogeRepository.cs
    public List<Hoge> ToList() {
        var q = from s in db.Hoges
                select s;
        return q.Include(m => m.Huga).ToList();
    }

2.string/intに対してHtml.DisplayForの使用をやめる

一覧画面で合計4000回ぐらいHtml.DisplayForを読んでいたところ
HTML出力が遅くなっていました。VS2015のプロファイラーで
CPU時間を確認するとHtml.DisplayForが原因の一部と判明。
モデルからはstringとintの出力しかしていなかったため
直接出力することにしました。
これは大量にHtml.DisplayForを実行している場合にだけ有効ですね。

転送量を削減すればディレイも短くなります。

3. 出力されるHTMLからスペースと改行を削除する

参考サイトよりWhitespaceFilterAttribute
を拝借してプロジェクトに追加します。
WhitespaceFilterAttributeをControllerにつけると出力されるHTMLのスペースと改行が削除されます。
同じサイトにCompressFilterAttributeも載ってましたが
1つのcontrollerに両方使用するとうまく動かなかったため
私は効果の高かったWhitespaceFilterだけ使用することにしました。
ある一覧画面では500kb出力していたページが200kbまで落とせました。

HogesController.cs
    [WhitespaceFilter]
    public class HogesController : Controller {
        // Index 等の実装・・・
    }

4. JS/CSSを圧縮する

これも基本ですができてませんでした。
Web.Release.configに以下を追記

Web.Release.config
<compilation debug="false" targetFramework="4.5.2" />

5. CSSスプライトを利用する

私は↓のサイトを利用して各ページのリンク画像をCSSスプライトにしました。
参考サイト2

6. レポジトリレベルでのキャッシュを実装する

これはアクセスが集中した場合に有効でした。
HogeクラスにToListという全レコードをリスト形式で取り出すメソッドがあったとして以下のような継承クラスを作成しControllerは
CachedHogeRepository を利用するように変えました。

CachedHogeRepository.cs
    public class CachedHogeRepository : HogeRepository
    {
        private static readonly object CacheLockObject = new object();
        private const string cacheKey = "CachedHogeRepository:ToList";
        public override IList<Hoge> ToList()
        {
            IList<Hoge> result = HttpRuntime.Cache[cacheKey] as List<Hoge>;
            // キャッシュにないの場合
            if (result == null)
            {
                lock (CacheLockObject)
                {
                    result = HttpRuntime.Cache[cacheKey] as List<Hoge>;
                    if (result == null)
                    {
                        result = base.ToList();
                        HttpRuntime.Cache.Insert(cacheKey, result,
                            null, DateTime.Now.AddHours(12), TimeSpan.Zero);
                    }
                }
            }
            return result;
        }
        // 作成更新削除があったらこのメソッドでキャッシュをクリアします。
        public override int SaveChanges()
        {
            var ret = base.SaveChanges();
            HttpRuntime.Cache.Remove(cacheKey);
            return ret;
        }
    }

(ここから追記)

7 link rel preloadを利用する

<link rel="preload" as="script" href='@Scripts.Url("~/bundles/jquery")' />

cssのダウンロード/読み込み中にscriptをダウンロードさせることで時間を節約します。

8 CSSや小さなJavaScriptをインラインにする

public ActionResult InlineCss(string Path)
{
    BundleContext context = new BundleContext(
        new HttpContextWrapper(System.Web.HttpContext.Current),
        BundleTable.Bundles,
        Path);

    Bundle cssBundle = BundleTable.Bundles.GetBundleFor(Path);
    BundleResponse response = cssBundle.GenerateBundleResponse(context);

    return Content(response.Content);
}

私はHomeControllerに上のメソッドを追加し_Layout.cshtml内で下のように読み込むことで
CSSをインラインにしました。

<style type="text/css">
    @Html.Action("InlineCss", "Home", new { Path = "~/Content/css" })
</style>

(ここまで追記)

現在はAzureで以下のようなレスポンスを達成できました。