David Reiss | ea2cba8 | 2009-03-30 21:35:00 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Licensed to the Apache Software Foundation (ASF) under one |
| 3 | * or more contributor license agreements. See the NOTICE file |
| 4 | * distributed with this work for additional information |
| 5 | * regarding copyright ownership. The ASF licenses this file |
| 6 | * to you under the Apache License, Version 2.0 (the |
| 7 | * "License"); you may not use this file except in compliance |
| 8 | * with the License. You may obtain a copy of the License at |
| 9 | * |
| 10 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | * |
| 12 | * Unless required by applicable law or agreed to in writing, |
| 13 | * software distributed under the License is distributed on an |
| 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | * KIND, either express or implied. See the License for the |
| 16 | * specific language governing permissions and limitations |
| 17 | * under the License. |
| 18 | */ |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 19 | |
| 20 | #include <algorithm> |
| 21 | #include <iostream> |
| 22 | |
| 23 | #include "TSocketPool.h" |
| 24 | |
T Jake Luciani | b5e6221 | 2009-01-31 22:36:20 +0000 | [diff] [blame] | 25 | namespace apache { namespace thrift { namespace transport { |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 26 | |
| 27 | using namespace std; |
| 28 | |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 29 | using boost::shared_ptr; |
| 30 | |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 31 | /** |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 32 | * TSocketPoolServer implementation |
| 33 | * |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 34 | */ |
| 35 | TSocketPoolServer::TSocketPoolServer() |
| 36 | : host_(""), |
| 37 | port_(0), |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 38 | socket_(-1), |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 39 | lastFailTime_(0), |
| 40 | consecutiveFailures_(0) {} |
| 41 | |
| 42 | /** |
| 43 | * Constructor for TSocketPool server |
| 44 | */ |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 45 | TSocketPoolServer::TSocketPoolServer(const string &host, int port) |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 46 | : host_(host), |
| 47 | port_(port), |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 48 | socket_(-1), |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 49 | lastFailTime_(0), |
| 50 | consecutiveFailures_(0) {} |
| 51 | |
| 52 | /** |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 53 | * TSocketPool implementation. |
| 54 | * |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 55 | */ |
| 56 | |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 57 | TSocketPool::TSocketPool() : TSocket(), |
| 58 | numRetries_(1), |
| 59 | retryInterval_(60), |
| 60 | maxConsecutiveFailures_(1), |
| 61 | randomize_(true), |
| 62 | alwaysTryLast_(true) { |
| 63 | } |
| 64 | |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 65 | TSocketPool::TSocketPool(const vector<string> &hosts, |
| 66 | const vector<int> &ports) : TSocket(), |
| 67 | numRetries_(1), |
| 68 | retryInterval_(60), |
| 69 | maxConsecutiveFailures_(1), |
| 70 | randomize_(true), |
| 71 | alwaysTryLast_(true) |
| 72 | { |
| 73 | if (hosts.size() != ports.size()) { |
boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 74 | GlobalOutput("TSocketPool::TSocketPool: hosts.size != ports.size"); |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 75 | throw TTransportException(TTransportException::BAD_ARGS); |
| 76 | } |
| 77 | |
| 78 | for (unsigned int i = 0; i < hosts.size(); ++i) { |
dweatherford | d137282 | 2007-10-09 22:57:23 +0000 | [diff] [blame] | 79 | addServer(hosts[i], ports[i]); |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 80 | } |
| 81 | } |
| 82 | |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 83 | TSocketPool::TSocketPool(const vector<pair<string, int> >& servers) : TSocket(), |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 84 | numRetries_(1), |
| 85 | retryInterval_(60), |
| 86 | maxConsecutiveFailures_(1), |
| 87 | randomize_(true), |
| 88 | alwaysTryLast_(true) |
| 89 | { |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 90 | for (unsigned i = 0; i < servers.size(); ++i) { |
| 91 | addServer(servers[i].first, servers[i].second); |
| 92 | } |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 93 | } |
| 94 | |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 95 | TSocketPool::TSocketPool(const vector< shared_ptr<TSocketPoolServer> >& servers) : TSocket(), |
David Reiss | 907ad76 | 2008-03-02 00:25:58 +0000 | [diff] [blame] | 96 | servers_(servers), |
| 97 | numRetries_(1), |
| 98 | retryInterval_(60), |
| 99 | maxConsecutiveFailures_(1), |
| 100 | randomize_(true), |
| 101 | alwaysTryLast_(true) |
| 102 | { |
| 103 | } |
| 104 | |
dweatherford | d137282 | 2007-10-09 22:57:23 +0000 | [diff] [blame] | 105 | TSocketPool::TSocketPool(const string& host, int port) : TSocket(), |
| 106 | numRetries_(1), |
| 107 | retryInterval_(60), |
| 108 | maxConsecutiveFailures_(1), |
| 109 | randomize_(true), |
| 110 | alwaysTryLast_(true) |
| 111 | { |
| 112 | addServer(host, port); |
| 113 | } |
| 114 | |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 115 | TSocketPool::~TSocketPool() { |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 116 | vector< shared_ptr<TSocketPoolServer> >::const_iterator iter = servers_.begin(); |
| 117 | vector< shared_ptr<TSocketPoolServer> >::const_iterator iterEnd = servers_.end(); |
| 118 | for (; iter != iterEnd; ++iter) { |
| 119 | setCurrentServer(*iter); |
| 120 | TSocketPool::close(); |
| 121 | } |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 122 | } |
| 123 | |
dweatherford | d137282 | 2007-10-09 22:57:23 +0000 | [diff] [blame] | 124 | void TSocketPool::addServer(const string& host, int port) { |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 125 | servers_.push_back(shared_ptr<TSocketPoolServer>(new TSocketPoolServer(host, port))); |
dweatherford | d137282 | 2007-10-09 22:57:23 +0000 | [diff] [blame] | 126 | } |
| 127 | |
David Reiss | 8106ba6 | 2010-03-09 05:19:50 +0000 | [diff] [blame] | 128 | void TSocketPool::addServer(shared_ptr<TSocketPoolServer> &server) { |
| 129 | if (server) { |
| 130 | servers_.push_back(server); |
| 131 | } |
| 132 | } |
| 133 | |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 134 | void TSocketPool::setServers(const vector< shared_ptr<TSocketPoolServer> >& servers) { |
| 135 | servers_ = servers; |
| 136 | } |
| 137 | |
| 138 | void TSocketPool::getServers(vector< shared_ptr<TSocketPoolServer> >& servers) { |
| 139 | servers = servers_; |
David Reiss | 907ad76 | 2008-03-02 00:25:58 +0000 | [diff] [blame] | 140 | } |
| 141 | |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 142 | void TSocketPool::setNumRetries(int numRetries) { |
| 143 | numRetries_ = numRetries; |
| 144 | } |
| 145 | |
| 146 | void TSocketPool::setRetryInterval(int retryInterval) { |
| 147 | retryInterval_ = retryInterval; |
| 148 | } |
| 149 | |
| 150 | |
| 151 | void TSocketPool::setMaxConsecutiveFailures(int maxConsecutiveFailures) { |
| 152 | maxConsecutiveFailures_ = maxConsecutiveFailures; |
| 153 | } |
| 154 | |
| 155 | void TSocketPool::setRandomize(bool randomize) { |
| 156 | randomize_ = randomize; |
| 157 | } |
| 158 | |
| 159 | void TSocketPool::setAlwaysTryLast(bool alwaysTryLast) { |
| 160 | alwaysTryLast_ = alwaysTryLast; |
| 161 | } |
| 162 | |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 163 | void TSocketPool::setCurrentServer(const shared_ptr<TSocketPoolServer> &server) { |
| 164 | currentServer_ = server; |
| 165 | host_ = server->host_; |
| 166 | port_ = server->port_; |
| 167 | socket_ = server->socket_; |
| 168 | } |
| 169 | |
David Reiss | 767c1a9 | 2010-03-09 05:20:28 +0000 | [diff] [blame] | 170 | /** |
| 171 | * This function throws an exception if socket open fails. When socket |
| 172 | * opens fails, the socket in the current server is reset. |
| 173 | */ |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 174 | /* TODO: without apc we ignore a lot of functionality from the php version */ |
| 175 | void TSocketPool::open() { |
David Reiss | eed9299 | 2010-03-09 05:19:52 +0000 | [diff] [blame] | 176 | |
| 177 | unsigned int numServers = servers_.size(); |
David Reiss | 767c1a9 | 2010-03-09 05:20:28 +0000 | [diff] [blame] | 178 | if (numServers == 0) { |
| 179 | socket_ = -1; |
| 180 | throw TTransportException(TTransportException::NOT_OPEN); |
| 181 | } |
| 182 | |
| 183 | if (isOpen()) { |
David Reiss | eed9299 | 2010-03-09 05:19:52 +0000 | [diff] [blame] | 184 | return; |
| 185 | } |
| 186 | |
| 187 | if (randomize_ && numServers > 1) { |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 188 | random_shuffle(servers_.begin(), servers_.end()); |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 189 | } |
| 190 | |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 191 | for (unsigned int i = 0; i < numServers; ++i) { |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 192 | |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 193 | shared_ptr<TSocketPoolServer> &server = servers_[i]; |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 194 | // Impersonate the server socket |
| 195 | setCurrentServer(server); |
David Reiss | f50021a | 2008-02-29 07:33:47 +0000 | [diff] [blame] | 196 | |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 197 | if (isOpen()) { |
| 198 | // already open means we're done |
| 199 | return; |
| 200 | } |
| 201 | |
David Reiss | eed9299 | 2010-03-09 05:19:52 +0000 | [diff] [blame] | 202 | bool retryIntervalPassed = (server->lastFailTime_ == 0); |
| 203 | bool isLastServer = alwaysTryLast_ ? (i == (numServers - 1)) : false; |
| 204 | |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 205 | if (server->lastFailTime_ > 0) { |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 206 | // 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] | 207 | int elapsedTime = time(NULL) - server->lastFailTime_; |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 208 | if (elapsedTime > retryInterval_) { |
| 209 | retryIntervalPassed = true; |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 210 | } |
| 211 | } |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 212 | |
| 213 | if (retryIntervalPassed || isLastServer) { |
| 214 | for (int j = 0; j < numRetries_; ++j) { |
| 215 | try { |
| 216 | TSocket::open(); |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 217 | } catch (TException e) { |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 218 | string errStr = "TSocketPool::open failed "+getSocketInfo()+": "+e.what(); |
| 219 | GlobalOutput(errStr.c_str()); |
David Reiss | 767c1a9 | 2010-03-09 05:20:28 +0000 | [diff] [blame] | 220 | socket_ = -1; |
| 221 | continue; |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 222 | } |
David Reiss | 767c1a9 | 2010-03-09 05:20:28 +0000 | [diff] [blame] | 223 | |
| 224 | // Copy over the opened socket so that we can keep it persistent |
| 225 | server->socket_ = socket_; |
| 226 | // reset lastFailTime_ is required |
| 227 | server->lastFailTime_ = 0; |
| 228 | // success |
| 229 | return; |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 230 | } |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 231 | |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 232 | ++server->consecutiveFailures_; |
| 233 | if (server->consecutiveFailures_ > maxConsecutiveFailures_) { |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 234 | // Mark server as down |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 235 | server->consecutiveFailures_ = 0; |
| 236 | server->lastFailTime_ = time(NULL); |
David Reiss | 8f3bce4 | 2008-03-18 18:21:52 +0000 | [diff] [blame] | 237 | } |
David Reiss | 6d0cccd | 2008-02-28 21:20:12 +0000 | [diff] [blame] | 238 | } |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 239 | } |
| 240 | |
boz | 6ded775 | 2007-06-05 22:41:18 +0000 | [diff] [blame] | 241 | GlobalOutput("TSocketPool::open: all connections failed"); |
jsobel | e02e424 | 2007-05-08 17:51:49 +0000 | [diff] [blame] | 242 | throw TTransportException(TTransportException::NOT_OPEN); |
| 243 | } |
| 244 | |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 245 | void TSocketPool::close() { |
David Reiss | 767c1a9 | 2010-03-09 05:20:28 +0000 | [diff] [blame] | 246 | TSocket::close(); |
| 247 | if (currentServer_) { |
David Reiss | 1997f10 | 2008-04-29 00:29:41 +0000 | [diff] [blame] | 248 | currentServer_->socket_ = -1; |
| 249 | } |
| 250 | } |
| 251 | |
T Jake Luciani | b5e6221 | 2009-01-31 22:36:20 +0000 | [diff] [blame] | 252 | }}} // apache::thrift::transport |