Skip to content

Commit 9d2f0c8

Browse files
committed
middleware and retry with backup domains
1 parent 3c0ebee commit 9d2f0c8

File tree

10 files changed

+328
-9
lines changed

10 files changed

+328
-9
lines changed

src/Qiniu/Config.php

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ final class Config
3232
private $regionCache;
3333
// UC Host
3434
private $ucHost;
35+
// backup UC Hosts
36+
private $backupUcHosts;
37+
// backup UC Hosts max retry time
38+
public $backupUcHostsRetryTimes;
3539

3640
// 构造函数
3741
public function __construct(Region $z = null)
@@ -41,11 +45,17 @@ public function __construct(Region $z = null)
4145
$this->useCdnDomains = false;
4246
$this->regionCache = array();
4347
$this->ucHost = Config::UC_HOST;
48+
$this->backupUcHosts = array(
49+
"kodo-config.qiniuapi.com",
50+
"api.qiniu.com"
51+
);
52+
$this->backupUcHostsRetryTimes = 2;
4453
}
4554

46-
public function setUcHost($ucHost)
55+
public function setUcHost($ucHost, $backupUcHosts=array())
4756
{
4857
$this->ucHost = $ucHost;
58+
$this->backupUcHosts = $backupUcHosts;
4959
}
5060

5161
public function getUcHost()
@@ -59,6 +69,18 @@ public function getUcHost()
5969
return $scheme . $this->ucHost;
6070
}
6171

72+
public function appendBackupUcHosts($hosts) {
73+
$this->backupUcHosts = array_merge($this->backupUcHosts, $hosts);
74+
}
75+
76+
public function prependBackupUcHosts($hosts) {
77+
$this->backupUcHosts = array_merge($hosts, $this->backupUcHosts);
78+
}
79+
80+
public function getBackupUcHosts() {
81+
return $this->backupUcHosts;
82+
}
83+
6284
public function getUpHost($accessKey, $bucket)
6385
{
6486
$region = $this->getRegion($accessKey, $bucket);
@@ -308,7 +330,13 @@ private function getRegion($accessKey, $bucket)
308330
return $regionCache;
309331
}
310332

311-
$region = Zone::queryZone($accessKey, $bucket, $this->getUcHost());
333+
$region = Zone::queryZone(
334+
$accessKey,
335+
$bucket,
336+
$this->getUcHost(),
337+
$this->getBackupUcHosts(),
338+
$this->backupUcHostsRetryTimes
339+
);
312340
if (is_array($region)) {
313341
list($region, $err) = $region;
314342
if ($err != null) {
@@ -332,7 +360,13 @@ private function getRegionV2($accessKey, $bucket)
332360
return array($regionCache, null);
333361
}
334362

335-
$region = Zone::queryZone($accessKey, $bucket, $this->getUcHost());
363+
$region = Zone::queryZone(
364+
$accessKey,
365+
$bucket,
366+
$this->getUcHost(),
367+
$this->getBackupUcHosts(),
368+
$this->backupUcHostsRetryTimes
369+
);
336370
if (is_array($region)) {
337371
list($region, $err) = $region;
338372
return array($region, $err);

src/Qiniu/Http/Client.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
namespace Qiniu\Http;
33

44
use Qiniu\Config;
5+
use Qiniu\Http\Middleware;
56

67
final class Client
78
{
@@ -14,7 +15,7 @@ final class Client
1415
public static function get($url, array $headers = array(), $opt = null)
1516
{
1617
$request = new Request('GET', $url, $headers, null, $opt);
17-
return self::sendRequest($request);
18+
return self::sendRequestWithMiddleware($request);
1819
}
1920

2021
/**
@@ -119,6 +120,18 @@ private static function userAgent()
119120
return $ua;
120121
}
121122

123+
/**
124+
* @param Request $request
125+
* @return Response
126+
*/
127+
public static function sendRequestWithMiddleware($request) {
128+
$middlewares = $request->opt->middlewares;
129+
$handle = Middleware\compose($middlewares, function ($req) {
130+
return Client::sendRequest($req);
131+
});
132+
return $handle($request);
133+
}
134+
122135
/**
123136
* @param Request $request
124137
* @return Response
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
namespace Qiniu\Http\Middleware;
3+
4+
use Qiniu\Http\Request;
5+
use Qiniu\Http\Response;
6+
7+
interface Middleware {
8+
/**
9+
* @param Request $request
10+
* @param callable(Request): Response $next
11+
* @return Response
12+
*/
13+
public function send($request, $next);
14+
}
15+
16+
/**
17+
* @param array<Middleware> $middlewares
18+
* @param callable(Request): Response $handler
19+
* @return callable(Request): Response
20+
*/
21+
function compose($middlewares, $handler) {
22+
$next = $handler;
23+
foreach (array_reverse($middlewares) as $middleware) {
24+
$next = function ($request) use ($middleware, $next) {
25+
return $middleware->send($request, $next);
26+
};
27+
}
28+
return $next;
29+
}
30+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
namespace Qiniu\Http\Middleware;
3+
4+
use Qiniu\Http\Request;
5+
use Qiniu\Http\Response;
6+
7+
class RetryDomainsMiddleware implements Middleware {
8+
/**
9+
* @var array<string> backup domains.
10+
*/
11+
private $backupDomains;
12+
13+
/**
14+
* @var numeric max retry times for each backup domains.
15+
*/
16+
private $maxRetryTimes;
17+
18+
/**
19+
* @param array<string> $backupDomains
20+
* @param numeric $maxRetryTimes
21+
*/
22+
public function __construct($backupDomains, $maxRetryTimes = 2)
23+
{
24+
$this->backupDomains = $backupDomains;
25+
$this->maxRetryTimes = $maxRetryTimes;
26+
}
27+
28+
/**
29+
* @param Request $request
30+
* @param callable(Request): Response $next
31+
* @return Response
32+
*/
33+
public function send($request, $next)
34+
{
35+
$response = null;
36+
$urlComponents = parse_url($request->url);
37+
38+
foreach (array_merge(array($urlComponents["host"]), $this->backupDomains) as $backupDomain) {
39+
$urlComponents["host"] = $backupDomain;
40+
$request->url = \Qiniu\unparse_url($urlComponents);
41+
$retriedTimes = 0;
42+
43+
while($retriedTimes < $this->maxRetryTimes) {
44+
$response = $next($request);
45+
46+
$retriedTimes += 1;
47+
48+
if ($response->ok()) {
49+
return $response;
50+
}
51+
}
52+
}
53+
54+
if (!$response) {
55+
$response = $next($request);
56+
}
57+
58+
return $response;
59+
}
60+
}

src/Qiniu/Http/Request.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,26 @@
33

44
final class Request
55
{
6+
/**
7+
* @var string
8+
*/
69
public $url;
10+
11+
/**
12+
* @var array<string, string>
13+
*/
714
public $headers;
15+
16+
/**
17+
* @var mixed|null
18+
*/
819
public $body;
20+
21+
/**
22+
* @var string
23+
*/
924
public $method;
25+
1026
/**
1127
* @var RequestOptions
1228
*/

src/Qiniu/Http/RequestOptions.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Qiniu\Http;
44

5+
use Qiniu\Http\Middleware\Middleware;
6+
57
final class RequestOptions
68
{
79

@@ -30,16 +32,23 @@ final class RequestOptions
3032
*/
3133
public $timeout_ms;
3234

35+
/**
36+
* @var array<Middleware>
37+
*/
38+
public $middlewares;
39+
3340
public function __construct(
3441
$connection_timeout = null,
3542
$connection_timeout_ms = null,
3643
$timeout = null,
37-
$timeout_ms = null
44+
$timeout_ms = null,
45+
$middlewares = array()
3846
) {
3947
$this->connection_timeout = $connection_timeout;
4048
$this->connection_timeout_ms = $connection_timeout_ms;
4149
$this->timeout = $timeout;
4250
$this->timeout_ms = $timeout_ms;
51+
$this->middlewares = $middlewares;
4352
}
4453

4554
public function getCurlOpt()

src/Qiniu/Region.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
use Qiniu\Http\Client;
55
use Qiniu\Http\Error;
6+
use Qiniu\Http\Middleware\RetryDomainsMiddleware;
7+
use Qiniu\Http\RequestOptions;
68

79
class Region
810
{
@@ -171,14 +173,21 @@ public static function regionSeoul()
171173
/*
172174
* GET /v2/query?ak=<ak>&bucket=<bucket>
173175
**/
174-
public static function queryRegion($ak, $bucket, $ucHost = null)
176+
public static function queryRegion($ak, $bucket, $ucHost = null, $backupUcHosts = array(), $retryTimes = 2)
175177
{
176178
$region = new Region();
177179
if (!$ucHost) {
178180
$ucHost = "https://" . Config::UC_HOST;
179181
}
180182
$url = $ucHost . '/v4/query' . "?ak=$ak&bucket=$bucket";
181-
$ret = Client::Get($url);
183+
$reqOpt = new RequestOptions();
184+
$reqOpt->middlewares = array(
185+
new RetryDomainsMiddleware(
186+
$backupUcHosts,
187+
$retryTimes
188+
)
189+
);
190+
$ret = Client::Get($url, array(), $reqOpt);
182191
if (!$ret->ok()) {
183192
return array(null, new Error($url, $ret));
184193
}

src/Qiniu/Zone.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ public static function qvmZonez1()
5050
return parent::qvmRegionHuabei();
5151
}
5252

53-
public static function queryZone($ak, $bucket, $ucHost = null)
53+
public static function queryZone($ak, $bucket, $ucHost = null, $backupUcHosts = array(), $retryTimes = 2)
5454
{
55-
return parent::queryRegion($ak, $bucket, $ucHost);
55+
return parent::queryRegion($ak, $bucket, $ucHost, $backupUcHosts, $retryTimes);
5656
}
5757
}

src/Qiniu/functions.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,4 +302,35 @@ function ucwords($str, $delimiters)
302302
return \ucwords($str, $delimiters);
303303
}
304304
}
305+
306+
/**
307+
* 将 parse_url 的结果转换回字符串
308+
* TODO: add unit test
309+
*
310+
* @param $parsed_url - parse_url 的结果
311+
* @return string
312+
*/
313+
function unparse_url($parsed_url) {
314+
315+
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
316+
317+
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
318+
319+
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
320+
321+
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
322+
323+
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
324+
325+
$pass = ($user || $pass) ? "$pass@" : '';
326+
327+
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
328+
329+
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
330+
331+
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
332+
333+
return "$scheme$user$pass$host$port$path$query$fragment";
334+
335+
}
305336
}

0 commit comments

Comments
 (0)