Azure函数和缓存

37
我们计划开发一个Azure函数,其输入触发器是服务总线消息,并且输出将存储在Blob存储中。服务总线消息将包含图像URL,该函数将调整图像大小为预定义的分辨率并上传到Azure Blob存储。
应将图像调整为的分辨率存储在数据库中,Azure函数需要调用数据库以了解输入消息中应使用的分辨率。该分辨率实际上是基于输入消息来源配置的主数据。
每次调用都需要进行数据库调用,这将是昂贵的。是否有任何方法可以缓存数据并在不调用数据库的情况下使用它?例如内存缓存?

之前的一些线程中也提到了一些建议。https://dev59.com/K5nga4cB1Zd3GeqPWUgM - Baskar Rao
5个回答

54

您可以自由地使用在其他 .NET 应用程序中使用的常规方法:

  • 您可以将数据缓存在内存中。最简单的方法是声明一个静态字典,并将数据库值放入其中(如果需要,使用并发字典)。缓存的值将被重复使用于所有后续的函数执行,这些函数在同一实例上运行。如果某个实例闲置了5分钟,或者应用程序扩展到额外的实例,您将不得不重新读取数据库;

  • 您可以使用分布式缓存,例如 Redis,通过从函数代码中使用其 SDK。可能会更好,因为您保持 Functions 的无状态性质,但可能会花费更多成本。表存储是 Redis 的一种可行替代方案,但具有更受限制的 API。

Azure Functions 本身没有“缓存”功能,没有任何额外的代码就能够使用。


5
如果函数托管在消耗计划中,那么将其缓存在内存中是否没有意义?每次执行后函数是否会被删除(?)并且有可能在不同的虚拟机上启动,因此内存缓存可能不是可行的方法? - Andy T
15
不,函数不会在每次执行后被删除。如果请求的速度超过每几分钟(默认为5分钟)一次,实例将被重复使用。 - Mikhail Shilkov
3
如果你仍然需要进行缓存(这是否还有意义?),那么你唯一的选择是使用进程外缓存。 - Mikhail Shilkov
3
你可以设置一个定时器来触发一个名为“Ping”的函数。调用“Ping”函数将保持整个应用程序处于运行状态,因为应用程序是被卸载的单位。我不确定这是否符合他们的服务条款。 - Simon_Weaver
2
现在离消耗计划上最后一个空闲实例被杀死还有20分钟。 这不适用于固定或高级计划。 - Mikhail Shilkov
显示剩余5条评论

10

您可以使用Azure Cache服务(https://azure.microsoft.com/zh-cn/services/cache/)来缓存您的数据。基本上,在您的Azure函数中,不要每次都调用数据库,而是调用Azure缓存,如果它没有过期并且未设置,则调用数据库以获取值,并使用适当的到期逻辑(在固定时间后超时或其他自定义逻辑)填充缓存。


3
虽然它不便宜:/ - Emil

7

您可以使用Durable Functions,并通过活动或子编排进行数据库调用,返回值将被缓存,每次函数重放时都会返回,而无需再次进行底层调用。


你能否以某种方式多次重复使用活动的(“旧”)响应? - Martin Wickman

6
Redis是一种内存缓存,而且有自定义输出绑定可以使用来保持函数的简洁性。
[FunctionName("SetPoco")]
public static async Task<IActionResult> SetPoco(
    [HttpTrigger("POST", Route = "poco/{key}")] HttpRequest request,
    [Redis(Key = "{key}")] IAsyncCollector<CustomObject> collector)
{
    string requestBody;
    using (var reader = new StreamReader(request.Body))
    {
        requestBody = reader.ReadToEnd();
        var value = JsonConvert.DeserializeObject<CustomObject>(requestBody);
        await collector.AddAsync(value);
    }
    return new OkObjectResult(requestBody);
}

项目链接:https://github.com/daulet/Indigo.Functions#redis

然而,如果您所说的内存缓存是指函数内存中的缓存,我强烈建议您不要这样做,因为函数应该是无状态的,您将无法在运行函数的多个主机之间共享该内存。这也不被推荐在Azure Functions最佳实践中。


4
我能理解为什么你建议在这个特定情况下不使用状态(像这样的主设置是状态,可以保存在设置中而不是数据库中),但一般来说,缓存被认为是一种性能优化而不是状态。该函数仍然是无状态且幂等的,它只是避免在每个请求到来时访问数据库。 - Casper

1
这是我构建的一个小类,旨在简化在运行实例的内存中存储和重复使用对象的任务,只要实例仍然存在。当然,这意味着每个新实例都需要自己填充,但这可以提供一些有用的优化。
// A simple light-weight cache, used for storing data in the memory of each running instance of the Azure Function.
// If an instance gets idle (for 5 minutes or whatever the latest time period is) or if the Function App scales out to an extra instance then the cache is re-populated.
// To use, create a static readonly instance of this class in the Azure Function class, in the constructor pass a function which populates the object to cache.
// Then simply reference the Data object.  It will be populated on the first call and re-used on future calls whilst the same instance remains alive.
public class FunctionInstanceCache<T>
{
    public FunctionInstanceCache(Func<T> populate)
    {
        Populate = populate;
        IsInit = false;
    }

    public Func<T> Populate { get; set; }

    public bool IsInit { get; set; }

    private T data;

    public T Data
    {
        get
        {
            if (IsInit == false)
            {
                Init();
            };
            return data;
        }

    }

    public void Init()
    {
        data = Populate();
        IsInit = true;
    }
}

然后在您的Azure函数实例实现中创建一个静态只读实例,传入一个Populate方法:

    private static readonly FunctionInstanceCache<string[]> Fic = new FunctionInstanceCache<string[]>(PopulateCache);

然后实现这个。
private static string[] PopulateCache()
{
    return DOSOMETHING HERE;
}

需要时只需调用Fic.Data即可——第一次使用时它将被填充,然后在实例仍然存在时重复使用。


7
使用已经处理好所有内容的MemoryCache类,难道不更容易吗? - Emil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接