1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* \AppserverIo\Server\Workers\ThreadWorker |
5
|
|
|
* |
6
|
|
|
* NOTICE OF LICENSE |
7
|
|
|
* |
8
|
|
|
* This source file is subject to the Open Software License (OSL 3.0) |
9
|
|
|
* that is available through the world-wide-web at this URL: |
10
|
|
|
* http://opensource.org/licenses/osl-3.0.php |
11
|
|
|
* |
12
|
|
|
* PHP version 5 |
13
|
|
|
* |
14
|
|
|
* @author Johann Zelger <[email protected]> |
15
|
|
|
* @copyright 2015 TechDivision GmbH <[email protected]> |
16
|
|
|
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) |
17
|
|
|
* @link https://github.com/appserver-io/server |
18
|
|
|
* @link http://www.appserver.io |
19
|
|
|
*/ |
20
|
|
|
|
21
|
|
|
namespace AppserverIo\Server\Workers; |
22
|
|
|
|
23
|
|
|
use AppserverIo\Server\Dictionaries\ServerVars; |
24
|
|
|
use AppserverIo\Server\Interfaces\ConfigInterface; |
25
|
|
|
use AppserverIo\Server\Interfaces\ConnectionHandlerInterface; |
26
|
|
|
use AppserverIo\Server\Interfaces\RequestContextInterface; |
27
|
|
|
use AppserverIo\Server\Interfaces\ServerContextInterface; |
28
|
|
|
use AppserverIo\Server\Interfaces\ServerInterface; |
29
|
|
|
use AppserverIo\Server\Interfaces\WorkerInterface; |
30
|
|
|
use AppserverIo\Server\Exceptions\ModuleNotFoundException; |
31
|
|
|
use AppserverIo\Server\Exceptions\ConnectionHandlerNotFoundException; |
32
|
|
|
use AppserverIo\Server\RequestHandlerThread; |
33
|
|
|
use AppserverIo\Server\Sockets\SocketInterface; |
34
|
|
|
use AppserverIo\Server\Sockets\StreamSocket; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Class ThreadWorker |
38
|
|
|
* |
39
|
|
|
* @author Johann Zelger <[email protected]> |
40
|
|
|
* @copyright 2015 TechDivision GmbH <[email protected]> |
41
|
|
|
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) |
42
|
|
|
* @link https://github.com/appserver-io/server |
43
|
|
|
* @link http://www.appserver.io |
44
|
|
|
*/ |
45
|
|
|
class ThreadWorker extends \Thread implements WorkerInterface |
46
|
|
|
{ |
47
|
|
|
/** |
48
|
|
|
* Define's the default value for accept min count |
49
|
|
|
* |
50
|
|
|
* @var int |
51
|
|
|
*/ |
52
|
|
|
const DEFAULT_ACCEPT_MIN = 8; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Define's the default value for accept max count |
56
|
|
|
* |
57
|
|
|
* @var int |
58
|
|
|
*/ |
59
|
|
|
const DEFAULT_ACCEPT_MAX = 32; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Hold's the serer connection resource |
63
|
|
|
* |
64
|
|
|
* @var resource |
65
|
|
|
*/ |
66
|
|
|
protected $serverConnectionResource; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* Holds the server context object |
70
|
|
|
* |
71
|
|
|
* @var \AppserverIo\Server\Interfaces\ServerContextInterface |
72
|
|
|
*/ |
73
|
|
|
protected $serverContext; |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Hold's an array of connection handlers to use |
77
|
|
|
* |
78
|
|
|
* @var array |
79
|
|
|
*/ |
80
|
|
|
protected $connectionHandlers; |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Defines the minimum count of connections for the worker to accept |
84
|
|
|
* |
85
|
|
|
* @var int |
86
|
|
|
*/ |
87
|
|
|
protected $acceptMin; |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Defines the maximum count of connections for the worker to accept |
91
|
|
|
* |
92
|
|
|
* @var int |
93
|
|
|
*/ |
94
|
|
|
protected $acceptMax; |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* Flag if worker should be restarted by server |
98
|
|
|
* |
99
|
|
|
* @var bool |
100
|
|
|
*/ |
101
|
|
|
public $shouldRestart; |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Constructs the worker by setting the server context |
105
|
|
|
* |
106
|
|
|
* @param resource $serverConnectionResource The server's file descriptor resource |
107
|
|
|
* @param \AppserverIo\Server\Interfaces\ServerContextInterface $serverContext The server's context |
108
|
|
|
* @param array $connectionHandlers An array of connection handlers to use |
109
|
|
|
*/ |
110
|
|
|
public function __construct($serverConnectionResource, ServerContextInterface $serverContext, array $connectionHandlers) |
111
|
|
|
{ |
112
|
|
|
$this->serverConnectionResource = $serverConnectionResource; |
113
|
|
|
// connection context init |
114
|
|
|
$this->serverContext = $serverContext; |
115
|
|
|
// connection handler init |
116
|
|
|
$this->connectionHandlers = $connectionHandlers; |
117
|
|
|
// init woker |
118
|
|
|
$this->init(); |
119
|
|
|
// autostart worker |
120
|
|
|
$this->start(PTHREADS_INHERIT_NONE | PTHREADS_INHERIT_CONSTANTS | PTHREADS_ALLOW_HEADERS); |
|
|
|
|
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Init's the worker before it runs |
125
|
|
|
* |
126
|
|
|
* @return void |
127
|
|
|
*/ |
128
|
|
|
public function init() |
129
|
|
|
{ |
130
|
|
|
// get server config to local ref |
131
|
|
|
$serverConfig = $this->getServerContext()->getServerConfig(); |
132
|
|
|
// read min and max accept stuff out of config |
133
|
|
|
$this->acceptMin = $serverConfig->getWorkerAcceptMin(); |
134
|
|
|
$this->acceptMax = $serverConfig->getWorkerAcceptMax(); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Return's an array of connection handlers to use |
139
|
|
|
* |
140
|
|
|
* @return array |
141
|
|
|
*/ |
142
|
|
|
public function getConnectionHandlers() |
143
|
|
|
{ |
144
|
|
|
return $this->connectionHandlers; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Return's the server context instance |
149
|
|
|
* |
150
|
|
|
* @return \AppserverIo\Server\Interfaces\ServerContextInterface The server's context |
151
|
|
|
*/ |
152
|
|
|
public function getServerContext() |
153
|
|
|
{ |
154
|
|
|
return $this->serverContext; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Return's the server's connection resource ref |
159
|
|
|
* |
160
|
|
|
* @return resource |
161
|
|
|
*/ |
162
|
|
|
protected function getServerConnectionResource() |
163
|
|
|
{ |
164
|
|
|
return $this->serverConnectionResource; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* Starts the worker doing logic. |
169
|
|
|
* |
170
|
|
|
* @return void |
171
|
|
|
*/ |
172
|
|
|
public function run() |
173
|
|
|
{ |
174
|
|
|
// set current dir to base dir for relative dirs |
175
|
|
|
chdir(SERVER_BASEDIR); |
176
|
|
|
// setup environment for worker |
177
|
|
|
require SERVER_AUTOLOADER; |
178
|
|
|
// prepare worker for upcoming connections in specific context |
179
|
|
|
$this->prepare(); |
180
|
|
|
// register shutdown handler |
181
|
|
|
register_shutdown_function(array(&$this, "shutdown")); |
182
|
|
|
// do work |
183
|
|
|
$this->work(); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Prepares the worker's in it's own context for upcoming work to do on things |
188
|
|
|
* that can not be shared by using the init method in the parent's context. |
189
|
|
|
* |
190
|
|
|
* @return void |
191
|
|
|
*/ |
192
|
|
|
public function prepare() |
193
|
|
|
{ |
194
|
|
|
// get local ref of connection handlers |
195
|
|
|
$connectionHandlers = $this->getConnectionHandlers(); |
196
|
|
|
// iterate then and call prepare on the it's modules |
197
|
|
|
foreach ($connectionHandlers as $connectionHandler) { |
198
|
|
|
// iterate all modules of connection handler |
199
|
|
|
foreach ($connectionHandler->getModules() as $name => $moduleInstance) { |
200
|
|
|
// prepare things in worker context |
201
|
|
|
$moduleInstance->prepare(); |
202
|
|
|
} |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Implements the workers actual logic |
208
|
|
|
* |
209
|
|
|
* @return void |
210
|
|
|
* |
211
|
|
|
* @throws \AppserverIo\Server\Exceptions\ModuleNotFoundException |
212
|
|
|
* @throws \AppserverIo\Server\Exceptions\ConnectionHandlerNotFoundException |
213
|
|
|
*/ |
214
|
|
|
public function work() |
215
|
|
|
{ |
216
|
|
|
// get server context |
217
|
|
|
$serverContext = $this->getServerContext(); |
218
|
|
|
// get connection handlers |
219
|
|
|
$connectionHandlers = $this->getConnectionHandlers(); |
220
|
|
|
|
221
|
|
|
// set should restart initial flag |
222
|
|
|
$this->shouldRestart = false; |
223
|
|
|
|
224
|
|
|
try { |
225
|
|
|
// get socket type |
226
|
|
|
$socketType = $serverContext->getServerConfig()->getSocketType(); |
227
|
|
|
|
228
|
|
|
/** @var SocketInterface $socketType */ |
229
|
|
|
// build connection instance by resource |
230
|
|
|
$serverConnection = $socketType::getInstance($this->serverConnectionResource); |
231
|
|
|
|
232
|
|
|
// init connection count |
233
|
|
|
$connectionCount = 0; |
234
|
|
|
$connectionLimit = rand($this->getAcceptMin(), $this->getAcceptMax()); |
235
|
|
|
|
236
|
|
|
// while worker not reached connection limit accept connections and process |
237
|
|
|
while (++$connectionCount <= $connectionLimit) { |
238
|
|
|
// accept connections and process working connection by handlers |
239
|
|
|
if (($connection = $serverConnection->accept()) !== false) { |
240
|
|
|
/** |
241
|
|
|
* This is for testing async request processing only. |
242
|
|
|
* |
243
|
|
|
* It'll delegate the request handling to another thread which will be processed async. |
244
|
|
|
* |
245
|
|
|
// call async request handler to handle connection |
246
|
|
|
$requestHandler = new RequestHandlerThread( |
247
|
|
|
$connection->getConnectionResource(), |
248
|
|
|
$connectionHandlers, |
249
|
|
|
$serverContext, |
250
|
|
|
$this |
251
|
|
|
); */ |
252
|
|
|
|
253
|
|
|
// iterate all connection handlers to handle connection right |
254
|
|
|
foreach ($connectionHandlers as $connectionHandler) { |
255
|
|
|
// if connectionHandler handled connection than break out of foreach |
256
|
|
|
if ($connectionHandler->handle($connection, $this)) { |
257
|
|
|
break; |
258
|
|
|
} |
259
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
} |
262
|
|
|
} |
263
|
|
|
} catch (\Exception $e) { |
264
|
|
|
// log error |
265
|
|
|
$serverContext->getLogger()->error($e->__toString()); |
266
|
|
|
} |
267
|
|
|
// call internal shutdown |
268
|
|
|
$this->shutdown(); |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
/** |
272
|
|
|
* Does shutdown logic for worker if something breaks in process. |
273
|
|
|
* |
274
|
|
|
* This shutdown function will be called from specific connection handler if an error occurs, so the connection |
275
|
|
|
* handler can send an response in the correct protocol specifications and a new worker can be started |
276
|
|
|
* |
277
|
|
|
* @return void |
278
|
|
|
*/ |
279
|
|
View Code Duplication |
public function shutdown() |
|
|
|
|
280
|
|
|
{ |
281
|
|
|
// check if there was a fatal error caused shutdown |
282
|
|
|
$lastError = error_get_last(); |
283
|
|
|
if ($lastError['type'] === E_ERROR || $lastError['type'] === E_USER_ERROR) { |
284
|
|
|
// log error |
285
|
|
|
$this->getServerContext()->getLogger()->error($lastError['message']); |
286
|
|
|
} |
287
|
|
|
$this->shouldRestart = true; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* Return's if worker should be restarted by server |
292
|
|
|
* |
293
|
|
|
* @return bool |
294
|
|
|
*/ |
295
|
|
|
public function shouldRestart() |
296
|
|
|
{ |
297
|
|
|
return $this->shouldRestart; |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
/** |
301
|
|
|
* Return's the max count for the worker to accept |
302
|
|
|
* |
303
|
|
|
* @return int |
304
|
|
|
*/ |
305
|
|
|
public function getAcceptMax() |
306
|
|
|
{ |
307
|
|
|
if ($this->acceptMax) { |
308
|
|
|
return $this->acceptMax; |
309
|
|
|
} |
310
|
|
|
return self::DEFAULT_ACCEPT_MAX; |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* Return's the min count for the worker to accept |
315
|
|
|
* |
316
|
|
|
* @return int |
317
|
|
|
*/ |
318
|
|
|
public function getAcceptMin() |
319
|
|
|
{ |
320
|
|
|
if ($this->acceptMin) { |
321
|
|
|
return $this->acceptMin; |
322
|
|
|
} |
323
|
|
|
return self::DEFAULT_ACCEPT_MIN; |
324
|
|
|
} |
325
|
|
|
} |
326
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.