【问题标题】:Keep track of a .net application variable through different requests通过不同的请求跟踪 .net 应用程序变量
【发布时间】:2017-09-30 09:49:29
【问题描述】:

在使用 C# 的 .Net 应用程序中,我必须进行昂贵的调用才能将一些数据发送给第三方 API,并且有时依赖于这个 API 会慢一些。

问题是,这些数据将是准确的,但我可以自己计算精度较低。所以我在想我如何才能让我们跟踪过去 5 分钟内的平均请求时间,以及它是否大于使用我自己的实现的阈值更改。

该方法的草图如下所示:

    public int GetMyData()
    {
        return isTooSlow() ? _ownImplementation.GetData() : thirdParty.GetData();
    }

即使理想情况下,我希望能够将第三方包装在我将实现的接口中,并在运行时更改它,这将是一件好事。

但主要问题是如何将这种状态保存在内存中。我只能考虑使用静态类,并且我已经阅读过有关 ApplicationState 的信息,但不确定其中是否是最好的方法。

此外,不是针对我的小项目,而是如何扩展这些解决方案?如果我必须考虑运行我的应用程序的多个实例,我认为唯一的解决方案是使用外部存储(redis 或类似的?)并在进行检查时对其进行查询。

对不起,如果这个问题太笼统,但认为这是一个有趣的问题,但不知道如何最好地解决它

谢谢

【问题讨论】:

  • 如果您有自己的实现,那么为什么需要调用第三方?或者在下班后致电第三方并将数据存储在某个地方,以便您可以更快地访问它。
  • 数据是在第三方“实时”更新的,所以我想每次都调用它,除非它在最后几分钟几乎没有响应。

标签: c# asp.net application-state


【解决方案1】:

我会将多个应用程序实例的问题放在次要位置。这并不重要,但如果您是针对接口进行编程,那么在某些时候您可以用缓存的东西替换您的实现。

如果您想要在 5 分钟等持续时间内的平均请求时间,那么您需要一个弹出过期条目的列表。这是一个尝试:

internal class TimestampedEntry<T>
{
    internal DateTimeOffset Timestamp { get; private set; }
    internal T Value { get; private set; }

    internal TimestampedEntry(T value)
    {
        Timestamp = DateTimeOffset.Now;
        Value = value;
    }
}

public class ExpiringList<T> 
{
    private readonly List<TimestampedEntry<T>> _list = new List<TimestampedEntry<T>>();
    private readonly TimeSpan _expiration;

    public ExpiringList(TimeSpan expiration)
    {
        _expiration = expiration;
    }

    public void Add(T item)
    {
        lock (_list)
        {
          _list.Add(new TimestampedEntry<T>(item));              
        }
    }

    public IReadOnlyCollection<T> Read()
    {
        var cutoff = DateTimeOffset.Now - _expiration;
        TimestampedEntry<T>[] result;
        lock (_list)
        {
            result = _list.Where(item => item.Timestamp > cutoff).ToArray();
            _list.Clear();
            _list.AddRange(result);
        }
        return new ReadOnlyCollection<T>(result.Select(item => item.Value).ToList());
    }
}

这确保当您从列表中读取时,它只返回存储在指定时间间隔内的项目,并删除其余项目。您可以创建一个ExpiringList&lt;TimeSpan&gt;,添加每个调用的经过时间,然后根据需要检查平均值。

存放在哪里?我会把它放在一个只有一个实例的类中。那可能是一个单例或一个静态类。我更喜欢使用返回单个实例的依赖注入容器(例如Windsor's singleton lifestyle。)我不喜欢创建单例。我宁愿创建一个“普通”类,然后管理它以保留一个实例。 Windsor 之类的 DI 容器让这一切变得简单。

我认为像这样的实现中的一个重要因素是将混乱的切换逻辑分开 - 隐藏在某种工厂中,而不是拥有一个 if/then 与所有逻辑来检查平均响应时间并调用任一 API都在一个大类中。

例如,如果您有一个表示获取数据的调用的接口,例如IMyDataProvider,那么您可以定义一个工厂,例如

interface IMyDataProviderFactory
{
    IMyDataProvider Create();
}

您的类只依赖于该工厂接口。实现 IMyDataProviderFactory 的类会检查您的平均响应时间,并返回调用外部 API 的 IMyDataProvider 的实现或使用您的计算的实现。

这样,该逻辑的复杂性与依赖于 API 的任何类保持分离。

Windsor 也很适合abstract factories。其他 DI 容器也使它们变得简单,并且这种功能内置在 ASP.NET Core 中。您不是在询问依赖注入,但我建议您调查一下。它使管理这种复杂性并保持可维护性变得更加容易。


回到多个应用程序实例和分布式缓存 - 您可以看到工厂模式的实现如何使其更易于管理。假设今天这是一个实例,但明天您想通过分布式缓存共享此数据。你在哪里做出改变?大多数依赖此 API 的代码根本不需要更改,因为它不“知道”任何这些实现​​细节。您将更改存储每个 API 调用时间的代码并更改工厂的实现。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-09
    • 1970-01-01
    • 2016-02-02
    • 2017-03-02
    • 1970-01-01
    • 2023-01-13
    相关资源
    最近更新 更多