" 【.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里,來解決集群部署的問題,如果想清理緩存數據,通過網關指定的授權介面即可完成,完全具備了網關的緩存的相關模塊的需求。
下一篇開始我們開始介紹針對不同客戶端設置不同的許可權來實現自定義認證,敬請期待,後面的課程會越來越精彩,也希望大家多多支持。