Merge pull request #475 from beyondcode/fix/memory-leaks
[fix] Memory Leaks
This commit is contained in:
commit
a08eee60e1
|
|
@ -239,37 +239,6 @@ return [
|
||||||
|
|
||||||
'delete_statistics_older_than_days' => 60,
|
'delete_statistics_older_than_days' => 60,
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| DNS Lookup
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Use an DNS resolver to make the requests to the statistics logger
|
|
||||||
| default is to resolve everything to 127.0.0.1.
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'perform_dns_lookup' => false,
|
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| DNS Lookup TLS Settings
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| You can configure the DNS Lookup Connector the TLS settings.
|
|
||||||
| Check the available options here:
|
|
||||||
| https://github.com/reactphp/socket/blob/master/src/Connector.php#L29
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
'tls' => [
|
|
||||||
|
|
||||||
'verify_peer' => env('APP_ENV') === 'production',
|
|
||||||
|
|
||||||
'verify_peer_name' => env('APP_ENV') === 'production',
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -84,9 +84,29 @@
|
||||||
v-if="connected && app.statisticsEnabled"
|
v-if="connected && app.statisticsEnabled"
|
||||||
class="w-full my-6 px-6"
|
class="w-full my-6 px-6"
|
||||||
>
|
>
|
||||||
<div class="font-semibold uppercase text-gray-700">
|
<div class="flex justify-between items-center">
|
||||||
Live statistics
|
<span class="font-semibold uppercase text-gray-700">
|
||||||
</div>
|
Live statistics
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="space-x-3 flex items-center">
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
v-model="autoRefresh"
|
||||||
|
class="mr-2"
|
||||||
|
/>
|
||||||
|
Refresh automatically
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
@click="loadChart"
|
||||||
|
class="rounded-full bg-blue-500 hover:bg-blue-600 focus:outline-none text-white px-3 py-1"
|
||||||
|
>
|
||||||
|
Refresh
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="statisticsChart"
|
id="statisticsChart"
|
||||||
|
|
@ -222,6 +242,9 @@
|
||||||
connected: false,
|
connected: false,
|
||||||
connecting: false,
|
connecting: false,
|
||||||
sendingEvent: false,
|
sendingEvent: false,
|
||||||
|
autoRefresh: true,
|
||||||
|
refreshInterval: {{ $refreshInterval }},
|
||||||
|
refreshTicker: null,
|
||||||
chart: null,
|
chart: null,
|
||||||
pusher: null,
|
pusher: null,
|
||||||
app: null,
|
app: null,
|
||||||
|
|
@ -236,6 +259,19 @@
|
||||||
mounted () {
|
mounted () {
|
||||||
this.app = this.apps[0] || null;
|
this.app = this.apps[0] || null;
|
||||||
},
|
},
|
||||||
|
destroyed () {
|
||||||
|
if (this.refreshTicker) {
|
||||||
|
this.clearRefreshInterval();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
connected (newVal) {
|
||||||
|
newVal ? this.startRefreshInterval() : this.clearRefreshInterval();
|
||||||
|
},
|
||||||
|
autoRefresh (newVal) {
|
||||||
|
newVal ? this.startRefreshInterval() : this.clearRefreshInterval();
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
connect () {
|
connect () {
|
||||||
this.connecting = true;
|
this.connecting = true;
|
||||||
|
|
@ -274,12 +310,14 @@
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
this.connecting = false;
|
this.connecting = false;
|
||||||
this.logs = [];
|
this.logs = [];
|
||||||
|
this.chart = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.pusher.connection.bind('error', event => {
|
this.pusher.connection.bind('error', event => {
|
||||||
if (event.error.data.code === 4100) {
|
if (event.error.data.code === 4100) {
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
this.logs = [];
|
this.logs = [];
|
||||||
|
this.chart = null;
|
||||||
|
|
||||||
throw new Error("Over capacity");
|
throw new Error("Over capacity");
|
||||||
}
|
}
|
||||||
|
|
@ -288,12 +326,12 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
this.subscribeToAllChannels();
|
this.subscribeToAllChannels();
|
||||||
this.subscribeToStatistics();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
disconnect () {
|
disconnect () {
|
||||||
this.pusher.disconnect();
|
this.pusher.disconnect();
|
||||||
this.connecting = false;
|
this.connecting = false;
|
||||||
|
this.chart = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
loadChart () {
|
loadChart () {
|
||||||
|
|
@ -333,7 +371,10 @@
|
||||||
autosize: true,
|
autosize: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.chart = Plotly.newPlot('statisticsChart', chartData, layout);
|
this.chart = this.chart
|
||||||
|
? Plotly.react('statisticsChart', chartData, layout)
|
||||||
|
: Plotly.newPlot('statisticsChart', chartData, layout);
|
||||||
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -348,18 +389,6 @@
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
subscribeToStatistics () {
|
|
||||||
this.pusher.subscribe('{{ $logPrefix }}statistics')
|
|
||||||
.bind('statistics-updated', (data) => {
|
|
||||||
var update = {
|
|
||||||
x: [[data.time], [data.time], [data.time]],
|
|
||||||
y: [[data.peak_connection_count], [data.websocket_message_count], [data.api_message_count]],
|
|
||||||
};
|
|
||||||
|
|
||||||
Plotly.extendTraces('statisticsChart', update, [0, 1, 2]);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
sendEvent () {
|
sendEvent () {
|
||||||
if (! this.sendingEvent) {
|
if (! this.sendingEvent) {
|
||||||
this.sendingEvent = true;
|
this.sendingEvent = true;
|
||||||
|
|
@ -415,6 +444,17 @@
|
||||||
|
|
||||||
return 'bg-gray-700 text-white';
|
return 'bg-gray-700 text-white';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
startRefreshInterval () {
|
||||||
|
this.refreshTicker = setInterval(function () {
|
||||||
|
this.loadChart();
|
||||||
|
}.bind(this), this.refreshInterval * 1000);
|
||||||
|
},
|
||||||
|
|
||||||
|
stopRefreshInterval () {
|
||||||
|
clearInterval(this.refreshTicker);
|
||||||
|
this.refreshTicker = null;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,12 @@ use BeyondCode\LaravelWebSockets\Server\Logger\ConnectionLogger;
|
||||||
use BeyondCode\LaravelWebSockets\Server\Logger\HttpLogger;
|
use BeyondCode\LaravelWebSockets\Server\Logger\HttpLogger;
|
||||||
use BeyondCode\LaravelWebSockets\Server\Logger\WebsocketsLogger;
|
use BeyondCode\LaravelWebSockets\Server\Logger\WebsocketsLogger;
|
||||||
use BeyondCode\LaravelWebSockets\Server\WebSocketServerFactory;
|
use BeyondCode\LaravelWebSockets\Server\WebSocketServerFactory;
|
||||||
use BeyondCode\LaravelWebSockets\Statistics\DnsResolver;
|
use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver;
|
||||||
use BeyondCode\LaravelWebSockets\Statistics\Logger\StatisticsLogger as StatisticsLoggerInterface;
|
use BeyondCode\LaravelWebSockets\Statistics\Logger\StatisticsLogger as StatisticsLoggerInterface;
|
||||||
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
|
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
|
||||||
use Clue\React\Buzz\Browser;
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use React\Dns\Config\Config as DnsConfig;
|
|
||||||
use React\Dns\Resolver\Factory as DnsFactory;
|
|
||||||
use React\Dns\Resolver\ResolverInterface;
|
|
||||||
use React\EventLoop\Factory as LoopFactory;
|
use React\EventLoop\Factory as LoopFactory;
|
||||||
use React\Socket\Connector;
|
|
||||||
|
|
||||||
class StartWebSocketServer extends Command
|
class StartWebSocketServer extends Command
|
||||||
{
|
{
|
||||||
|
|
@ -103,19 +98,12 @@ class StartWebSocketServer extends Command
|
||||||
*/
|
*/
|
||||||
protected function configureStatisticsLogger()
|
protected function configureStatisticsLogger()
|
||||||
{
|
{
|
||||||
$connector = new Connector($this->loop, [
|
$this->laravel->singleton(StatisticsLoggerInterface::class, function () {
|
||||||
'dns' => $this->getDnsResolver(),
|
|
||||||
'tls' => config('websockets.statistics.tls'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$browser = new Browser($this->loop, $connector);
|
|
||||||
|
|
||||||
$this->laravel->singleton(StatisticsLoggerInterface::class, function () use ($browser) {
|
|
||||||
$class = config('websockets.statistics.logger', \BeyondCode\LaravelWebSockets\Statistics\Logger\MemoryStatisticsLogger::class);
|
$class = config('websockets.statistics.logger', \BeyondCode\LaravelWebSockets\Statistics\Logger\MemoryStatisticsLogger::class);
|
||||||
|
|
||||||
return new $class(
|
return new $class(
|
||||||
$this->laravel->make(ChannelManager::class),
|
$this->laravel->make(ChannelManager::class),
|
||||||
$browser
|
$this->laravel->make(StatisticsDriver::class)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -273,27 +261,6 @@ class StartWebSocketServer extends Command
|
||||||
->createServer();
|
->createServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a DNS resolver for the stats manager.
|
|
||||||
*
|
|
||||||
* @return \React\Dns\Resolver\ResolverInterface
|
|
||||||
*/
|
|
||||||
protected function getDnsResolver(): ResolverInterface
|
|
||||||
{
|
|
||||||
if (! config('websockets.statistics.perform_dns_lookup')) {
|
|
||||||
return new DnsResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
$dnsConfig = DnsConfig::loadSystemConfigBlocking();
|
|
||||||
|
|
||||||
return (new DnsFactory)->createCached(
|
|
||||||
$dnsConfig->nameservers
|
|
||||||
? reset($dnsConfig->nameservers)
|
|
||||||
: '1.1.1.1',
|
|
||||||
$this->loop
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the last time the server restarted.
|
* Get the last time the server restarted.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -2,45 +2,21 @@
|
||||||
|
|
||||||
namespace BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers;
|
namespace BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers;
|
||||||
|
|
||||||
|
use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class DashboardApiController
|
class DashboardApiController
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Get statistics for an app ID.
|
* Get statistics for an app ID.
|
||||||
*
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @param \BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver $driver
|
||||||
* @param mixed $appId
|
* @param mixed $appId
|
||||||
* @return \Illuminate\Http\Response
|
* @return \Illuminate\Http\Response
|
||||||
*/
|
*/
|
||||||
public function getStatistics($appId)
|
public function getStatistics(Request $request, StatisticsDriver $driver, $appId)
|
||||||
{
|
{
|
||||||
$model = config('websockets.statistics.model');
|
return $driver::get($appId, $request);
|
||||||
|
|
||||||
$statistics = $model::where('app_id', $appId)
|
|
||||||
->latest()
|
|
||||||
->limit(120)
|
|
||||||
->get();
|
|
||||||
|
|
||||||
$statisticData = $statistics->map(function ($statistic) {
|
|
||||||
return [
|
|
||||||
'timestamp' => (string) $statistic->created_at,
|
|
||||||
'peak_connection_count' => $statistic->peak_connection_count,
|
|
||||||
'websocket_message_count' => $statistic->websocket_message_count,
|
|
||||||
'api_message_count' => $statistic->api_message_count,
|
|
||||||
];
|
|
||||||
})->reverse();
|
|
||||||
|
|
||||||
return [
|
|
||||||
'peak_connections' => [
|
|
||||||
'x' => $statisticData->pluck('timestamp'),
|
|
||||||
'y' => $statisticData->pluck('peak_connection_count'),
|
|
||||||
],
|
|
||||||
'websocket_message_count' => [
|
|
||||||
'x' => $statisticData->pluck('timestamp'),
|
|
||||||
'y' => $statisticData->pluck('websocket_message_count'),
|
|
||||||
],
|
|
||||||
'api_message_count' => [
|
|
||||||
'x' => $statisticData->pluck('timestamp'),
|
|
||||||
'y' => $statisticData->pluck('api_message_count'),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ class ShowDashboard
|
||||||
'port' => config('websockets.dashboard.port', 6001),
|
'port' => config('websockets.dashboard.port', 6001),
|
||||||
'channels' => DashboardLogger::$channels,
|
'channels' => DashboardLogger::$channels,
|
||||||
'logPrefix' => DashboardLogger::LOG_CHANNEL_PREFIX,
|
'logPrefix' => DashboardLogger::LOG_CHANNEL_PREFIX,
|
||||||
|
'refreshInterval' => config('websockets.statistics.interval_in_seconds'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
namespace BeyondCode\LaravelWebSockets\Statistics\Drivers;
|
namespace BeyondCode\LaravelWebSockets\Statistics\Drivers;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class DatabaseDriver implements StatisticsDriver
|
class DatabaseDriver implements StatisticsDriver
|
||||||
{
|
{
|
||||||
|
|
@ -87,6 +88,46 @@ class DatabaseDriver implements StatisticsDriver
|
||||||
return new static($class::create($data));
|
return new static($class::create($data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the records to show to the dashboard.
|
||||||
|
*
|
||||||
|
* @param mixed $appId
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function get($appId, Request $request): array
|
||||||
|
{
|
||||||
|
$class = config('websockets.statistics.database.model');
|
||||||
|
|
||||||
|
$statistics = $class::whereAppId($appId)
|
||||||
|
->latest()
|
||||||
|
->limit(120)
|
||||||
|
->get()
|
||||||
|
->map(function ($statistic) {
|
||||||
|
return [
|
||||||
|
'timestamp' => (string) $statistic->created_at,
|
||||||
|
'peak_connection_count' => $statistic->peak_connection_count,
|
||||||
|
'websocket_message_count' => $statistic->websocket_message_count,
|
||||||
|
'api_message_count' => $statistic->api_message_count,
|
||||||
|
];
|
||||||
|
})->reverse();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'peak_connections' => [
|
||||||
|
'x' => $statistics->pluck('timestamp'),
|
||||||
|
'y' => $statistics->pluck('peak_connection_count'),
|
||||||
|
],
|
||||||
|
'websocket_message_count' => [
|
||||||
|
'x' => $statistics->pluck('timestamp'),
|
||||||
|
'y' => $statistics->pluck('websocket_message_count'),
|
||||||
|
],
|
||||||
|
'api_message_count' => [
|
||||||
|
'x' => $statistics->pluck('timestamp'),
|
||||||
|
'y' => $statistics->pluck('api_message_count'),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete statistics from the store,
|
* Delete statistics from the store,
|
||||||
* optionally by app id, returning
|
* optionally by app id, returning
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace BeyondCode\LaravelWebSockets\Statistics\Drivers;
|
namespace BeyondCode\LaravelWebSockets\Statistics\Drivers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
interface StatisticsDriver
|
interface StatisticsDriver
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
@ -55,6 +57,15 @@ interface StatisticsDriver
|
||||||
*/
|
*/
|
||||||
public static function create(array $data): StatisticsDriver;
|
public static function create(array $data): StatisticsDriver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the records to show to the dashboard.
|
||||||
|
*
|
||||||
|
* @param mixed $appId
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function get($appId, Request $request);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete statistics from the store,
|
* Delete statistics from the store,
|
||||||
* optionally by app id, returning
|
* optionally by app id, returning
|
||||||
|
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace BeyondCode\LaravelWebSockets\Statistics\Events;
|
|
||||||
|
|
||||||
use BeyondCode\LaravelWebSockets\Dashboard\DashboardLogger;
|
|
||||||
use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver;
|
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
use Illuminate\Support\Str;
|
|
||||||
|
|
||||||
class StatisticsUpdated implements ShouldBroadcast
|
|
||||||
{
|
|
||||||
use SerializesModels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The statistics driver instance.
|
|
||||||
*
|
|
||||||
* @var \BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver
|
|
||||||
*/
|
|
||||||
protected $driver;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the event.
|
|
||||||
*
|
|
||||||
* @param \BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver $driver
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(StatisticsDriver $driver)
|
|
||||||
{
|
|
||||||
$this->driver = $driver;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format the broadcasting message.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function broadcastWith()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'time' => $this->driver->getTime(),
|
|
||||||
'app_id' => $this->driver->getAppId(),
|
|
||||||
'peak_connection_count' => $this->driver->getPeakConnectionCount(),
|
|
||||||
'websocket_message_count' => $this->driver->getWebsocketMessageCount(),
|
|
||||||
'api_message_count' => $this->driver->getApiMessageCount(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the channel to broadcast on.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Broadcasting\Channel
|
|
||||||
*/
|
|
||||||
public function broadcastOn()
|
|
||||||
{
|
|
||||||
$channelName = Str::after(DashboardLogger::LOG_CHANNEL_PREFIX.'statistics', 'private-');
|
|
||||||
|
|
||||||
return new PrivateChannel(
|
|
||||||
Str::after(DashboardLogger::LOG_CHANNEL_PREFIX.'statistics', 'private-')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the broadcasted event name.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function broadcastAs()
|
|
||||||
{
|
|
||||||
return 'statistics-updated';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace BeyondCode\LaravelWebSockets\Statistics\Http\Controllers;
|
|
||||||
|
|
||||||
use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver;
|
|
||||||
use BeyondCode\LaravelWebSockets\Statistics\Events\StatisticsUpdated;
|
|
||||||
use BeyondCode\LaravelWebSockets\Statistics\Rules\AppId;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class WebSocketStatisticsEntriesController
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Store the entry.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @param \BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver $driver
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function store(Request $request, StatisticsDriver $driver)
|
|
||||||
{
|
|
||||||
$validatedAttributes = $request->validate([
|
|
||||||
'app_id' => ['required', new AppId()],
|
|
||||||
'peak_connection_count' => 'required|integer',
|
|
||||||
'websocket_message_count' => 'required|integer',
|
|
||||||
'api_message_count' => 'required|integer',
|
|
||||||
]);
|
|
||||||
|
|
||||||
broadcast(new StatisticsUpdated(
|
|
||||||
$driver::create($validatedAttributes)
|
|
||||||
));
|
|
||||||
|
|
||||||
return 'ok';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace BeyondCode\LaravelWebSockets\Statistics\Http\Middleware;
|
|
||||||
|
|
||||||
use BeyondCode\LaravelWebSockets\Apps\App;
|
|
||||||
|
|
||||||
class Authorize
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Authorize the request by app secret.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @param \Closure $next
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function handle($request, $next)
|
|
||||||
{
|
|
||||||
return is_null(App::findBySecret($request->secret))
|
|
||||||
? abort(403)
|
|
||||||
: $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,11 +3,9 @@
|
||||||
namespace BeyondCode\LaravelWebSockets\Statistics\Logger;
|
namespace BeyondCode\LaravelWebSockets\Statistics\Logger;
|
||||||
|
|
||||||
use BeyondCode\LaravelWebSockets\Apps\App;
|
use BeyondCode\LaravelWebSockets\Apps\App;
|
||||||
use BeyondCode\LaravelWebSockets\Statistics\Http\Controllers\WebSocketStatisticsEntriesController;
|
use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver;
|
||||||
use BeyondCode\LaravelWebSockets\Statistics\Statistic;
|
use BeyondCode\LaravelWebSockets\Statistics\Statistic;
|
||||||
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
|
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
|
||||||
use Clue\React\Buzz\Browser;
|
|
||||||
use function GuzzleHttp\Psr7\stream_for;
|
|
||||||
use Ratchet\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
class MemoryStatisticsLogger implements StatisticsLogger
|
class MemoryStatisticsLogger implements StatisticsLogger
|
||||||
|
|
@ -27,23 +25,23 @@ class MemoryStatisticsLogger implements StatisticsLogger
|
||||||
protected $channelManager;
|
protected $channelManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Browser instance.
|
* The statistics driver instance.
|
||||||
*
|
*
|
||||||
* @var \Clue\React\Buzz\Browser
|
* @var \BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver
|
||||||
*/
|
*/
|
||||||
protected $browser;
|
protected $driver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the logger.
|
* Initialize the logger.
|
||||||
*
|
*
|
||||||
* @param \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager $channelManager
|
* @param \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager $channelManager
|
||||||
* @param \Clue\React\Buzz\Browser $browser
|
* @param \BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver $driver
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(ChannelManager $channelManager, Browser $browser)
|
public function __construct(ChannelManager $channelManager, StatisticsDriver $driver)
|
||||||
{
|
{
|
||||||
$this->channelManager = $channelManager;
|
$this->channelManager = $channelManager;
|
||||||
$this->browser = $browser;
|
$this->driver = $driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -106,16 +104,7 @@ class MemoryStatisticsLogger implements StatisticsLogger
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$postData = array_merge($statistic->toArray(), [
|
$this->driver::create($statistic->toArray());
|
||||||
'secret' => App::findById($appId)->secret,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->browser
|
|
||||||
->post(
|
|
||||||
action([WebSocketStatisticsEntriesController::class, 'store']),
|
|
||||||
['Content-Type' => 'application/json'],
|
|
||||||
stream_for(json_encode($postData))
|
|
||||||
);
|
|
||||||
|
|
||||||
$currentConnectionCount = $this->channelManager->getConnectionCount($appId);
|
$currentConnectionCount = $this->channelManager->getConnectionCount($appId);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace BeyondCode\LaravelWebSockets\Statistics\Logger;
|
namespace BeyondCode\LaravelWebSockets\Statistics\Logger;
|
||||||
|
|
||||||
|
use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver;
|
||||||
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
|
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
|
||||||
use Clue\React\Buzz\Browser;
|
|
||||||
use Ratchet\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
|
|
||||||
class NullStatisticsLogger implements StatisticsLogger
|
class NullStatisticsLogger implements StatisticsLogger
|
||||||
|
|
@ -16,23 +16,23 @@ class NullStatisticsLogger implements StatisticsLogger
|
||||||
protected $channelManager;
|
protected $channelManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Browser instance.
|
* The statistics driver instance.
|
||||||
*
|
*
|
||||||
* @var \Clue\React\Buzz\Browser
|
* @var \BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver
|
||||||
*/
|
*/
|
||||||
protected $browser;
|
protected $driver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the logger.
|
* Initialize the logger.
|
||||||
*
|
*
|
||||||
* @param \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager $channelManager
|
* @param \BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager $channelManager
|
||||||
* @param \Clue\React\Buzz\Browser $browser
|
* @param \BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver $driver
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct(ChannelManager $channelManager, Browser $browser)
|
public function __construct(ChannelManager $channelManager, StatisticsDriver $driver)
|
||||||
{
|
{
|
||||||
$this->channelManager = $channelManager;
|
$this->channelManager = $channelManager;
|
||||||
$this->browser = $browser;
|
$this->driver = $driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,6 @@ use BeyondCode\LaravelWebSockets\Dashboard\Http\Middleware\Authorize as Authoriz
|
||||||
use BeyondCode\LaravelWebSockets\PubSub\Broadcasters\RedisPusherBroadcaster;
|
use BeyondCode\LaravelWebSockets\PubSub\Broadcasters\RedisPusherBroadcaster;
|
||||||
use BeyondCode\LaravelWebSockets\Server\Router;
|
use BeyondCode\LaravelWebSockets\Server\Router;
|
||||||
use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver;
|
use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver;
|
||||||
use BeyondCode\LaravelWebSockets\Statistics\Http\Controllers\WebSocketStatisticsEntriesController;
|
|
||||||
use BeyondCode\LaravelWebSockets\Statistics\Http\Middleware\Authorize as AuthorizeStatistics;
|
|
||||||
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
|
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
|
||||||
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager;
|
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManagers\ArrayChannelManager;
|
||||||
use Illuminate\Broadcasting\BroadcastManager;
|
use Illuminate\Broadcasting\BroadcastManager;
|
||||||
|
|
@ -127,10 +125,6 @@ class WebSocketsServiceProvider extends ServiceProvider
|
||||||
Route::post('auth', AuthenticateDashboard::class);
|
Route::post('auth', AuthenticateDashboard::class);
|
||||||
Route::post('event', SendMessage::class);
|
Route::post('event', SendMessage::class);
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::middleware(AuthorizeStatistics::class)->group(function () {
|
|
||||||
Route::post('statistics', [WebSocketStatisticsEntriesController::class, 'store']);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace BeyondCode\LaravelWebSockets\Tests\Statistics\Controllers;
|
|
||||||
|
|
||||||
use BeyondCode\LaravelWebSockets\Statistics\Http\Controllers\WebSocketStatisticsEntriesController;
|
|
||||||
use BeyondCode\LaravelWebSockets\Statistics\Models\WebSocketsStatisticsEntry;
|
|
||||||
use BeyondCode\LaravelWebSockets\Tests\TestCase;
|
|
||||||
|
|
||||||
class WebSocketsStatisticsControllerTest extends TestCase
|
|
||||||
{
|
|
||||||
/** @test */
|
|
||||||
public function it_can_store_statistics()
|
|
||||||
{
|
|
||||||
$this->post(
|
|
||||||
action([WebSocketStatisticsEntriesController::class, 'store']),
|
|
||||||
array_merge($this->payload(), [
|
|
||||||
'secret' => config('websockets.apps.0.secret'),
|
|
||||||
])
|
|
||||||
);
|
|
||||||
|
|
||||||
$entries = WebSocketsStatisticsEntry::get();
|
|
||||||
|
|
||||||
$this->assertCount(1, $entries);
|
|
||||||
|
|
||||||
$actual = $entries->first()->attributesToArray();
|
|
||||||
|
|
||||||
foreach ($this->payload() as $key => $value) {
|
|
||||||
$this->assertArrayHasKey($key, $actual);
|
|
||||||
$this->assertSame($value, $actual[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function payload(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'app_id' => config('websockets.apps.0.id'),
|
|
||||||
'peak_connection_count' => '1',
|
|
||||||
'websocket_message_count' => '2',
|
|
||||||
'api_message_count' => '3',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -6,13 +6,12 @@ use BeyondCode\LaravelWebSockets\Facades\StatisticsLogger;
|
||||||
use BeyondCode\LaravelWebSockets\PubSub\Drivers\LocalClient;
|
use BeyondCode\LaravelWebSockets\PubSub\Drivers\LocalClient;
|
||||||
use BeyondCode\LaravelWebSockets\PubSub\Drivers\RedisClient;
|
use BeyondCode\LaravelWebSockets\PubSub\Drivers\RedisClient;
|
||||||
use BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface;
|
use BeyondCode\LaravelWebSockets\PubSub\ReplicationInterface;
|
||||||
|
use BeyondCode\LaravelWebSockets\Statistics\Drivers\StatisticsDriver;
|
||||||
use BeyondCode\LaravelWebSockets\Tests\Mocks\Connection;
|
use BeyondCode\LaravelWebSockets\Tests\Mocks\Connection;
|
||||||
use BeyondCode\LaravelWebSockets\Tests\Mocks\Message;
|
use BeyondCode\LaravelWebSockets\Tests\Mocks\Message;
|
||||||
use BeyondCode\LaravelWebSockets\Tests\Statistics\Logger\FakeStatisticsLogger;
|
use BeyondCode\LaravelWebSockets\Tests\Statistics\Logger\FakeStatisticsLogger;
|
||||||
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
|
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
|
||||||
use Clue\React\Buzz\Browser;
|
|
||||||
use GuzzleHttp\Psr7\Request;
|
use GuzzleHttp\Psr7\Request;
|
||||||
use Mockery;
|
|
||||||
use Ratchet\ConnectionInterface;
|
use Ratchet\ConnectionInterface;
|
||||||
use React\EventLoop\Factory as LoopFactory;
|
use React\EventLoop\Factory as LoopFactory;
|
||||||
|
|
||||||
|
|
@ -45,7 +44,7 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
||||||
|
|
||||||
StatisticsLogger::swap(new FakeStatisticsLogger(
|
StatisticsLogger::swap(new FakeStatisticsLogger(
|
||||||
$this->channelManager,
|
$this->channelManager,
|
||||||
Mockery::mock(Browser::class)
|
app(StatisticsDriver::class)
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
|
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
|
||||||
|
|
@ -94,8 +93,6 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$app['config']->set('websockets.statistics.perform_dns_lookup', true);
|
|
||||||
|
|
||||||
$app['config']->set('database.redis.default', [
|
$app['config']->set('database.redis.default', [
|
||||||
'host' => env('REDIS_HOST', '127.0.0.1'),
|
'host' => env('REDIS_HOST', '127.0.0.1'),
|
||||||
'password' => env('REDIS_PASSWORD', null),
|
'password' => env('REDIS_PASSWORD', null),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue