通过不同的请求跟踪.net应用程序变量

时间:2017-05-02 18:52:23

标签: c# asp.net application-state

在使用C#的.Net应用程序中,我必须拨打昂贵的电话才能将某些数据提供给第三方API,并且依赖于此,有时它会慢一些。

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

该方法的草图将是这样的:

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

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

但主要的问题是如何将这种状态保留在记忆中。我只能想到使用静态类,我已阅读有关ApplicationState的信息,但不确定这些是否是最好的方法。

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

很抱歉,如果问题过于通用,但认为这是一个有趣的问题需要解决,并且不知道如何最好地接近它

由于

1 个答案:

答案 0 :(得分:1)

我会把多个应用程序实例的问题放在后面。并不重要,但如果您针对接口进行编程,那么在某些时候您可以使用缓存的内容替换您的实现。

如果您希望平均请求时间超过五分钟,那么您需要一个列出弹出过期条目的列表。以下是对此的抨击:

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<TimeSpan>,为每个调用添加已用时间,然后根据需要检查平均值。

哪里存放?我把它放在一个带有单个实例的类中。那可能是单身人士或静态阶级。我更喜欢使用一个返回单个实例的依赖注入容器(如Windsor's singleton lifestyle。)我不喜欢创建单例。我宁愿创造一个正常的&#34;类,然后管理它以保持单个实例。像温莎这样的DI容器就这么简单。

我认为像这样实现的一个重要因素是将凌乱的交换逻辑分开 - 隐藏在某种工厂中,而不是让if/then具有检查平均响应时间和调用的所有逻辑这两种API都属于一个大类。

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

这样的工厂
interface IMyDataProviderFactory
{
    IMyDataProvider Create();
}

您的课程仅取决于该工厂界面。实现IMyDataProviderFactory的类会检查您的平均响应时间,并返回调用外部API的IMyDataProvider实现或使用您计算的实现。

这样,逻辑的复杂性就会与依赖于API的任何类分开。

温莎对那些abstract factories也很好。其他DI容器也使它们变得简单,并且这种功能内置于ASP.NET Core中。你没有询问依赖注入,但我建议你研究一下。它可以更轻松地管理这种复杂性并保持其可维护性。

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