【.NET Core項目實戰-統一認證平臺】第五章 網關篇-自定義緩存Redis

来源:https://www.cnblogs.com/jackcao/archive/2018/11/14/9960788.html
-Advertisement-
Play Games

" 【.NET Core項目實戰 統一認證平臺】開篇及目錄索引 " 上篇文章我們介紹了2種網關配置信息更新的方法和擴展Mysql存儲,本篇我們將介紹如何使用Redis來實現網關的所有緩存功能,用到的文檔及源碼將會在GitHub上開源,每篇的源代碼我將用分支的方式管理,本篇使用的分支為 。 附文檔及源 ...


【.NET Core項目實戰-統一認證平臺】開篇及目錄索引

上篇文章我們介紹了2種網關配置信息更新的方法和擴展Mysql存儲,本篇我們將介紹如何使用Redis來實現網關的所有緩存功能,用到的文檔及源碼將會在GitHub上開源,每篇的源代碼我將用分支的方式管理,本篇使用的分支為course3
附文檔及源碼下載地址:[https://github.com/jinyancao/CtrAuthPlatform/tree/course3]

一、緩存介紹及選型

網關的一個重要的功能就是緩存,可以對一些不常更新的數據進行緩存,減少後端服務開銷,預設Ocelot實現的緩存為本地文件進行緩存,無法達到生產環境大型應用的需求,而且不支持分散式環境部署,所以我們需要一個滿足大型應用和分散式環境部署的緩存方案。Redis應該是當前應用最廣泛的緩存資料庫,支持5種存儲類型,滿足不同應用的實現,且支持分散式部署等特性,所以緩存我們決定使用Redis作為緩存實現。

本文將介紹使用CSRedisCore來實現Redis相關操作,至於為什麼選擇CSRedisCore,可參考文章[.NET Core開發者的福音之玩轉Redis的又一傻瓜式神器推薦],裡面詳細的介紹了各種Redis組件比較及高級應用,併列出了不同組件的壓力測試對比,另外也附CSRedisCore作者交流QQ群:8578575,使用中有什麼問題可以直接咨詢作者本人。

二、緩存擴展實現

首先本地安裝Redis和管理工具Redis Desktop Manager,本文不介紹安裝過程,然後NuGet安裝 CSRedisCore,現在開始我們重寫IOcelotCache<T>的實現,新建InRedisCache.cs文件。

using Ctr.AhphOcelot.Configuration;
using Ocelot.Cache;
using System;
using System.Collections.Generic;
using System.Text;

namespace Ctr.AhphOcelot.Cache
{
    /// <summary>
    /// 金焰的世界
    /// 2018-11-14
    /// 使用Redis重寫緩存
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class InRedisCache<T> : IOcelotCache<T>
    {
        private readonly AhphOcelotConfiguration _options;
        public InRedisCache(AhphOcelotConfiguration options)
        {
            _options = options;
            CSRedis.CSRedisClient csredis;
            if (options.RedisConnectionStrings.Count == 1)
            {
                //普通模式
                csredis = new CSRedis.CSRedisClient(options.RedisConnectionStrings[0]);
            }
            else
            {
                //集群模式
                //實現思路:根據key.GetHashCode() % 節點總數量,確定連向的節點
                //也可以自定義規則(第一個參數設置)
                csredis = new CSRedis.CSRedisClient(null, options.RedisConnectionStrings.ToArray());
            }
            //初始化 RedisHelper
            RedisHelper.Initialization(csredis);
        }

        /// <summary>
        /// 添加緩存信息
        /// </summary>
        /// <param name="key">緩存的key</param>
        /// <param name="value">緩存的實體</param>
        /// <param name="ttl">過期時間</param>
        /// <param name="region">緩存所屬分類,可以指定分類緩存過期</param>
        public void Add(string key, T value, TimeSpan ttl, string region)
        {
            key = GetKey(region, key);
            if (ttl.TotalMilliseconds <= 0)
            {
                return;
            }
            RedisHelper.Set(key, value.ToJson(), (int)ttl.TotalSeconds); 
        }

        
        public void AddAndDelete(string key, T value, TimeSpan ttl, string region)
        {
            Add(key, value, ttl, region);
        }

        /// <summary>
        /// 批量移除regin開頭的所有緩存記錄
        /// </summary>
        /// <param name="region">緩存分類</param>
        public void ClearRegion(string region)
        {
            //獲取所有滿足條件的key
            var data= RedisHelper.Keys(_options.RedisKeyPrefix + "-" + region + "-*");
            //批量刪除
            RedisHelper.Del(data);
        }

        /// <summary>
        /// 獲取執行的緩存信息
        /// </summary>
        /// <param name="key">緩存key</param>
        /// <param name="region">緩存分類</param>
        /// <returns></returns>
        public T Get(string key, string region)
        {
            key= GetKey(region, key);
            var result = RedisHelper.Get(key);
            if (!String.IsNullOrEmpty(result))
            {
                return result.ToObject<T>();
            }
            return default(T);
        }

        /// <summary>
        /// 獲取格式化後的key
        /// </summary>
        /// <param name="region">分類標識</param>
        /// <param name="key">key</param>
        /// <returns></returns>
        private string GetKey(string region,string key)
        {
            return _options.RedisKeyPrefix + "-" + region + "-" + key;
        }
    }
}

實現所有緩存相關介面,是不是很優雅呢?實現好緩存後,我們需要把我們現實的註入到網關里,在ServiceCollectionExtensions類中,修改註入方法。

/// <summary>
/// 添加預設的註入方式,所有需要傳入的參數都是用預設值
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static IOcelotBuilder AddAhphOcelot(this IOcelotBuilder builder, Action<AhphOcelotConfiguration> option)
{
    builder.Services.Configure(option);
    //配置信息
    builder.Services.AddSingleton(
        resolver => resolver.GetRequiredService<IOptions<AhphOcelotConfiguration>>().Value);
    //配置文件倉儲註入
    builder.Services.AddSingleton<IFileConfigurationRepository, SqlServerFileConfigurationRepository>();
    //註冊後端服務
    builder.Services.AddHostedService<DbConfigurationPoller>();
    //使用Redis重寫緩存
    builder.Services.AddSingleton<IOcelotCache<FileConfiguration>, InRedisCache<FileConfiguration>>();
            builder.Services.AddSingleton<IOcelotCache<CachedResponse>, InRedisCache<CachedResponse>>();
    return builder;
}

奈斯,我們使用Redis實現緩存已經全部完成,現在開始我們在網關配置信息增加緩存來測試下,看緩存是否生效,並查看是否存儲在Redis里。

為了驗證緩存是否生效,修改測試服務api/values/{id}代碼,增加伺服器時間輸出。

[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
    return id+"-"+DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}

增加新的測試路由腳本,然後增加緩存策略,緩存60秒,緩存分類test_ahphocelot

--插入路由測試信息 
insert into AhphReRoute values(1,'/ctr/values/{id}','[ "GET" ]','','http','/api/Values/{id}','[{"Host": "localhost","Port": 9000 }]',
'','','{ "TtlSeconds": 60, "Region": "test_ahphocelot" }','','','','',0,1);
--插入網關關聯表
insert into dbo.AhphConfigReRoutes values(1,2);

現在我們測試訪問網關地址http://localhost:7777/api/values/1,過幾十秒後繼續訪問,結果如下。

可以看出來,緩存已經生效,1分鐘內請求都不會路由到服務端,再查詢下redis緩存數據,發現緩存信息已經存在,然後使用Redis Desktop Manager查看Redis緩存信息是否存在,奈斯,已經存在,說明已經達到我們預期目的。

三、解決網關集群配置信息變更問題

前面幾篇已經介紹了網關的資料庫存儲,並介紹了網關的2種更新方式,但是如果網關集群部署時,採用介面更新方式,無法直接更新所有集群端配置數據,那如何實現集群配置信息一致呢?前面介紹了redis緩存,可以解決當前遇到的問題,我們需要重寫內部配置文件提取倉儲類,使用redis存儲。

我們首先使用redis實現IInternalConfigurationRepository介面,每次請求配置信息時直接從redis存儲,避免單機緩存出現數據無法更新的情況。RedisInternalConfigurationRepository代碼如下。

using Ctr.AhphOcelot.Configuration;
using Ocelot.Configuration;
using Ocelot.Configuration.Repository;
using Ocelot.Responses;
using System;
using System.Collections.Generic;
using System.Text;

namespace Ctr.AhphOcelot.Cache
{
    /// <summary>
    /// 金焰的世界
    /// 2018-11-14
    /// 使用redis存儲內部配置信息
    /// </summary>
    public class RedisInternalConfigurationRepository : IInternalConfigurationRepository
    {
        private readonly AhphOcelotConfiguration _options;
        private IInternalConfiguration _internalConfiguration;
        public RedisInternalConfigurationRepository(AhphOcelotConfiguration options)
        {
            _options = options;
            CSRedis.CSRedisClient csredis;
            if (options.RedisConnectionStrings.Count == 1)
            {
                //普通模式
                csredis = new CSRedis.CSRedisClient(options.RedisConnectionStrings[0]);
            }
            else
            {
                //集群模式
                //實現思路:根據key.GetHashCode() % 節點總數量,確定連向的節點
                //也可以自定義規則(第一個參數設置)
                csredis = new CSRedis.CSRedisClient(null, options.RedisConnectionStrings.ToArray());
            }
            //初始化 RedisHelper
            RedisHelper.Initialization(csredis);
        }

        /// <summary>
        /// 設置配置信息
        /// </summary>
        /// <param name="internalConfiguration">配置信息</param>
        /// <returns></returns>
        public Response AddOrReplace(IInternalConfiguration internalConfiguration)
        {
            var key = _options.RedisKeyPrefix + "-internalConfiguration";
            RedisHelper.Set(key, internalConfiguration.ToJson());
            return new OkResponse();
        }

        /// <summary>
        /// 從緩存中獲取配置信息
        /// </summary>
        /// <returns></returns>
        public Response<IInternalConfiguration> Get()
        {
            var key = _options.RedisKeyPrefix + "-internalConfiguration";
            var result = RedisHelper.Get<InternalConfiguration>(key);
            if (result!=null)
            {
                return new OkResponse<IInternalConfiguration>(result);
            }
            return new OkResponse<IInternalConfiguration>(default(InternalConfiguration));
        }
    }
}

redis實現後,然後在ServiceCollectionExtensions里增加介面實現註入。

builder.Services.AddSingleton<IInternalConfigurationRepository, RedisInternalConfigurationRepository>();

然後啟動網關測試,可以發現網關配置信息已經使用redis緩存了,可以解決集群部署後無法同步更新問題。

四、如何清除緩存記錄

實際項目使用過程中,可能會遇到需要立即清除緩存數據,那如何實現從網關清除緩存數據呢?在上篇中我們介紹了介面更新網關配置的說明,緩存的更新也是使用介面的方式進行刪除,詳細代碼如下。

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Ocelot.Cache
{
    [Authorize]
    [Route("outputcache")]
    public class OutputCacheController : Controller
    {
        private readonly IOcelotCache<CachedResponse> _cache;

        public OutputCacheController(IOcelotCache<CachedResponse> cache)
        {
            _cache = cache;
        }

        [HttpDelete]
        [Route("{region}")]
        public IActionResult Delete(string region)
        {
            _cache.ClearRegion(region);
            return new NoContentResult();
        }
    }
}

我們可以先拉去授權,獲取授權方式請參考上一篇,然後使用HTTP DELETE方式,請求刪除地址,比如刪除前面的測試緩存介面,可以請求http://localhost:7777/CtrOcelot/outputcache/test_ahphocelot地址進行刪除,可以使用PostMan進行測試,測試結果如下。

執行成功後可以刪除指定的緩存記錄,且立即生效,完美的解決了我們問題。

五、總結及預告

本篇我們介紹了使用redis緩存來重寫網關的所有緩存模塊,並把網關配置信息也存儲到redis里,來解決集群部署的問題,如果想清理緩存數據,通過網關指定的授權介面即可完成,完全具備了網關的緩存的相關模塊的需求。

下一篇開始我們開始介紹針對不同客戶端設置不同的許可權來實現自定義認證,敬請期待,後面的課程會越來越精彩,也希望大家多多支持。


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

-Advertisement-
Play Games
更多相關文章
  • Input Output 題意:給你一顆樹,選擇一個三個點構成的集合,使得這三個點不在一條直線上(意思就是 從一個點出發,用一條不回頭的線不能將這三個點連起來)問一共有多少個這樣的集合 思路 :先求出一共有多少個集合,就是Cn3 (n-2)*(n-1)*n/6 ; 然後再求不符合條件的個數 求不符合 ...
  • 本項目是一個通用響應式管理後臺,導入開發環境安裝就能直接運行,界面也非誠漂亮,在PC端和移動端也是自適應的。非常適合企業或者個人搭建各種商城後臺,博客後臺,網站管理後臺等。 源碼啟動後的截圖 需要這套源碼的朋友聯繫作者微信(同電話):18629374628 ...
  • 一.ORM欄位 | 類型 | 說明 | | | | | AutoField | 一個自動增加的整數類型欄位。通常你不需要自己編寫它,Django會自動幫你添加欄位:\ ,這是一個自增欄位,從1開始計數。如果你非要自己設置主鍵,那麼請務必將欄位設置為\ 。Django在一個模型中只允許有一個自增欄位, ...
  • 引入模塊的方式: 1. import 模塊 2. from xxx import 模塊 一、collections 模塊 1.Counter() counter是一個計數器,主要用來計數,計算一個字元串中每個字元出現的次數 1 from collections import Counter 2 s ...
  • Python中的邏輯運算符 not, and, or and 與運算 兩者為真則為真 >>>True and True True 其中一個為假,則為假 >>>True and False False or 或運算 兩者為假則為假 >>>False or False False 其中一個為真,則為真 ...
  • ASP.NET -- 一般處理程式ashx 如果在一個html頁面向伺服器端請求數據,可用ashx作為後臺頁面處理數據。ashx適合用作數據後臺處理,相當於WebForm中的aspx.cs文件或aspx.vb文件。 入門案例:html頁面向ashx頁面請求數據,ashx作為後臺頁面返回數據。 前端h ...
  • 在上一篇博文《 "[UWP]不那麼好用的ContentDialog" 》中我們講到了ContentDialog在複雜場景下使用的幾個令人頭疼的弊端。那麼,就讓我們在這篇博文里開始愉快的造輪子之旅吧! 首先要向大家說明:這篇博文主要還是寫的構建Picker時的思考過程,如果不感興趣的,可以直接略過這篇 ...
  • 問題如圖所示: 解決辦法: 1. 啟動iis(internet information services)服務。 2. 打開左側網站列表=> 右鍵點擊自己配置的網站 => 點擊管理網站 => 點擊 瀏覽 3. 回到vs 重新打開進程列表,可以看到 w3wp.exe 進程已啟動。 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...