<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      spritekuang

      導(dǎo)航

      企業(yè)級(jí)自定義表單引擎解決方案(十)--緩存設(shè)計(jì)2

        新年伊始,萬(wàn)物皆生機(jī),然冠未去,美帝相向,于華夏之子,吾輩當(dāng)自強(qiáng)。

        這篇文章接上一篇文章,主要介紹緩存的代碼實(shí)現(xiàn)

      后端本地緩存

        之前介紹的將自定義表單數(shù)據(jù)全部存儲(chǔ)到應(yīng)用程序內(nèi)存中,任何自定義表單數(shù)據(jù)更新之后,都刷新內(nèi)存緩存,分布式部署涉及到緩存同步刷新問(wèn)題。

      • 全局本地緩存容器設(shè)計(jì)
      1. 用線程安全的字典ConcurrentDictionary<string, object> CacheDict,存儲(chǔ)每一個(gè)數(shù)據(jù)對(duì)象集合,比如視圖集合、表單集合等,每一次數(shù)據(jù)變更都清除具體的一個(gè)字典項(xiàng)數(shù)據(jù)
      2. 絕大多數(shù)時(shí)間都是讀取緩存內(nèi)容,因此這里上的讀寫(xiě)鎖,讀寫(xiě)每一項(xiàng)緩存時(shí),都上自己的讀鎖,鎖的集合存儲(chǔ)在ConcurrentDictionary<string, ReaderWriterLock> CacheReaderWriterLockDict變量中,Key與CacheDict的Key相同。
      3. 當(dāng)檢測(cè)到緩存通知服務(wù)斷開(kāi)時(shí),會(huì)將本地所有緩存清空,直接讀取原始數(shù)據(jù)庫(kù),用bool IsEnabledLocalCache變量控制。
      4. 當(dāng)讀取緩存時(shí),發(fā)現(xiàn)本地緩存沒(méi)有數(shù)據(jù),則調(diào)用具體加載數(shù)據(jù)委托方法,本地沒(méi)有數(shù)據(jù)讀取時(shí),需要加鎖,防止緩存穿透。

      具體代碼如下:

      /// <summary>
          /// 本地緩存容器
          /// </summary>
          public class LocalCacheContainer
          {
              private static ConcurrentDictionary<string, object> CacheDict;
              private static ConcurrentDictionary<string, ReaderWriterLock> CacheReaderWriterLockDict;
      
              static LocalCacheContainer()
              {
                  CacheDict = new ConcurrentDictionary<string, object>();
                  CacheReaderWriterLockDict = new ConcurrentDictionary<string, ReaderWriterLock>();
              }
      
              public static bool IsEnabledLocalCache { get; private set; } = true;
      
              /// <summary>
              /// 緩存通知斷開(kāi)時(shí)調(diào)用
              /// </summary>
              /// <param name="isEnabled">是否啟用緩存</param>
              internal static void SetLocalCacheIsEnabled(bool isEnabled)
              {
                  IsEnabledLocalCache = isEnabled;
                  if(!isEnabled)
                  {
                      ClearAllCache();
                  }
              }
      
              public static object Get(string key, Func<string, object> factory)
              {
                  var readerWriterLock = GetReadWriteLock(key);
                  readerWriterLock.AcquireReaderLock(5000);
      
                  try
                  {
                      //return CacheDict.GetOrAdd(key, factory); // 緩存穿透?
                      if (CacheDict.ContainsKey(key))
                      {
                          return CacheDict.GetOrAdd(key, factory);
                      }
                      else
                      {
                          lock (string.Intern(key))
                          {
                              return CacheDict.GetOrAdd(key, factory);
                          }
                      }
                  }
                  finally
                  {
                      readerWriterLock.ReleaseReaderLock();
                  }
              }
      
              internal static void ClearCache(string key)
              {
                  var readerWriterLock = GetReadWriteLock(key);
                  readerWriterLock.AcquireWriterLock(5000);
      
                  try
                  {
                      object objRemove;
                      CacheDict.TryRemove(key, out objRemove);
                  }
                  finally
                  {
                      readerWriterLock.ReleaseReaderLock();
                  }
              }
      
              // 清楚所有緩存信息
              private static void ClearAllCache()
              {
                  CacheDict.Clear();
                  CacheReaderWriterLockDict.Clear();
              }
      
              private static ReaderWriterLock GetReadWriteLock(string key)
              {
                  return CacheReaderWriterLockDict.GetOrAdd(key, k =>
                  {
                      return new ReaderWriterLock();
                  });
              }
          }
      

      緩存變更處理

      1. 主要分為緩存變更通知與接收緩存變更處理,緩存變更只需要通知哪一個(gè)Key過(guò)期即可。
      2. 接收緩存變更處理比較簡(jiǎn)單,接收到緩存變更之后,將內(nèi)存容器中對(duì)應(yīng)的字典項(xiàng)刪除即可。
      3. 緩存通知定義為接口,如果是單應(yīng)用部署,直接調(diào)用刪除本地緩存服務(wù)即可,如果是分布式部署,也會(huì)調(diào)用刪除本地緩存數(shù)據(jù),通知發(fā)送分布式通知到其他自定義表單應(yīng)用服務(wù)器,其他自定義表單應(yīng)用服務(wù)器接收到緩存變更通知時(shí),刪除本地緩存數(shù)據(jù)。
      • ReceiveCacheNotice代碼
      public static class ReceiveCacheNotice
          {
              public static void ReceiveClearCache(string key)
              {
                  LocalCacheContainer.ClearCache(key);
              }
      
              public static void ReceiveClearCaches(List<string> keys)
              {
                  foreach(var key in keys)
                  {
                      LocalCacheContainer.ClearCache(key);
                  }
              }
      
              public static void SetLocalCacheIsEnabled(bool isEnabled)
              {
                  LocalCacheContainer.SetLocalCacheIsEnabled(isEnabled);
              }
          }
      
      • ICacheSendNotice及本地通知LocalCacheSendNotice代碼
          /// <summary>
          /// 設(shè)計(jì)時(shí)實(shí)體變更通知緩存
          /// </summary>
          public interface ICacheSendNotice
          {
              /// <summary>
              /// 發(fā)送緩存變更
              /// </summary>
              /// <param name="key">緩存Key</param>
              void SendClearCache(string key);
      
              /// <summary>
              /// 發(fā)送緩存多個(gè)變更
              /// </summary>
              /// <param name="key">緩存Key集合</param>
              void SendClearCaches(List<string> keys);
          }
      
          /// <summary>
          /// 本地緩存容器通知服務(wù)
          /// </summary>
          public class LocalCacheSendNotice : ICacheSendNotice
          {
              public void SendClearCache(string key)
              {
                  ReceiveCacheNotice.ReceiveClearCache(key);
              }
      
              public void SendClearCaches(List<string> keys)
              {
                  ReceiveCacheNotice.ReceiveClearCaches(keys);
              }
          }
      
      • 分布式緩存發(fā)布訂閱Redis實(shí)現(xiàn),主要是用StackExchange.Redis組件實(shí)現(xiàn),代碼沒(méi)有太多的邏輯,閱讀代碼即可。
      /// <summary>
          /// Redis緩存容器通知服務(wù)
          /// </summary>
          public class RedisCacheSendNotice : ICacheSendNotice
          {
              private readonly SpriteConfig _callHttpConfig;
              private readonly IDistributedCache _distributedCache;
              private readonly ISubscriber _subscriber;
      
              public RedisCacheSendNotice(IDistributedCache distributedCache, IOptions<SpriteConfig> callHttpConfig)
              {
                  _distributedCache = distributedCache;
                  _callHttpConfig = callHttpConfig.Value;
                  var spriteRedisCache = _distributedCache as SpriteRedisCache;
                  spriteRedisCache.RedisDatabase.Multiplexer.ConnectionFailed += Multiplexer_ConnectionFailed;
                  spriteRedisCache.RedisDatabase.Multiplexer.ConnectionRestored += Multiplexer_ConnectionRestored;
                  _subscriber = spriteRedisCache.RedisDatabase.Multiplexer.GetSubscriber();
      
                  if (_callHttpConfig.RemoteReceivePreKey != null)
                  {
                      foreach (var remoteReceivePreKey in _callHttpConfig.RemoteReceivePreKey)
                      {
                          _subscriber.Subscribe(remoteReceivePreKey, (channel, message) =>
                          {
                              ReceiveCacheNotice.ReceiveClearCache(message);
                          });
      
                          _subscriber.Subscribe($"{remoteReceivePreKey}s", (channel, message) =>
                          {
                              List<string> keys = JsonConvert.DeserializeObject<List<string>>(message);
                              ReceiveCacheNotice.ReceiveClearCaches(keys);
                          });
                      }
                  }
              }
      
              private void Multiplexer_ConnectionRestored(object sender, StackExchange.Redis.ConnectionFailedEventArgs e)
              {
                  ReceiveCacheNotice.SetLocalCacheIsEnabled(true);
              }
      
              private void Multiplexer_ConnectionFailed(object sender, StackExchange.Redis.ConnectionFailedEventArgs e)
              {
                  ReceiveCacheNotice.SetLocalCacheIsEnabled(false);
              }
      
              public void SendClearCache(string key)
              {
                  ReceiveCacheNotice.ReceiveClearCache(key);
                  if (_callHttpConfig.RemoteNoticePreKey != null)
                  {
                      if (_callHttpConfig.RemoteNoticePreKey.Any(r => key.StartsWith($"{r}-")))
                      {
                          _subscriber.Publish(key.Split('-')[0], key);
                      }
                  }
              }
      
              public void SendClearCaches(List<string> keys)
              {
                  ReceiveCacheNotice.ReceiveClearCaches(keys);
                  if (_callHttpConfig.RemoteNoticePreKey != null)
                  {
                      var groupKeyLists = keys.GroupBy(r => r.Split('-')[0]);
                      foreach (var groupKeyList in groupKeyLists)
                      {
      
                          if (_callHttpConfig.RemoteNoticePreKey.Any(r => groupKeyList.Key == r))
                          {
                              _subscriber.Publish($"{groupKeyList.Key}s", JsonConvert.SerializeObject(groupKeyList.ToList()));
                          }
                      }
                  }
              }
          }
      
      • 具體緩存代碼實(shí)現(xiàn)舉例(以表單為例)
      public class SpriteFormLocalCache : LocalCache<SpriteFormVueDto>
          {
              public override string CacheKey => CommonConsts.SpriteFormCacheKey;
      
              public override Dictionary<Guid, SpriteFormVueDto> GetAllDict(string applicationCode)
              {
                  if (!LocalCacheContainer.IsEnabledLocalCache) // 如果緩存通知服務(wù)不可以,直接讀取數(shù)據(jù)庫(kù)
                  {
                      return _serviceProvider.DoDapperService(DefaultDbConfig, (unitOfWork) =>
                      {
                          return GetSpriteFormVueDtos(applicationCode, unitOfWork);
                      });
                  }
                  else
                  {
      	// 讀取本地緩存內(nèi)容,如果本地緩存沒(méi)有數(shù)據(jù),讀取數(shù)據(jù)庫(kù)數(shù)據(jù),并寫(xiě)入本地緩存容器
                      return (Dictionary<Guid, SpriteFormVueDto>)LocalCacheContainer.Get($"{CommonConsts.SpriteFormCachePreKey}-{applicationCode}_{CacheKey}", key =>
                      {
                          return _serviceProvider.DoDapperService(DefaultDbConfig, (unitOfWork) =>
                          {
                              return GetSpriteFormVueDtos(applicationCode, unitOfWork);
                          });
                      });
                  }
              }
      ......
      }
      
      • 前端緩存主要是用IndexDb實(shí)現(xiàn),前端代碼暫時(shí)沒(méi)開(kāi)源,閱讀一下即可
      import Dexie from 'dexie'
      import { SpriteRumtimeApi } from '@/sprite/api/spriteform'
      
      const db = new Dexie('formDb')
      db.version(1).stores({
          form: `id`
      })
      
      db.version(1).stores({
          view: `id`
      })
      
      db.version(1).stores({
          frameworkCache: `id`
      })
      
      db.version(1).stores({
          dict: `id`
      })
      
      window.spriteDb = db
      
      db.menuFormRelationInfo = {}
      const createMenuFormRelations = function (routeName, applicationCode, relationInfos) {
          if (!db.menuFormRelationInfo.hasOwnProperty(routeName)) {
              db.menuFormRelationInfo[routeName] = {}
              db.menuFormRelationInfo[routeName].applicationCode = applicationCode
              db.menuFormRelationInfo[routeName].relationInfos = relationInfos
          } else {
              relationInfos.forEach(relationInfo => {
                  if (!db.menuFormRelationInfo[routeName].relationInfos.find(r => r.relationType === relationInfo.relationType && r.id === relationInfo.id && r.version === relationInfo.version)) {
                      db.menuFormRelationInfo[routeName].relationInfos.push(relationInfo)
                  } 
              });
          }
      }
      
      /**
       * 遞歸獲取表單或視圖關(guān)聯(lián)表單視圖版本信息
       * @param {guid} objId 表單或視圖Id
       * @param {int} relationType 1=表單,2=視圖
       * @param {obj} relationInfos 表單和視圖版本信息
       */
      const findRelationConfigs = async function (objId, relationType, relationInfos) {
          if (!relationInfos) {
              relationInfos = []
          }
          console.log(relationType)
          var findData = relationType === 1 ? await db.form.get(objId) : await db.view.get(objId)
          if (findData && relationInfos.findIndex(r => r.id === findData.id) < 0) {
              relationInfos.push({ relationType: relationType, id: findData.id, version: findData.version })
          }
          if (findData && findData.relationInfos && findData.relationInfos.length > 0) {
              for (var i = 0; i < findData.relationInfos.length; i++) {
                  await findRelationConfigs(findData.relationInfos[i].id, findData.relationInfos[i].relationType, relationInfos)
              }
          }
          console.log('relationInfos')
          console.log(relationInfos)
          return relationInfos
      }
      
      db.getFormData = async function (routeName, formId, fromMenu, applicationCode) {
          var formData = await db.form.get(formId)
          var dictFrameworkCache = await db.frameworkCache.get('dict')
          console.log("getFormData")
          if (!formData) {
              var resultData = await SpriteRumtimeApi.simpleform({ id: formId, applicationCode: applicationCode })
              var menuFormrelationInfos = []
              if (resultData && resultData) {
                  for (var i = 0; i < resultData.formDatas.length; i++) {
                      await db.form.put(resultData.formDatas[i])
                      menuFormrelationInfos.push({relationType: 1, id: resultData.formDatas[i].id, version: resultData.formDatas[i].version})
                  }
                  for (var j = 0; j < resultData.viewDatas.length; j++) {
                      await db.view.put(resultData.viewDatas[j])
                      menuFormrelationInfos.push({relationType: 2, id: resultData.viewDatas[j].id, version: resultData.viewDatas[j].version})
                  }
              }
              if (resultData && resultData.dictVersion && resultData.dicts) {
                  await db.frameworkCache.put({ id: 'dict', version: resultData.dictVersion })
                  await db.dict.clear()
                  await db.dict.bulkAdd(resultData.dicts)
              }
              createMenuFormRelations(routeName, applicationCode, menuFormrelationInfos)
              formData = await db.form.get(formId)
          } else { // 從indexdb找到數(shù)據(jù),如果從菜單進(jìn)入,需要調(diào)用接口,判斷版本號(hào)信息
              if (fromMenu) {
                  delete db.menuFormRelationInfo[routeName]
                  var relationInfos = await findRelationConfigs(formId, 1, [])
                  var relationParams = { applicationCode: applicationCode, formId: formId, relationInfos: relationInfos, dictVersion: dictFrameworkCache?.version }
                  var checkResult = await SpriteRumtimeApi.checkversions(relationParams)
                  if ((checkResult && checkResult.formDatas && checkResult.formDatas.length > 0) || (checkResult && checkResult.viewDatas && checkResult.viewDatas.length > 0)) {
                      relationInfos = []
                  }
                  if (checkResult && checkResult.formDatas && checkResult.formDatas.length > 0) {
                      for (var i2 = 0; i2 < checkResult.formDatas.length; i2++) {
                          await db.form.put(checkResult.formDatas[i2])
                          relationInfos.push({relationType: 1, id: checkResult.formDatas[i2].id, version: checkResult.formDatas[i2].version})
                      }
                  }
                  if (checkResult && checkResult.viewDatas && checkResult.viewDatas.length > 0) {
                      for (var j2 = 0; j2 < checkResult.viewDatas.length; j2++) {
                          await db.view.put(checkResult.viewDatas[j2])
                          relationInfos.push({relationType: 2, id: checkResult.viewDatas[j2].id, version: checkResult.viewDatas[j2].version})
                      }
                  }
                  if (checkResult && checkResult.dictVersion && checkResult.dicts) {
                      await db.frameworkCache.put({ id: 'dict', version: checkResult.dictVersion })
                      await db.dict.clear()
                      await db.dict.bulkAdd(checkResult.dicts)
                  }
                  createMenuFormRelations(routeName, applicationCode, relationInfos)
                  formData = await db.form.get(formId)
              }
          }
          return formData
      }
      

       

      開(kāi)源地址:https://gitee.com/kuangqifu/sprite

      體驗(yàn)地址:http://47.108.141.193:8031(首次加載可能有點(diǎn)慢,用的阿里云最差的服務(wù)器)

      自定義表單文章地址:http://www.rzrgm.cn/spritekuang/

      流程引擎文章地址:http://www.rzrgm.cn/spritekuang/category/834975.html(采用WWF開(kāi)發(fā),已過(guò)時(shí),已改用Elsa實(shí)現(xiàn),http://www.rzrgm.cn/spritekuang/p/14970992.html )

      Github地址:https://github.com/kuangqifu/CK.Sprite.Job

       

      posted on 2022-02-09 18:50  spritekuang  閱讀(850)  評(píng)論(0)    收藏  舉報(bào)

      主站蜘蛛池模板: 亚洲精品日本久久久中文字幕| 強壮公弄得我次次高潮A片| 亚洲午夜理论无码电影| 国产成AV人片久青草影院| 国产美女午夜福利视频| 国产一区二区视频在线看| 人人妻人人澡人人爽| 东京热人妻无码一区二区av| 国产熟睡乱子伦视频在线播放| 国产综合久久久久鬼色| 日韩在线视频线观看一区| 国产免费无遮挡吸奶头视频 | 人妻精品人妻无码一区二区三区| 中文字幕日韩精品有码| 国产成人av综合色| 亚洲免费一区二区av| 91福利视频一区二区| 资源县| 国产高清乱码又大又圆| 国产一区二区精品久久呦| 四川丰满少妇无套内谢| 天天爽夜夜爱| 兴海县| 伊人久久大香线蕉av五月天| 厨房与子乱在线观看| 国产精品永久久久久久久久久| 日韩av综合免费在线| 少妇人妻偷人精品免费| 色综合久久综合中文综合网| 日本精品不卡一二三区| 精品无码久久久久久尤物| 亚洲精品综合一区二区在线| 小污女小欲女导航| 艳妇臀荡乳欲伦交换h在线观看| av无码久久久久不卡网站蜜桃 | 一区二区三区鲁丝不卡| 亚洲高潮喷水无码AV电影| 亚洲人成日韩中文字幕不卡| 亚洲色大成网站WWW国产| 国产精品三级黄色小视频| 成人无码潮喷在线观看|