如何从C#Web API REST服务器缓存缓慢的资源初始化?

如何从C#Web API REST服务器缓存缓慢的资源初始化?,c#,rest,caching,asp.net-web-api,C#,Rest,Caching,Asp.net Web Api,上下文 我正在尝试实现一个RESTAPI web服务,它“包装”了一个现有的C程序 问题/目标 考虑到当我告诉C程序打开一个特定的文件夹时,它的初始化时间慢,RAM使用率高(假设这无法改进),我考虑缓存C句柄/对象,因此下次GET请求命中同一文件夹时,我可以使用现有的句柄 我尝试过的 首先声明从文件夹路径到句柄的静态字典映射: static ConcurrentDictionary<string, IHandle> handles = new ConcurrentDictionary

上下文

我正在尝试实现一个RESTAPI web服务,它“包装”了一个现有的C程序

问题/目标

考虑到当我告诉C程序打开一个特定的文件夹时,它的初始化时间慢,RAM使用率高(假设这无法改进),我考虑缓存C句柄/对象,因此下次GET请求命中同一文件夹时,我可以使用现有的句柄

我尝试过的

首先声明从文件夹路径到句柄的静态字典映射:

static ConcurrentDictionary<string, IHandle> handles = new ConcurrentDictionary<string, IHandle>();
这样一来,每当以前获得某个特定文件夹时,它就已经有了一个句柄,可以供我使用

为什么不好

因此,如果同时缓存了太多的文件夹,我就有内存不足的风险。如何将类似GC的后台进程添加到
TryRemove()
中,并在旧句柄上调用
IHandle.Dispose()
,可能是在最近使用最少或使用最少的策略中?理想情况下,它应该仅在可用物理内存不足时启动触发

我曾尝试在GET函数中添加以下语句,但它似乎太粗糙,并且在函数中非常有限。只有当我总是希望句柄在10秒后过期时,这种方法才可以正常工作,并且如果后续请求在10秒内传入,它不会重新启动计时器

HostingEnvironment.QueueBackgroundWorkItem(ct =>
{
    System.Threading.Thread.Sleep(10000);
    if (handles.TryRemove(dir.Name, out var handle2))
        handle2.Dispose();
});
这个问题不是什么

我不认为缓存输出是这里的解决方案。在我返回这个GET请求的结果(它只是文件夹内容的元数据)之后,可能会有另一个GET请求来获取更深入的数据,这需要调用
Handle
的方法

我希望我的问题足够清楚

  • 在内存不足时关闭句柄
  • ConcurrentQueue句柄=新建ConcurrentQueue();
    void CheckMemory_optionalReleaseOldHandles()
    {
    var performance=新系统.诊断.性能计数器(“内存”,“可用MB”);
    while(performance.NextValue()v.Item1==dir.Name).Item2;
    if(theHandle==null)
    {
    theHandle=新句柄(目录名);
    handles.Enqueue((dir.Name,theHandle));
    }
    返回手柄;
    });
    
  • 你的背景任务
  • void SetupMemoryCheck()
    {
    检查记忆=ct=>
    {
    对于(;;)
    {
    如果(ct.IsCancellationRequested)
    {
    打破
    }
    选中Memory_OptionalReleaseOldHandles();
    睡眠(500);
    };
    };
    HostingEnvironment.QueueBackgroundWorkItem(ct=>
    {
    var tf=新TaskFactory(ct、TaskCreationOptions.LongRunning、TaskContinuationOptions.None、TaskScheduler.Current);
    tf.StartNew(()=>正在检查内存(ct));
    });
    }
    

    我想这个系列会有一些元素,所以不需要查字典。

    我第一次没注意到你的LRU/LFU需求。在这里,您可以检查一些混合LRU/LFU缓存模型

  • 在内存不足时关闭句柄
  • /*
    *string–句柄名称,
    *IHandle–手柄,
    *int–命中计数,
    */
    ConcurrentDictionary句柄=新建ConcurrentDictionary();
    无效免费资源()
    {
    if(handles.Count==0)
    {
    返回;
    }
    var performance=新系统.诊断.性能计数器(“内存”,“可用MB”);
    while(performance.NextValue()
    {
    对于(;;)
    {        
    如果(ct.IsCancellationRequested)中断;
    免费资源();
    睡眠(500);
    }
    };
    HostingEnvironment.QueueBackgroundWorkItem(ct=>
    {
    新任务(()=>BeCheckingTheMemory(ct),TaskCreationOptions.LongRunning.Start();
    });
    }
    

    如果您希望收集大量的for循环,则可以对其进行优化。

    您说过响应只包含文件夹元数据?是否需要文件夹(元数据)经常更改?为什么不只缓存元数据?正如我问题的最后一部分所述,可能还有一个获取更详细数据的GET请求,因此我需要将句柄保存在内存中,而不仅仅是元数据。我现在读到了。如果详细请求会出现,是否仍然需要将句柄保持打开状态?可以有任意数量的后续de尾部请求。保持句柄打开可以让我非常快速地响应这些详细请求。因为可能有太多不同的输出,所以不可能缓存详细请求。我希望尽可能长时间地保持句柄,直到物理内存变低,然后我释放它们,希望是以LRU/LFU方式。我没有这样做“我第一次没有注意到你的LRU/LFU需求。你还对它感兴趣吗?我还意识到你的代码可能仍然容易受到OutOfMemoryException的攻击。这会影响你的\u TRESHHOLD和最大的文件期望比率,以及请求方式。会有同时的请求吗?请求会多久出现一次?我还意识到没有必要。”o定期检查内存级别。如果内存级别出现异常,可以在处理负载时进行检查。这将一举两得
    HostingEnvironment.QueueBackgroundWorkItem(ct =>
    {
        System.Threading.Thread.Sleep(10000);
        if (handles.TryRemove(dir.Name, out var handle2))
            handle2.Dispose();
    });