1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace BeyondCode\LaravelWebSockets\Console\Commands; |
4
|
|
|
|
5
|
|
|
use BeyondCode\LaravelWebSockets\Contracts\ChannelManager; |
6
|
|
|
use BeyondCode\LaravelWebSockets\Facades\StatisticsCollector as StatisticsCollectorFacade; |
7
|
|
|
use BeyondCode\LaravelWebSockets\Facades\WebSocketRouter; |
8
|
|
|
use BeyondCode\LaravelWebSockets\Server\Loggers\ConnectionLogger; |
9
|
|
|
use BeyondCode\LaravelWebSockets\Server\Loggers\HttpLogger; |
10
|
|
|
use BeyondCode\LaravelWebSockets\Server\Loggers\WebSocketsLogger; |
11
|
|
|
use BeyondCode\LaravelWebSockets\ServerFactory; |
12
|
|
|
use Illuminate\Console\Command; |
13
|
|
|
use Illuminate\Support\Facades\Cache; |
14
|
|
|
use React\EventLoop\Factory as LoopFactory; |
15
|
|
|
|
16
|
|
|
class StartServer extends Command |
17
|
|
|
{ |
18
|
|
|
/** |
19
|
|
|
* The name and signature of the console command. |
20
|
|
|
* |
21
|
|
|
* @var string |
22
|
|
|
*/ |
23
|
|
|
protected $signature = 'websockets:serve |
24
|
|
|
{--host=0.0.0.0} |
25
|
|
|
{--port=6001} |
26
|
|
|
{--disable-statistics : Disable the statistics tracking.} |
27
|
|
|
{--statistics-interval= : The amount of seconds to tick between statistics saving.} |
28
|
|
|
{--debug : Forces the loggers to be enabled and thereby overriding the APP_DEBUG setting.} |
29
|
|
|
{--loop : Programatically inject the loop.} |
30
|
|
|
'; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* The console command description. |
34
|
|
|
* |
35
|
|
|
* @var string|null |
36
|
|
|
*/ |
37
|
|
|
protected $description = 'Start the LaravelWebSockets server.'; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Get the loop instance. |
41
|
|
|
* |
42
|
|
|
* @var \React\EventLoop\LoopInterface |
43
|
|
|
*/ |
44
|
|
|
protected $loop; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* The Pusher server instance. |
48
|
|
|
* |
49
|
|
|
* @var \Ratchet\Server\IoServer |
50
|
|
|
*/ |
51
|
|
|
public $server; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Initialize the command. |
55
|
|
|
* |
56
|
|
|
* @return void |
57
|
|
|
*/ |
58
|
|
|
public function __construct() |
59
|
|
|
{ |
60
|
|
|
parent::__construct(); |
61
|
|
|
|
62
|
|
|
$this->loop = LoopFactory::create(); |
|
|
|
|
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Run the command. |
67
|
|
|
* |
68
|
|
|
* @return void |
69
|
|
|
*/ |
70
|
|
|
public function handle() |
71
|
|
|
{ |
72
|
|
|
$this->configureLoggers(); |
73
|
|
|
|
74
|
|
|
$this->configureManagers(); |
75
|
|
|
|
76
|
|
|
$this->configureStatistics(); |
77
|
|
|
|
78
|
|
|
$this->configureRestartTimer(); |
79
|
|
|
|
80
|
|
|
$this->configureRoutes(); |
81
|
|
|
|
82
|
|
|
$this->configurePcntlSignal(); |
83
|
|
|
|
84
|
|
|
$this->configurePongTracker(); |
85
|
|
|
|
86
|
|
|
$this->startServer(); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Configure the loggers used for the console. |
91
|
|
|
* |
92
|
|
|
* @return void |
93
|
|
|
*/ |
94
|
|
|
protected function configureLoggers() |
95
|
|
|
{ |
96
|
|
|
$this->configureHttpLogger(); |
97
|
|
|
$this->configureMessageLogger(); |
98
|
|
|
$this->configureConnectionLogger(); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Register the managers that are not resolved |
103
|
|
|
* in the package service provider. |
104
|
|
|
* |
105
|
|
|
* @return void |
106
|
|
|
*/ |
107
|
|
|
protected function configureManagers() |
108
|
|
|
{ |
109
|
|
|
$this->laravel->singleton(ChannelManager::class, function ($app) { |
110
|
|
|
$config = $app['config']['websockets']; |
111
|
|
|
$mode = $config['replication']['mode'] ?? 'local'; |
112
|
|
|
|
113
|
|
|
$class = $config['replication']['modes'][$mode]['channel_manager']; |
114
|
|
|
|
115
|
|
|
return new $class($this->loop); |
116
|
|
|
}); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* Register the Statistics Collectors that |
121
|
|
|
* are not resolved in the package service provider. |
122
|
|
|
* |
123
|
|
|
* @return void |
124
|
|
|
*/ |
125
|
|
|
protected function configureStatistics() |
126
|
|
|
{ |
127
|
|
|
if (! $this->option('disable-statistics')) { |
128
|
|
|
$intervalInSeconds = $this->option('statistics-interval') ?: config('websockets.statistics.interval_in_seconds', 3600); |
129
|
|
|
|
130
|
|
|
$this->loop->addPeriodicTimer($intervalInSeconds, function () { |
|
|
|
|
131
|
|
|
$this->line('Saving statistics...'); |
132
|
|
|
|
133
|
|
|
StatisticsCollectorFacade::save(); |
134
|
|
|
}); |
135
|
|
|
} |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* Configure the restart timer. |
140
|
|
|
* |
141
|
|
|
* @return void |
142
|
|
|
*/ |
143
|
|
|
public function configureRestartTimer() |
144
|
|
|
{ |
145
|
|
|
$this->lastRestart = $this->getLastRestart(); |
|
|
|
|
146
|
|
|
|
147
|
|
|
$this->loop->addPeriodicTimer(10, function () { |
148
|
|
|
if ($this->getLastRestart() !== $this->lastRestart) { |
149
|
|
|
$this->triggerSoftShutdown(); |
150
|
|
|
} |
151
|
|
|
}); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Register the routes for the server. |
156
|
|
|
* |
157
|
|
|
* @return void |
158
|
|
|
*/ |
159
|
|
|
protected function configureRoutes() |
160
|
|
|
{ |
161
|
|
|
WebSocketRouter::registerRoutes(); |
|
|
|
|
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Configure the PCNTL signals for soft shutdown. |
166
|
|
|
* |
167
|
|
|
* @return void |
168
|
|
|
*/ |
169
|
|
|
protected function configurePcntlSignal() |
170
|
|
|
{ |
171
|
|
|
// When the process receives a SIGTERM or a SIGINT |
172
|
|
|
// signal, it should mark the server as unavailable |
173
|
|
|
// to receive new connections, close the current connections, |
174
|
|
|
// then stopping the loop. |
175
|
|
|
|
176
|
|
|
if (! extension_loaded('pcntl')) { |
177
|
|
|
return; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
$this->loop->addSignal(SIGTERM, function () { |
181
|
|
|
$this->line('Closing existing connections...'); |
182
|
|
|
|
183
|
|
|
$this->triggerSoftShutdown(); |
184
|
|
|
}); |
185
|
|
|
|
186
|
|
|
$this->loop->addSignal(SIGINT, function () { |
187
|
|
|
$this->line('Closing existing connections...'); |
188
|
|
|
|
189
|
|
|
$this->triggerSoftShutdown(); |
190
|
|
|
}); |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Configure the tracker that will delete |
195
|
|
|
* from the store the connections that. |
196
|
|
|
* |
197
|
|
|
* @return void |
198
|
|
|
*/ |
199
|
|
|
protected function configurePongTracker() |
200
|
|
|
{ |
201
|
|
|
$this->loop->addPeriodicTimer(10, function () { |
202
|
|
|
$this->laravel |
203
|
|
|
->make(ChannelManager::class) |
204
|
|
|
->removeObsoleteConnections(); |
205
|
|
|
}); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Configure the HTTP logger class. |
210
|
|
|
* |
211
|
|
|
* @return void |
212
|
|
|
*/ |
213
|
|
|
protected function configureHttpLogger() |
214
|
|
|
{ |
215
|
|
|
$this->laravel->singleton(HttpLogger::class, function ($app) { |
216
|
|
|
return (new HttpLogger($this->output)) |
217
|
|
|
->enable($this->option('debug') ?: ($app['config']['app']['debug'] ?? false)) |
|
|
|
|
218
|
|
|
->verbose($this->output->isVerbose()); |
219
|
|
|
}); |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* Configure the logger for messages. |
224
|
|
|
* |
225
|
|
|
* @return void |
226
|
|
|
*/ |
227
|
|
|
protected function configureMessageLogger() |
228
|
|
|
{ |
229
|
|
|
$this->laravel->singleton(WebSocketsLogger::class, function ($app) { |
230
|
|
|
return (new WebSocketsLogger($this->output)) |
231
|
|
|
->enable($this->option('debug') ?: ($app['config']['app']['debug'] ?? false)) |
|
|
|
|
232
|
|
|
->verbose($this->output->isVerbose()); |
233
|
|
|
}); |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* Configure the connection logger. |
238
|
|
|
* |
239
|
|
|
* @return void |
240
|
|
|
*/ |
241
|
|
|
protected function configureConnectionLogger() |
242
|
|
|
{ |
243
|
|
|
$this->laravel->bind(ConnectionLogger::class, function ($app) { |
244
|
|
|
return (new ConnectionLogger($this->output)) |
245
|
|
|
->enable($app['config']['app']['debug'] ?? false) |
246
|
|
|
->verbose($this->output->isVerbose()); |
247
|
|
|
}); |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* Start the server. |
252
|
|
|
* |
253
|
|
|
* @return void |
254
|
|
|
*/ |
255
|
|
|
protected function startServer() |
256
|
|
|
{ |
257
|
|
|
$this->info("Starting the WebSocket server on port {$this->option('port')}..."); |
258
|
|
|
|
259
|
|
|
$this->buildServer(); |
260
|
|
|
|
261
|
|
|
$this->server->run(); |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* Build the server instance. |
266
|
|
|
* |
267
|
|
|
* @return void |
268
|
|
|
*/ |
269
|
|
|
protected function buildServer() |
270
|
|
|
{ |
271
|
|
|
$this->server = new ServerFactory( |
|
|
|
|
272
|
|
|
$this->option('host'), $this->option('port') |
273
|
|
|
); |
274
|
|
|
|
275
|
|
|
if ($loop = $this->option('loop')) { |
276
|
|
|
$this->loop = $loop; |
|
|
|
|
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
$this->server = $this->server |
280
|
|
|
->setLoop($this->loop) |
281
|
|
|
->withRoutes(WebSocketRouter::getRoutes()) |
|
|
|
|
282
|
|
|
->setConsoleOutput($this->output) |
283
|
|
|
->createServer(); |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
/** |
287
|
|
|
* Get the last time the server restarted. |
288
|
|
|
* |
289
|
|
|
* @return int |
290
|
|
|
*/ |
291
|
|
|
protected function getLastRestart() |
292
|
|
|
{ |
293
|
|
|
return Cache::get( |
294
|
|
|
'beyondcode:websockets:restart', 0 |
295
|
|
|
); |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
/** |
299
|
|
|
* Trigger a soft shutdown for the process. |
300
|
|
|
* |
301
|
|
|
* @return void |
302
|
|
|
*/ |
303
|
|
|
protected function triggerSoftShutdown() |
304
|
|
|
{ |
305
|
|
|
$channelManager = $this->laravel->make(ChannelManager::class); |
306
|
|
|
|
307
|
|
|
// Close the new connections allowance on this server. |
308
|
|
|
$channelManager->declineNewConnections(); |
|
|
|
|
309
|
|
|
|
310
|
|
|
// Get all local connections and close them. They will |
311
|
|
|
// be automatically be unsubscribed from all channels. |
312
|
|
|
$channelManager->getLocalConnections() |
313
|
|
|
->then(function ($connections) { |
314
|
|
|
foreach ($connections as $connection) { |
315
|
|
|
$connection->close(); |
316
|
|
|
} |
317
|
|
|
}) |
318
|
|
|
->then(function () { |
319
|
|
|
$this->loop->stop(); |
320
|
|
|
}); |
321
|
|
|
} |
322
|
|
|
} |
323
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.