在Azure功能中调整Redis连接以防止超时

时间:2016-09-08 14:38:19

标签: azure redis stackexchange.redis azure-functions

TL; DR

如何在Azure功能中修改redis的最小线程数?

问题

我有一个Azure函数,它使用redis(通过StackExchange.Redis包)来缓存某些值,或检索现有值(如果已存在)。我目前遇到的超时问题看起来是因为Busy IOCP线程超过了Min IOCP线程值。

  

2016-09-08T11:52:44.492执行函数时出现异常:Functions.blobtoeventhub。 mscorlib:调用目标抛出了异常。 StackExchange.Redis:超时执行SETNX 586:tag:NULL,inst:1,mgr:Inactive,err:never,queue:4,qu:0,qs:4,qc:0,wr:0,wq:0,in :260,ar:0,clientName:RD00155D3AE265,IOCP :( Busy = 8,Free = 992,Min = 2,Max = 1000),WORKER :( Busy = 7,Free = 32760,Min = 2,Max = 32767) ,Local-CPU:不可用(请查看本文以了解可能导致超时的一些常见客户端问题:https://github.com/StackExchange/StackExchange.Redis/tree/master/Docs/Timeouts.md)。

根据docs on timeouts,解决方案涉及调整MinThread计数:

  

如何配置此设置:

     

在ASP.NET中,使用" minIoThreads" machine.config中配置元素下的配置设置。如果您在Azure WebSites内部运行,则不会通过配置选项公开此设置。您应该能够在global.asax.cs中的Application_Start方法中以编程方式(请参见下文)进行设置。   重要说明:此配置元素中指定的值是每个核心设置。例如,如果你有一台4核机器,并希望你的minIOThreads设置在运行时为200,你可以使用。   在ASP.NET之外,使用ThreadPool.SetMinThreads(...)API。

在Azure函数中,global.asax.cs文件不可用,ThreadPool.SetMinThreads的使用与我可以解析的关联信息很少! webjobs上有一个类似的问题没有答案。

我的具体细节

  • Redis = Azure Redis缓存标准版1Gb
  • Azure功能=版本0.5
    • StackExchange.Redis =版本1.1.603

Redis代码位于main函数的单独文件中。

using StackExchange.Redis;
using System.Text;

private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
        {
            string redisCacheName = System.Environment.GetEnvironmentVariable("rediscachename", EnvironmentVariableTarget.Process).ToString();;
            string redisCachePassword = System.Environment.GetEnvironmentVariable("rediscachepassword", EnvironmentVariableTarget.Process).ToString();;
            return ConnectionMultiplexer.Connect(redisCacheName + ",abortConnect=false,ssl=true,password=" + redisCachePassword);
        });

public static ConnectionMultiplexer Connection
{
    get
    {
        return lazyConnection.Value;
    }
}

static string depersonalise_value(string input, string field, int account_id)
{
    IDatabase cache = Connection.GetDatabase();
    string depersvalue = $"{account_id}:{field}:{input}";
    string value = $"{account_id}{Guid.NewGuid()}";
    bool created = cache.StringSet(depersvalue, value, when: When.NotExists);
    string retur = created? value : cache.StringGet(depersvalue).ToString();
    return (retur);
}

2 个答案:

答案 0 :(得分:3)

最终,我们需要追求@mathewc的答案,并在连接多路复用器代码中添加一行,以将最小线程设置为500

readonly static Lazy<ConnectionMultiplexer> lazyConnection =

    new Lazy<ConnectionMultiplexer>(() =>

    {
      ThreadPool.SetMinThreads(500, 500);

此外,还需要进一步调整,并通过SO code review增强代码。这里最重要的是大幅度提高超时时间。

using StackExchange.Redis;
using System.Text;
using System.Threading;

readonly static Lazy<ConnectionMultiplexer> lazyConnection =
    new Lazy<ConnectionMultiplexer>(() =>
    {
        ThreadPool.SetMinThreads(500, 500);
        string redisCacheName = System.Environment.GetEnvironmentVariable("rediscache_name", EnvironmentVariableTarget.Process).ToString();
        string redisCachePassword = System.Environment.GetEnvironmentVariable("rediscache_password", EnvironmentVariableTarget.Process).ToString();
        return ConnectionMultiplexer.Connect(new ConfigurationOptions
        {
            AbortOnConnectFail = false,
            Ssl = true,
            ConnectRetry = 3,
            ConnectTimeout = 5000,
            SyncTimeout = 5000,
            DefaultDatabase = 0,
            EndPoints = { { redisCacheName, 0 } },
            Password = redisCachePassword
        });
    });


public static ConnectionMultiplexer Connection => lazyConnection.Value;


static string depersonalise_value(string input, string field, int account_id)
{
    IDatabase cache = Connection.GetDatabase();
    string depersvalue = $"{account_id}:{field}:{input}";
    string existingguid = (string)cache.StringGet(depersvalue);
    if (String.IsNullOrEmpty(existingguid)){ 
        string value = $"{account_id}{Guid.NewGuid()}";
        cache.StringSet(depersvalue, value);
        return value;
    }
    return existingguid;
}

答案 1 :(得分:1)

我们目前没有很好的方法来执行这样的应用级初始化。这是由我们的回购here中的问题跟踪的。

目前,您唯一真正的解决方法是将此init代码放入您在函数开头调用的共享帮助程序中。共享init方法应该具有逻辑,以便它只执行一次。