-
Notifications
You must be signed in to change notification settings - Fork 1
/
SentinelClient.php
160 lines (143 loc) · 4.46 KB
/
SentinelClient.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
<?php
namespace Sentinel;
use Sentinel\Rpc\SentinelRpcClient;
use Thrift\Exception\TException;
use Thrift\Exception\TTransportException;
use Thrift\Protocol\TBinaryProtocolAccelerated;
use Thrift\Transport\TFramedTransport;
use Thrift\Transport\TSocket;
/**
* Sentinel 客户端。通过 RPC 调用 sidecar 实现。
* TODO 多线程时底层 RPC 通信必须加锁串行访问
*
* @package Sentinel
*/
class SentinelClient
{
/**
* 当前进程 PID 。
* @var int
*/
protected $pid_ = null;
/**
* @var string
*/
protected $addr_ = null;
/**
* @var bool
*/
protected $persist_ = null;
/**
* 底层 RPC 通信 socket 。
*
* @var TSocket
*/
protected $socket_ = null;
/**
* @var SentinelRpcClient
*/
protected $client_ = null;
/**
* SentinelClient constructor.
* TODO 是否可使用 persistent socket 优化性能 ?
*
* @param string $host Sentinel sidecar socket hostname
* @param int $port Sentinel sidecar socket port
* @param bool $persist Whether to use a persistent socket
*/
public function __construct(
$host = 'localhost',
$port = -1,
$persist = true
)
{
// CentOS 上 posix_getpid 可能被移动到 php-process 软件包, 基础环境可能缺少此方法.
if (function_exists('\posix_getpid')) {
$this->pid_ = \posix_getpid();
} else if (function_exists('\getmypid')) {
$this->pid_ = \getmypid();
}
if ($port == -1) {
$this->addr_ = $host;
} else {
$this->addr_ = $host . ":" . "$port";
}
$this->persist_ = $persist;
$this->socket_ = new TSocket($host, $port, $persist);
$trans = new TFramedTransport($this->socket_);
$protocol = new TBinaryProtocolAccelerated($trans, true, true);
$input = $protocol;
$output = $protocol;
$this->client_ = new SentinelRpcClient($input, $output);
}
/**
* 确认打开底层 socket 连接。
*
* @throws TTransportException
* @throws TException
*/
protected function ensureOpen() {
if (!$this->socket_->isOpen()) {
$this->socket_->open();
# 打印连接成功日志? 应使用调试日志并默认关闭, 避免频繁打印刷屏。
#error_log("PID=$this->pid_, SentinelClient(persist=$this->persist_) connected to sidecar $this->addr_ .");
}
}
/**
* 获取受保护的资源访问入口。
* 返回对象不再被引用后, 将自动释放访问入口。
*
* 注意: 必须定义一个变量持有访问入口, 否则返回对象被自动销毁, 将自动释放访问入口。
*
* @param string $name 资源名称
* @return SentinelEntry
* 流控通过时返回资源访问入口对象。
* 客户端异常 (如连接服务器失败等情况) 时返回 null, 此时流控防护失效。
* @throws BlockException 当前访问被限流时抛出 \Sentinel\BlockException 异常。
*/
public function entry($name) {
try {
return $this->doEntry($name);
} catch (BlockException $e) {
throw $e;
} catch (\Exception $e) {
// 捕获除 BlockException 之外的所有异常, 打印错误日志并返回 null 。
// TODO 打印错误日志, 使用 error_log() 函数还是 monolog 库 ?
#error_log("SentinelClient entry() error: $e");
return null;
}
}
/**
* 底层操作, 应用代码请使用 `entry($name)` 。
*
* @param string $name 资源名称
* @return SentinelEntry
* @throws BlockException
* @throws TException
* @throws TTransportException
* @see entry
*/
protected function doEntry($name) {
$this->ensureOpen();
$id = $this->client_->entry($name);
return new SentinelEntry($this, $id);
}
/**
* @param $entry SentinelEntry
*/
public function close($entry) {
$this->doClose($entry->id_);
}
/**
* @param $id int
*/
protected function doClose($id) {
try {
$this->client_->close($id);
} catch (\Exception $e) {
// 捕获所有异常, 打印错误日志。
// TODO 打印错误日志, 使用 error_log() 函数还是 monolog 库 ?
#error_log("SentinelClient close() error: $e");
}
}
}