Asp.Net Core中利用過濾器控制Nginx的緩存時間

来源:https://www.cnblogs.com/simendancer/archive/2023/02/10/17109964.html
-Advertisement-
Play Games

前言 Web項目中很多網頁資源比如html、js、css通常會做伺服器端的緩存,加快網頁的載入速度 一些周期性變化的API數據也可以做緩存,例如廣告資源位數據,菜單數據,商品類目數據,商品詳情數據,商品列表數據,公共配置數據等,這樣就可以省去很多在服務端手動實現緩存的操作 最早資源緩存大部分都用Ex ...


前言

Web項目中很多網頁資源比如html、js、css通常會做伺服器端的緩存,加快網頁的載入速度

一些周期性變化的API數據也可以做緩存,例如廣告資源位數據,菜單數據,商品類目數據,商品詳情數據,商品列表數據,公共配置數據等,這樣就可以省去很多在服務端手動實現緩存的操作

最早資源緩存大部分都用Expires、Cache-Control或Etag實現的,我們可以在WebServer中統一設置響應頭,或者指定規則單獨設置

以上都是基於Http協議的緩存,如今很多WebServer,例如Nginx和阿裡二次開發的Tengine,都是自己的一套緩存實現,通過獨有的響應頭參數(X-Accel-Expires)來識別控制緩存,優先順序是大於Http協議那些的

通常Nginx都是作為代理伺服器,反向代理多台源伺服器,如果開啟了緩存,二次請求到了Nginx就會直接響應給客戶端了,能減輕源伺服器的壓力

本文主要是基於 X-Accel-Expires 來實現緩存的,前提是在Nginx中已經配置了Proxy Cache規則

 

Nginx的緩存原理

1. 這是資源訪問路徑,通過Nginx反向代理多個源伺服器,Nginx中配置了緩存,第二次訪問到了Nginx就直接返回了,不會再到後面的源伺服器

2. 常見的Http緩存響應頭設置有以下幾種,其中Etag和Last-Modified是組合使用的,X-Accel-Expires是Nginx獨有的參數,優先順序高於其他幾個設置,值的單位是秒,0為不生效

Nginx緩存識別優先順序如下

3. Nginx實現緩存的原理是把Url和相關參數,通過自定義組合作為Key,並使用MD5演算法對Key進行哈希,把響應結果存到硬碟上的對應目錄,支持通過命令清除緩存

具體可以參考以下文章,非常詳細:

https://www.nginx.com/blog/nginx-high-performance-caching/

https://czerasz.com/2015/03/30/nginx-caching-tutorial/

 

代碼實現

以下是通過過濾器實現控制該參數,支持在Controller或Action上傳入滑動時間,或者固定時間,靈活控制緩存時間

    /// <summary>
    /// 配合nginx緩存
    /// </summary>
    [AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Method,AllowMultiple = false)]
    public class NginxCacheFilterAttribute : Attribute, IAsyncActionFilter
    {
        /// <summary>
        /// 構造函數
        /// </summary>
        public NginxCacheFilterAttribute() { }
        
        /// <summary>
        /// 固定時間格式正則,例如:00:00 、10:30
        /// <summary>
        static Regex reg = new Regex(@"^(\d{1,2}):(\d{1,2})$",RegexOptions.IgnoreCase);

        /// <summary>
        /// 緩存清除固定時間,new string[] { "00:00", "10:00", "14:00", "15:00" }
        /// </summary>
        public string[] MustCleanTimes { get; set; }

        /// <summary>
        /// 緩存清除滑動時間,預設 300 (5分鐘)
        /// </summary>
        public int Period { get; set; } = 300;

        /// <summary>
        /// 請求頭變數
        /// </summary>
        const string X_Accel_Expires = "X-Accel-Expires";
        const string ETag = "ETag";
        const string Cache_Control = "Cache-Control";

        /// <summary>
        /// 過濾器執行
        /// </summary>
        /// <param name="context"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public Task OnActionExecutionAsync(ActionExecutingContext context,ActionExecutionDelegate next)
        {
            //非GET請求,不設置nginx緩存頭
            if (context.HttpContext.Request.Method.ToUpper() != "GET") {
                return next.Invoke();
            }
            var response = context.HttpContext.Response;
            //判斷固定時間
            if (MustCleanTimes != null && MustCleanTimes.Length > 0) {
                var nowTime = DateTime.Now;                  //當前時間
                var nowYmd = nowTime.ToString("yyyy-MM-dd"); //當前日期
                List<DateTime> cleanTimes = new List<DateTime>();
                foreach (var time in MustCleanTimes) {
                    if (reg.IsMatch(time) && DateTime.TryParse($"{nowYmd} {time}",out DateTime _date)) {
                        //已超時的推到第二天,例如設置的是00:00,刷新時間就應該是第二天的00:00
                        if (_date < nowTime)
                            cleanTimes.Add(_date.AddDays(1));
                        else
                            cleanTimes.Add(_date);
                    }
                }
                if (cleanTimes.Count > 0) {
                    var nextTime = cleanTimes.OrderBy(o => o).FirstOrDefault(); //下次刷新時間
                    var leftSeconds = nextTime.Subtract(nowTime).TotalSeconds;  //下次刷新剩餘秒數
                    if (leftSeconds >= 0 && leftSeconds < Period)
                        Period = (int)leftSeconds;
                }
            }

            //添加X_Accel_Expires
            if (response.Headers.ContainsKey(X_Accel_Expires)) {
                response.Headers.Remove(X_Accel_Expires);
            }
            response.Headers.Add(X_Accel_Expires,Period.ToString());

            //添加ETag
            if (response.Headers.ContainsKey(ETag)) {
                response.Headers.Remove(ETag);
            }
            response.Headers.Add(ETag,new System.Net.Http.Headers.EntityTagHeaderValue($"\"{DateTime.Now.Ticks.ToString()}\"",true).ToString());

            //移除Cache-Control
            response.Headers.Remove(Cache_Control);

            return next.Invoke();
        }
    }

 具體的使用方式如下:

1. 全局用法,全局Api都是設置的預設緩存時間,不需要緩存的Api在Controller或Action上單獨設置Period=0即可

//在Stratup中全局添加過濾器       
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(config => {
        config.Filters.Add<NginxCacheFilterAttribute>();
    });
}


/// <summary>
/// 設置滑動時間
/// Period=0為不生效
/// </summary>
/// <returns></returns>
[HttpGet]
[NginxCacheFilter(Period = 0)]
public HttpResponseMessage TestCache1()
{
    return new HttpResponseMessage() { StatusCode = System.Net.HttpStatusCode.OK };
}

 2. 局部用法

/// <summary>
/// 設置滑動時間
/// 30秒後自動過期
/// </summary>
/// <returns></returns>
[HttpGet]
[NginxCacheFilter(Period = 30)]
public HttpResponseMessage TestCache1()
{
    return new HttpResponseMessage() { StatusCode = System.Net.HttpStatusCode.OK };
}

/// <summary>
/// 設置固定時間
/// 例如:9點第一次請求,一直緩存到10點失效,12點第一次請求,一直緩存到15點失效
/// </summary>
/// <returns></returns>
[HttpGet]
[NginxCacheFilter(MustCleanTimes = new[] { "10:00","15:00","22:00" })]
public HttpResponseMessage TestCache2()
{
    return new HttpResponseMessage() { StatusCode = System.Net.HttpStatusCode.OK };
}

 

具體效果

1. 我們第一次請求介面,返回200狀態碼,Nginx在響應頭上會返回X-Cache:MISS,代表緩存未命中

2. 第二次請求,會返回304狀態碼,Nginx在響應頭上會返回 X-Cache:HIT,代表已經命中緩存

3. 我們開啟Chrome調試中的Disable Cache,這樣所有請求的請求頭中都會設置 Cache-Control: no-cache,再刷新下介面看下

發現介面返回200狀態碼,Nginx在響應頭上會返回X-Cache:EXPIRED,說明緩存已過期,已從源伺服器返回了數據,也說明通過請求頭設置Cache-Control為no cache是可以跳過緩存的

更多含義:

 

高性能用法:

proxy_cache_lock:緩存鎖

proxy_cache_lock_timeout:緩存鎖過期時間

如果給緩存規則設置了proxy_cache_lock,那麼該規則下同時進來多個同一個Key的請求,只會有一個請求被轉發到後面的源伺服器,其餘請求會被等待,直到源伺服器的內容被成功緩存

可以配合設置proxy_cache_lock_timeout,設置一個緩存鎖的過期時間,這樣其餘請求如果等待超時了,也會被釋放請求到後面的源伺服器

通過這兩個參數的組合使用,可以有效避免同一個請求大量打入時,瞬間壓垮後面的源伺服器

 

原創作者:Harry

原文出處:https://www.cnblogs.com/simendancer/articles/17109964.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.什麼是函數模版 函數模板,實際上是建立一個通用函數,其函數類型和形參類型不具體制定,用一個虛擬的類型來代表。這個通用函數就成為函數模板 2.怎麼編寫函數模版 //T代表泛型的數據類型,不是只能寫T, template<class T>//讓編譯器看到這句話後面緊跟著的函數里有T不要報錯 void ...
  • 2023-02-10 一、配置SSM環境 1、添加日誌文件 在“shf-parent/web-admin/src/main/resources”下創建“logback.xml” <?xml version="1.0" encoding="UTF-8"?> <configuration debug=" ...
  • 教程簡介 批處理腳本語法 - 從簡單和簡單的步驟學習批處理腳本,從基本到高級概念,包括概述,環境,命令,文件,語法,變數,註釋,字元串,數組,決策,操作符,日期和時間,輸入/輸出,返回代碼,函數,進程,別名,設備,註冊表,網路,列印,調試,日誌記錄。 教程目錄 批處理腳本 - 語法 批處理腳本 - ...
  • 參考:https://blog.csdn.net/weixin_42401159/article/details/112187778 https://cloud.tencent.com/developer/article/1406445 在處理一些自然語言文字的過程中,會遇到一些錶面很奇怪的現象。 ...
  • 一、安裝 kafka是由scala語言寫成的,後面用Java重構了,但是不管怎樣,都要編譯到jvm虛擬機中執行。 centos:yum install java-11-openjdk ubuntu:apt install default-jdk java -version 下載kafka 下載 wg ...
  • 大數據時代,各行各業對數據採集的需求日益增多,網路爬蟲的運用也更為廣泛,越來越多的人開始學習網路爬蟲這項技術,K哥爬蟲此前已經推出不少爬蟲進階、逆向相關文章,為實現從易到難全方位覆蓋,特設【0基礎學爬蟲】專欄,幫助小白快速入門爬蟲,本期為爬蟲的基本介紹。 一、爬蟲概述 爬蟲又稱網路蜘蛛、網路機器人, ...
  • 教程簡介 C#概述 - 從簡單和簡單的步驟學習C#從基本到高級概念,包括概述,環境設置,程式結構,基本語法,數據類型,類型轉換,變數,常量,運算符,決策,迴圈,方法,Nullables ,數組,字元串,結構,枚舉,文件I / O,類,封裝,介面,繼承,命名空間,多態性,運算符重載,封裝,反射,屬性, ...
  • 首先我們需要瞭解到分散式事件匯流排是什麼; 分散式事件匯流排是一種在分散式系統中提供事件通知、訂閱和發佈機制的技術。它允許多個組件或微服務之間的協作和通信,而無需直接耦合或瞭解彼此的實現細節。通過事件匯流排,組件或微服務可以通過發佈或訂閱事件來實現非同步通信。 例如,當一個組件完成了某項任務並生成了一個事件 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...