Merge branch 'master' of github.com:beyondcode/laravel-websockets

This commit is contained in:
Marcel Pociot 2018-12-04 01:05:14 +01:00
commit dd283c66d1
7 changed files with 79 additions and 58 deletions

View File

@ -42,10 +42,19 @@ return [
*/ */
'max_request_size_in_kb' => 250, 'max_request_size_in_kb' => 250,
'statistics' => [
/* /*
* This model will be used to store the statistics of the WebSocketsServer * This model will be used to store the statistics of the WebSocketsServer.
* The only requirement is that the model should be or extend
* `WebSocketsStatisticsEntry` provided by this package.
*/ */
'statistics_model' => \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry::class, 'model' => \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry::class,
/*
* Here you can specify the interval in seconds at which statistics should be logged.
*/
'interval_in_seconds' => 60,
],
/* /*
* Define the optional SSL context for your WebSocket connections. * Define the optional SSL context for your WebSocket connections.

View File

@ -53,12 +53,11 @@ class StartWebSocketServer extends Command
$browser = new Browser($this->loop, $connector); $browser = new Browser($this->loop, $connector);
app()->singleton(StatisticsLoggerInterface::class, function() use ($browser) { app()->singleton(StatisticsLoggerInterface::class, function() use ($browser) {
return new HttpStatisticsLogger(app(ChannelManager::class), $browser); return new HttpStatisticsLogger(app(ChannelManager::class), $browser);
}); });
$this->loop->addPeriodicTimer(5, function() { $this->loop->addPeriodicTimer(config('websockets.statistics.interval_in_seconds'), function() {
StatisticsLogger::save(); StatisticsLogger::save();
}); });

View File

@ -3,6 +3,7 @@
namespace BeyondCode\LaravelWebsockets\Statistics\Events; namespace BeyondCode\LaravelWebsockets\Statistics\Events;
use BeyondCode\LaravelWebSockets\Dashboard\DashboardLogger; use BeyondCode\LaravelWebSockets\Dashboard\DashboardLogger;
use BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry;
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
@ -11,27 +12,30 @@ class StatisticsUpdated implements ShouldBroadcast
{ {
use SerializesModels; use SerializesModels;
public $statisticModel; /** @var \BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry */
protected $webSocketsStatisticsEntry;
public function __construct($statisticModel) public function __construct(WebSocketsStatisticsEntry $webSocketsStatisticsEntry)
{ {
$this->statisticModel = $statisticModel; $this->webSocketsStatisticsEntry = $webSocketsStatisticsEntry;
} }
public function broadcastWith() public function broadcastWith()
{ {
return [ return [
'time' => (string)$this->statisticModel->created_at, 'time' => $this->webSocketsStatisticsEntry->created_at->timestamp,
'app_id' => $this->statisticModel->app_id, 'app_id' => $this->webSocketsStatisticsEntry->appId,
'peak_connection_count' => $this->statisticModel->peak_connection_count, 'peak_connection_count' => $this->webSocketsStatisticsEntry->peakConnectionCount,
'websocket_message_count' => $this->statisticModel->websocket_message_count, 'websocket_message_count' => $this->webSocketsStatisticsEntry->webSocketMessageCount,
'api_message_count' => $this->statisticModel->api_message_count, 'api_message_count' => $this->webSocketsStatisticsEntry->apiMessageCount,
]; ];
} }
public function broadcastOn() public function broadcastOn()
{ {
return new PrivateChannel(str_after(DashboardLogger::LOG_CHANNEL_PREFIX . 'statistics', 'private-')); $channelName = str_after(DashboardLogger::LOG_CHANNEL_PREFIX . 'statistics', 'private-');
return new PrivateChannel($channelName);
} }
public function broadcastAs() public function broadcastAs()

View File

@ -17,7 +17,7 @@ class WebSocketStatisticsEntriesController
'api_message_count' => 'required|integer', 'api_message_count' => 'required|integer',
]); ]);
$webSocketsStatisticsEntryModelClass = config('websockets.statistics_model'); $webSocketsStatisticsEntryModelClass = config('websockets.statistics.model');
$statisticModel = $webSocketsStatisticsEntryModelClass::create($validatedAttributes); $statisticModel = $webSocketsStatisticsEntryModelClass::create($validatedAttributes);

View File

@ -11,13 +11,13 @@ use Ratchet\ConnectionInterface;
class HttpStatisticsLogger implements StatisticsLogger class HttpStatisticsLogger implements StatisticsLogger
{ {
/** @var Statistic[] */ /** @var \BeyondCode\LaravelWebSockets\Statistics\Statistic[] */
protected $statistics = []; protected $statistics = [];
/** @var \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager */ /** @var \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager */
protected $channelManager; protected $channelManager;
/** @var Browser */ /** @var \Clue\React\Buzz\Browser */
protected $browser; protected $browser;
public function __construct(ChannelManager $channelManager, Browser $browser) public function __construct(ChannelManager $channelManager, Browser $browser)
@ -29,37 +29,39 @@ class HttpStatisticsLogger implements StatisticsLogger
public function webSocketMessage(ConnectionInterface $connection) public function webSocketMessage(ConnectionInterface $connection)
{ {
$this->initializeStatistics($connection->app->id); $this
->findOrMakeStatisticForAppId($connection->app->id)
$this->statistics[$connection->app->id]->webSocketMessage(); ->webSocketMessage();
} }
public function apiMessage($appId) public function apiMessage($appId)
{ {
$this->initializeStatistics($appId); $this
->findOrMakeStatisticForAppId($appId)
$this->statistics[$appId]->apiMessage(); ->apiMessage();
} }
public function connection(ConnectionInterface $connection) public function connection(ConnectionInterface $connection)
{ {
$this->initializeStatistics($connection->app->id); $this
->findOrMakeStatisticForAppId($connection->app->id)
$this->statistics[$connection->app->id]->connection(); ->connection();
} }
public function disconnection(ConnectionInterface $connection) public function disconnection(ConnectionInterface $connection)
{ {
$this->initializeStatistics($connection->app->id); $this
->findOrMakeStatisticForAppId($connection->app->id)
$this->statistics[$connection->app->id]->disconnection(); ->disconnection();
} }
protected function initializeStatistics($id) protected function findOrMakeStatisticForAppId($appId): Statistic
{ {
if (!isset($this->statistics[$id])) { if (!isset($this->statistics[$appId])) {
$this->statistics[$id] = new Statistic($id); $this->statistics[$appId] = new Statistic($appId);
} }
return $this->statistics[$appId];
} }
public function save() public function save()
@ -70,19 +72,15 @@ class HttpStatisticsLogger implements StatisticsLogger
continue; continue;
} }
$this->browser $this
->browser
->post( ->post(
action([WebSocketStatisticsEntriesController::class, 'store']), action([WebSocketStatisticsEntriesController::class, 'store']),
['Content-Type' => 'application/json'], ['Content-Type' => 'application/json'],
stream_for(json_encode($statistic->toArray())) stream_for(json_encode($statistic->toArray()))
); );
// Reset connection and message count $currentConnectionCount = $this->channelManager->getConnectionCount($appId);
$currentConnectionCount = collect($this->channelManager->getChannels($appId))
->sum(function ($channel) {
return count($channel->getSubscribedConnections());
});
$statistic->reset($currentConnectionCount); $statistic->reset($currentConnectionCount);
} }
} }

View File

@ -46,6 +46,12 @@ class ChannelManager
return $this->channels[$appId] ?? []; return $this->channels[$appId] ?? [];
} }
public function getConnectionCount(string $appId): int
{
return collect($this->getChannels($appId))
->sum->getSubscribedConnections();
}
public function removeFromAllChannels(ConnectionInterface $connection) public function removeFromAllChannels(ConnectionInterface $connection)
{ {
if (!isset($connection->app)) { if (!isset($connection->app)) {

View File

@ -22,22 +22,21 @@ class WebSocketsServiceProvider extends ServiceProvider
public function boot() public function boot()
{ {
$this->publishes([ $this->publishes([
__DIR__.'/../config/websockets.php' => base_path('config/websockets.php'), __DIR__ . '/../config/websockets.php' => base_path('config/websockets.php'),
], 'config'); ], 'config');
if (! class_exists('CreateWebSocketsStatisticsEntries')) { if (!class_exists('CreateWebSocketsStatisticsEntries')) {
$this->publishes([ $this->publishes([
__DIR__.'/../database/migrations/create_websockets_statistics_entries_table.php.stub' => database_path('migrations/'.date('Y_m_d_His', time()).'_create_websockets_statistics_entries_table.php'), __DIR__ . '/../database/migrations/create_websockets_statistics_entries_table.php.stub' => database_path('migrations/' . date('Y_m_d_His', time()) . '_create_websockets_statistics_entries_table.php'),
], 'migrations'); ], 'migrations');
} }
$this->registerRouteMacro(); $this
->registerRouteMacro()
->registerStatisticRoute()
->registerDashboardGate();
$this->registerStatisticRoute(); $this->loadViewsFrom(__DIR__ . '/../resources/views/', 'websockets');
$this->registerDashboardGate();
$this->loadViewsFrom(__DIR__.'/../resources/views/', 'websockets');
$this->commands([ $this->commands([
Console\StartWebSocketServer::class, Console\StartWebSocketServer::class,
@ -46,38 +45,42 @@ class WebSocketsServiceProvider extends ServiceProvider
public function register() public function register()
{ {
$this->mergeConfigFrom(__DIR__.'/../config/websockets.php', 'websockets'); $this->mergeConfigFrom(__DIR__ . '/../config/websockets.php', 'websockets');
$this->app->singleton('websockets.router', function() { $this->app->singleton('websockets.router', function () {
return new Router(); return new Router();
}); });
$this->app->singleton(ChannelManager::class, function() { $this->app->singleton(ChannelManager::class, function () {
return new ChannelManager(); return new ChannelManager();
}); });
$this->app->singleton(AppProvider::class, function() { $this->app->singleton(AppProvider::class, function () {
return app(config('websockets.app_provider')); return app(config('websockets.app_provider'));
}); });
} }
protected function registerRouteMacro() protected function registerRouteMacro()
{ {
Route::macro('webSockets', function($prefix = 'laravel-websockets') { Route::macro('webSockets', function ($prefix = 'laravel-websockets') {
Route::prefix($prefix)->namespace('\\')->middleware(Authorize::class)->group(function() { Route::prefix($prefix)->namespace('\\')->middleware(Authorize::class)->group(function () {
Route::get('/', ShowDashboard::class); Route::get('/', ShowDashboard::class);
Route::get('/api/{appId}/statistics', DashboardApiController::class.'@getStatistics'); Route::get('/api/{appId}/statistics', DashboardApiController::class . '@getStatistics');
Route::post('auth', AuthenticateDashboard::class); Route::post('auth', AuthenticateDashboard::class);
Route::post('event', SendMessage::class); Route::post('event', SendMessage::class);
}); });
}); });
return $this;
} }
protected function registerStatisticRoute() protected function registerStatisticRoute()
{ {
Route::prefix('/laravel-websockets')->namespace('\\')->group(function() { Route::prefix('/laravel-websockets')->namespace('\\')->group(function () {
Route::post('statistics', [WebSocketStatisticsEntriesController::class, 'store']); Route::post('statistics', [WebSocketStatisticsEntriesController::class, 'store']);
}); });
return $this;
} }
protected function registerDashboardGate() protected function registerDashboardGate()
@ -85,5 +88,7 @@ class WebSocketsServiceProvider extends ServiceProvider
Gate::define('viewWebSocketsDashboard', function ($user = null) { Gate::define('viewWebSocketsDashboard', function ($user = null) {
return app()->environment('local'); return app()->environment('local');
}); });
return $this;
} }
} }