jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 1 | // Copyright (c) 2007- Facebook |
| 2 | // Distributed under the Thrift Software License |
| 3 | // |
| 4 | // See accompanying file LICENSE or visit the Thrift site at: |
| 5 | // http://developers.facebook.com/thrift/ |
| 6 | |
| 7 | #include <algorithm> |
| 8 | #include <iostream> |
| 9 | |
| 10 | #include "TSocketPool.h" |
| 11 | |
David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame] | 12 | namespace facebook { namespace thrift { namespace transport { |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 13 | |
| 14 | using namespace std; |
| 15 | |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 16 | using boost::shared_ptr; |
| 17 | |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 18 | /** |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 19 | * TSocketPoolServer implementation |
| 20 | * |
| 21 | * @author Akhil Wable <akhil@facebook.com> |
| 22 | */ |
| 23 | TSocketPoolServer::TSocketPoolServer() |
| 24 | : host_(""), |
| 25 | port_(0), |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 26 | socket_(-1), |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 27 | lastFailTime_(0), |
| 28 | consecutiveFailures_(0) {} |
| 29 | |
| 30 | /** |
| 31 | * Constructor for TSocketPool server |
| 32 | */ |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 33 | TSocketPoolServer::TSocketPoolServer(const string &host, int port) |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 34 | : host_(host), |
| 35 | port_(port), |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 36 | socket_(-1), |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 37 | lastFailTime_(0), |
| 38 | consecutiveFailures_(0) {} |
| 39 | |
| 40 | /** |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 41 | * TSocketPool implementation. |
| 42 | * |
| 43 | * @author Jason Sobel <jsobel@facebook.com> |
| 44 | */ |
| 45 | |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 46 | TSocketPool::TSocketPool() : TSocket(), |
| 47 | numRetries_(1), |
| 48 | retryInterval_(60), |
| 49 | maxConsecutiveFailures_(1), |
| 50 | randomize_(true), |
| 51 | alwaysTryLast_(true) { |
| 52 | } |
| 53 | |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 54 | TSocketPool::TSocketPool(const vector<string> &hosts, |
| 55 | const vector<int> &ports) : TSocket(), |
| 56 | numRetries_(1), |
| 57 | retryInterval_(60), |
| 58 | maxConsecutiveFailures_(1), |
| 59 | randomize_(true), |
| 60 | alwaysTryLast_(true) |
| 61 | { |
| 62 | if (hosts.size() != ports.size()) { |
boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 63 | GlobalOutput("TSocketPool::TSocketPool: hosts.size != ports.size"); |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 64 | throw TTransportException(TTransportException::BAD_ARGS); |
| 65 | } |
| 66 | |
| 67 | for (unsigned int i = 0; i < hosts.size(); ++i) { |
dweatherford | d137282 | 2007-10-09 22:57:23 +0000 | [diff] [blame] | 68 | addServer(hosts[i], ports[i]); |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 69 | } |
| 70 | } |
| 71 | |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 72 | TSocketPool::TSocketPool(const vector<pair<string, int> >& servers) : TSocket(), |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 73 | numRetries_(1), |
| 74 | retryInterval_(60), |
| 75 | maxConsecutiveFailures_(1), |
| 76 | randomize_(true), |
| 77 | alwaysTryLast_(true) |
| 78 | { |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 79 | for (unsigned i = 0; i < servers.size(); ++i) { |
| 80 | addServer(servers[i].first, servers[i].second); |
| 81 | } |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 82 | } |
| 83 | |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 84 | TSocketPool::TSocketPool(const vector< shared_ptr<TSocketPoolServer> >& servers) : TSocket(), |
David Reiss | 907ad76 | 2008-03-02 00:25:58 +0000 | [diff] [blame] | 85 | servers_(servers), |
| 86 | numRetries_(1), |
| 87 | retryInterval_(60), |
| 88 | maxConsecutiveFailures_(1), |
| 89 | randomize_(true), |
| 90 | alwaysTryLast_(true) |
| 91 | { |
| 92 | } |
| 93 | |
dweatherford | d137282 | 2007-10-09 22:57:23 +0000 | [diff] [blame] | 94 | TSocketPool::TSocketPool(const string& host, int port) : TSocket(), |
| 95 | numRetries_(1), |
| 96 | retryInterval_(60), |
| 97 | maxConsecutiveFailures_(1), |
| 98 | randomize_(true), |
| 99 | alwaysTryLast_(true) |
| 100 | { |
| 101 | addServer(host, port); |
| 102 | } |
| 103 | |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 104 | TSocketPool::~TSocketPool() { |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 105 | vector< shared_ptr<TSocketPoolServer> >::const_iterator iter = servers_.begin(); |
| 106 | vector< shared_ptr<TSocketPoolServer> >::const_iterator iterEnd = servers_.end(); |
| 107 | for (; iter != iterEnd; ++iter) { |
| 108 | setCurrentServer(*iter); |
| 109 | TSocketPool::close(); |
| 110 | } |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 111 | } |
| 112 | |
dweatherford | d137282 | 2007-10-09 22:57:23 +0000 | [diff] [blame] | 113 | void TSocketPool::addServer(const string& host, int port) { |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 114 | servers_.push_back(shared_ptr<TSocketPoolServer>(new TSocketPoolServer(host, port))); |
dweatherford | d137282 | 2007-10-09 22:57:23 +0000 | [diff] [blame] | 115 | } |
| 116 | |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 117 | void TSocketPool::setServers(const vector< shared_ptr<TSocketPoolServer> >& servers) { |
| 118 | servers_ = servers; |
| 119 | } |
| 120 | |
| 121 | void TSocketPool::getServers(vector< shared_ptr<TSocketPoolServer> >& servers) { |
| 122 | servers = servers_; |
David Reiss | 907ad76 | 2008-03-02 00:25:58 +0000 | [diff] [blame] | 123 | } |
| 124 | |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 125 | void TSocketPool::setNumRetries(int numRetries) { |
| 126 | numRetries_ = numRetries; |
| 127 | } |
| 128 | |
| 129 | void TSocketPool::setRetryInterval(int retryInterval) { |
| 130 | retryInterval_ = retryInterval; |
| 131 | } |
| 132 | |
| 133 | |
| 134 | void TSocketPool::setMaxConsecutiveFailures(int maxConsecutiveFailures) { |
| 135 | maxConsecutiveFailures_ = maxConsecutiveFailures; |
| 136 | } |
| 137 | |
| 138 | void TSocketPool::setRandomize(bool randomize) { |
| 139 | randomize_ = randomize; |
| 140 | } |
| 141 | |
| 142 | void TSocketPool::setAlwaysTryLast(bool alwaysTryLast) { |
| 143 | alwaysTryLast_ = alwaysTryLast; |
| 144 | } |
| 145 | |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 146 | void TSocketPool::setCurrentServer(const shared_ptr<TSocketPoolServer> &server) { |
| 147 | currentServer_ = server; |
| 148 | host_ = server->host_; |
| 149 | port_ = server->port_; |
| 150 | socket_ = server->socket_; |
| 151 | } |
| 152 | |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 153 | /* TODO: without apc we ignore a lot of functionality from the php version */ |
| 154 | void TSocketPool::open() { |
| 155 | if (randomize_) { |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 156 | random_shuffle(servers_.begin(), servers_.end()); |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 157 | } |
| 158 | |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 159 | unsigned int numServers = servers_.size(); |
| 160 | for (unsigned int i = 0; i < numServers; ++i) { |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 161 | |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 162 | shared_ptr<TSocketPoolServer> &server = servers_[i]; |
| 163 | bool retryIntervalPassed = (server->lastFailTime_ == 0); |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 164 | bool isLastServer = alwaysTryLast_ ? (i == (numServers - 1)) : false; |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 165 | |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 166 | // Impersonate the server socket |
| 167 | setCurrentServer(server); |
David Reiss | f50021a | 2008-02-29 07:33:47 +0000 | [diff] [blame] | 168 | |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 169 | if (isOpen()) { |
| 170 | // already open means we're done |
| 171 | return; |
| 172 | } |
| 173 | |
| 174 | if (server->lastFailTime_ > 0) { |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 175 | // The server was marked as down, so check if enough time has elapsed to retry |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 176 | int elapsedTime = time(NULL) - server->lastFailTime_; |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 177 | if (elapsedTime > retryInterval_) { |
| 178 | retryIntervalPassed = true; |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 179 | } |
| 180 | } |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 181 | |
| 182 | if (retryIntervalPassed || isLastServer) { |
| 183 | for (int j = 0; j < numRetries_; ++j) { |
| 184 | try { |
| 185 | TSocket::open(); |
| 186 | |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 187 | // Copy over the opened socket so that we can keep it persistent |
| 188 | server->socket_ = socket_; |
| 189 | |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 190 | // reset lastFailTime_ is required |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 191 | if (server->lastFailTime_) { |
| 192 | server->lastFailTime_ = 0; |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 193 | } |
| 194 | |
| 195 | // success |
| 196 | return; |
| 197 | } catch (TException e) { |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 198 | string errStr = "TSocketPool::open failed "+getSocketInfo()+": "+e.what(); |
| 199 | GlobalOutput(errStr.c_str()); |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 200 | // connection failed |
| 201 | } |
| 202 | } |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 203 | |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 204 | ++server->consecutiveFailures_; |
| 205 | if (server->consecutiveFailures_ > maxConsecutiveFailures_) { |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 206 | // Mark server as down |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 207 | server->consecutiveFailures_ = 0; |
| 208 | server->lastFailTime_ = time(NULL); |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 209 | } |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 210 | } |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 211 | } |
| 212 | |
boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 213 | GlobalOutput("TSocketPool::open: all connections failed"); |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 214 | throw TTransportException(TTransportException::NOT_OPEN); |
| 215 | } |
| 216 | |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 217 | void TSocketPool::close() { |
| 218 | if (isOpen()) { |
| 219 | TSocket::close(); |
| 220 | currentServer_->socket_ = -1; |
| 221 | } |
| 222 | } |
| 223 | |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 224 | }}} // facebook::thrift::transport |