diff options
author | mike <mike@okcupid.com> | 2014-08-14 16:24:59 -0400 |
---|---|---|
committer | Matt Stancliff <matt@genges.com> | 2015-01-05 16:39:30 -0500 |
commit | 7c4d2557c4bbc637514392cb725719790b50f677 (patch) | |
tree | 7203ac816e360be67253f6f7175580e90b8d1caa | |
parent | a1bc89b23621df61437619eec849a6a8bcc30a03 (diff) |
Add support for SO_REUSEADDR
[This introduces some new API functions.]
* Adds new flag to the connection context indicating SO_REUSEADDR
should be set.
* Adds max number of retries constant for when connect() hits
EADDRNOTAVAIL.
* Adds new function, redisAsyncConnectBindWithReuse(), letting
clients enable this functionality.
[Removed trailing whitespace in new header lines.]
Closes #264
-rw-r--r-- | async.c | 8 | ||||
-rw-r--r-- | async.h | 2 | ||||
-rw-r--r-- | hiredis.c | 9 | ||||
-rw-r--r-- | hiredis.h | 12 | ||||
-rw-r--r-- | net.c | 20 |
5 files changed, 49 insertions, 2 deletions
@@ -173,6 +173,14 @@ redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, return ac; } +redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, + const char *source_addr) { + redisContext *c = redisConnectBindNonBlockWithReuse(ip,port,source_addr); + redisAsyncContext *ac = redisAsyncInitialize(c); + __redisAsyncCopyError(ac); + return ac; +} + redisAsyncContext *redisAsyncConnectUnix(const char *path) { redisContext *c; redisAsyncContext *ac; @@ -103,6 +103,8 @@ typedef struct redisAsyncContext { /* Functions that proxy to hiredis */ redisAsyncContext *redisAsyncConnect(const char *ip, int port); redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr); +redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port, + const char *source_addr); redisAsyncContext *redisAsyncConnectUnix(const char *path); int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); @@ -1123,6 +1123,15 @@ redisContext *redisConnectBindNonBlock(const char *ip, int port, return c; } +redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, + const char *source_addr) { + redisContext *c = redisContextInit(); + c->flags &= ~REDIS_BLOCK; + c->flags |= REDIS_REUSEADDR; + redisContextConnectBindTcp(c,ip,port,NULL,source_addr); + return c; +} + redisContext *redisConnectUnix(const char *path) { redisContext *c; @@ -80,6 +80,9 @@ /* Flag that is set when monitor mode is active */ #define REDIS_MONITORING 0x40 +/* Flag that is set when we should set SO_REUSEADDR before calling bind() */ +#define REDIS_REUSEADDR 0x80 + #define REDIS_REPLY_STRING 1 #define REDIS_REPLY_ARRAY 2 #define REDIS_REPLY_INTEGER 3 @@ -91,6 +94,10 @@ #define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */ +/* number of times we retry to connect in the case of EADDRNOTAVAIL and + * SO_REUSEADDR is being used. */ +#define REDIS_CONNECT_RETRIES 10 + #ifdef __cplusplus extern "C" { #endif @@ -177,7 +184,10 @@ typedef struct redisContext { redisContext *redisConnect(const char *ip, int port); redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); redisContext *redisConnectNonBlock(const char *ip, int port); -redisContext *redisConnectBindNonBlock(const char *ip, int port, const char *source_addr); +redisContext *redisConnectBindNonBlock(const char *ip, int port, + const char *source_addr); +redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, + const char *source_addr); redisContext *redisConnectUnix(const char *path); redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); redisContext *redisConnectUnixNonBlock(const char *path); @@ -256,10 +256,12 @@ int redisContextSetTimeout(redisContext *c, const struct timeval tv) { static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout, const char *source_addr) { - int s, rv; + int s, rv, n; char _port[6]; /* strlen("65535"); */ struct addrinfo hints, *servinfo, *bservinfo, *p, *b; int blocking = (c->flags & REDIS_BLOCK); + int reuseaddr = (c->flags & REDIS_REUSEADDR); + int reuses = 0; snprintf(_port, 6, "%d", port); memset(&hints,0,sizeof(hints)); @@ -279,6 +281,7 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, } } for (p = servinfo; p != NULL; p = p->ai_next) { +addrretry: if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1) continue; @@ -294,6 +297,15 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, __redisSetError(c,REDIS_ERR_OTHER,buf); goto error; } + + if (reuseaddr) { + n = 1; + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*) &n, + sizeof(n)) < 0) { + goto error; + } + } + for (b = bservinfo; b != NULL; b = b->ai_next) { if (bind(s,b->ai_addr,b->ai_addrlen) != -1) { bound = 1; @@ -314,6 +326,12 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, continue; } else if (errno == EINPROGRESS && !blocking) { /* This is ok. */ + } else if (errno == EADDRNOTAVAIL && reuseaddr) { + if (++reuses >= REDIS_CONNECT_RETRIES) { + goto error; + } else { + goto addrretry; + } } else { if (redisContextWaitReady(c,timeout) != REDIS_OK) goto error; |