Question

I hate the questions that have "Not Enough Info". So I will try to give detailed information. And in this case it is code.

Server: 64 bit of https://github.com/MSOpenTech/redis/tree/2.6/bin/release

There are three classes:

DbOperationContext.cs: https://gist.github.com/glikoz/7119628

PerRequestLifeTimeManager.cs: https://gist.github.com/glikoz/7119699

RedisRepository.cs https://gist.github.com/glikoz/7119769

We are using Redis with Unity ..

In this case we are getting this strange message:

"Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use.";

We checked these:

  • Is the problem configuration issue

  • Are we using wrong RedisServer.exe

  • Is there any architectural problem

Any idea? Any similar story?

Thanks.

Extra Info 1

There is no rejected connection issue on server stats (I've checked it via redis-cli.exe info command)

Était-ce utile?

La solution

I have continued to debug this problem, and have fixed numerous things on my platform to avoid this exception. Here is what I have done to solve the issue:

Executive summary:

People encountering this exception should check:

  1. That the PooledRedisClientsManager (IRedisClientsManager) is registed in a singleton scope
  2. That the RedisMqServer (IMessageService) is registered in a singleton scope
  3. That any utilized RedisClient returned from either of the above is properly disposed of, to ensure that the pooled clients are not left stale.

The solution to my problem:

First of all, this exception is thrown by the PooledRedisClient because it has no more pooled connections available.

I'm registering all the required Redis stuff in the StructureMap IoC container (not unity as in the author's case). Thanks to this post I was reminded that the PooledRedisClientManager should be a singleton - I also decided to register the RedisMqServer as a singleton:

 ObjectFactory.Configure(x =>
 {
     // register the message queue stuff as Singletons in this AppDomain
     x.For<IRedisClientsManager>()
         .Singleton()
         .Use(BuildRedisClientsManager);
     x.For<IMessageService>()
         .Singleton()
         .Use<RedisMqServer>()
         .Ctor<IRedisClientsManager>().Is(i => i.GetInstance<IRedisClientsManager>())
         .Ctor<int>("retryCount").Is(2)
         .Ctor<TimeSpan?>().Is(TimeSpan.FromSeconds(5));

     // Retrieve a new message factory from the singleton IMessageService 
     x.For<IMessageFactory>()
         .Use(i => i.GetInstance<IMessageService>().MessageFactory);
 });

My "BuildRedisClientManager" function looks like this:

private static IRedisClientsManager BuildRedisClientsManager()
{
    var appSettings = new AppSettings();
    var redisClients = appSettings.Get("redis-servers", "redis.local:6379").Split(',');
    var redisFactory = new PooledRedisClientManager(redisClients);
    redisFactory.ConnectTimeout = 5;
    redisFactory.IdleTimeOutSecs = 30;
    redisFactory.PoolTimeout = 3;
    return redisFactory;
 }

Then, when it comes to producing messages it's very important that the utilized RedisClient is properly disposed of, otherwise we run into the dreaded "Timeout Expired" (thanks to this post). I have the following helper code to send a message to the queue:

public static void PublishMessage<T>(T msg)
{
    try
    {
       using (var producer = GetMessageProducer())
       {
           producer.Publish<T>(msg);
       }
    }
    catch (Exception ex)
    {
        // TODO: Log or whatever... I'm not throwing to avoid showing users that we have a broken MQ
    }
}

private static IMessageQueueClient GetMessageProducer()
{
    var producer = ObjectFactory.GetInstance<IMessageService>() as RedisMqServer;
    var client = producer.CreateMessageQueueClient();

    return client;
}

I hope this helps solve your issue too.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top