Skip to content
This repository was archived by the owner on Feb 7, 2024. It is now read-only.

Commit a7c505e

Browse files
authored
[2.x] Dispatch events on actions (#556)
* Dispatching events
1 parent 9cb83dc commit a7c505e

13 files changed

+443
-27
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
title: Dispatched Events
3+
order: 5
4+
---
5+
6+
# Dispatched Events
7+
8+
Laravel WebSockets takes advantage of Laravel's Event dispatching observer, in a way that you can handle in-server events outside of it.
9+
10+
For example, you can listen for events like when a new connection establishes or when an user joins a presence channel.
11+
12+
## Events
13+
14+
Below you will find a list of dispatched events:
15+
16+
- `BeyondCode\LaravelWebSockets\Events\NewConnection` - when a connection successfully establishes on the server
17+
- `BeyondCode\LaravelWebSockets\Events\ConnectionClosed` - when a connection leaves the server
18+
- `BeyondCode\LaravelWebSockets\Events\SubscribedToChannel` - when a connection subscribes to a specific channel
19+
- `BeyondCode\LaravelWebSockets\Events\UnsubscribedFromChannel` - when a connection unsubscribes from a specific channel
20+
- `BeyondCode\LaravelWebSockets\Events\WebSocketMessageReceived` - when the server receives a message
21+
- `BeyondCode\LaravelWebSockets\EventsConnectionPonged` - when a connection pings to the server that it is still alive
22+
23+
## Queued Listeners
24+
25+
Because the default Redis connection (either PhpRedis or Predis) is a blocking I/O method and can cause problems with the server speed and availability, you might want to check the [Non-Blocking Queue Driver](non-blocking-queue-driver.md) documentation that helps you create the Async Redis queue driver that is going to fix the Blocking I/O issue.
26+
27+
If set up, you can use the `async-redis` queue driver in your listeners:
28+
29+
```php
30+
<?php
31+
32+
namespace App\Listeners;
33+
34+
use BeyondCode\LaravelWebSockets\Events\NewConnection;
35+
use Illuminate\Contracts\Queue\ShouldQueue;
36+
37+
class HandleNewConnections implements ShouldQueue
38+
{
39+
/**
40+
* The name of the connection the job should be sent to.
41+
*
42+
* @var string|null
43+
*/
44+
public $connection = 'async-redis';
45+
46+
/**
47+
* Create the event listener.
48+
*
49+
* @return void
50+
*/
51+
public function __construct()
52+
{
53+
//
54+
}
55+
56+
/**
57+
* Handle the event.
58+
*
59+
* @param NewConnection $event
60+
* @return void
61+
*/
62+
public function handle(NewConnection $event)
63+
{
64+
//
65+
}
66+
}
67+
```
68+
69+
The `EventServiceProvider` might look like this, registering the listeners that are going to be used by the event dispatching:
70+
71+
```php
72+
/**
73+
* The event listener mappings for the application.
74+
*
75+
* @var array
76+
*/
77+
protected $listen = [
78+
\BeyondCode\LaravelWebSockets\Events\NewConnection::class => [
79+
App\Listeners\HandleNewConnections::class,
80+
],
81+
];
82+
```
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
title: Non-Blocking Queue Driver
3+
order: 4
4+
---
5+
6+
# Non-Blocking Queue Driver
7+
8+
In Laravel, he default Redis connection also interacts with the queues. Since you might want to dispatch jobs on Redis from the server, you can encounter an anti-pattern of using a blocking I/O connection (like PhpRedis or PRedis) within the WebSockets server.
9+
10+
To solve this issue, you can configure the built-in queue driver that uses the Async Redis connection when it's possible, like within the WebSockets server. It's highly recommended to switch your queue to it if you are going to use the queues within the server controllers, for example.
11+
12+
Add the `async-redis` queue driver to your list of connections. The configuration parameters are compatible with the default `redis` driver:
13+
14+
```php
15+
'connections' => [
16+
'async-redis' => [
17+
'driver' => 'async-redis',
18+
'connection' => env('WEBSOCKETS_REDIS_REPLICATION_CONNECTION', 'default'),
19+
'queue' => env('REDIS_QUEUE', 'default'),
20+
'retry_after' => 90,
21+
'block_for' => null,
22+
],
23+
]
24+
```
25+
26+
Also, make sure that the default queue driver is set to `async-redis`:
27+
28+
```
29+
QUEUE_CONNECTION=async-redis
30+
```

docs/horizontal-scaling/redis.md

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -40,29 +40,3 @@ You can set the connection name to the Redis database under `redis`:
4040
```
4141

4242
The connections can be found in your `config/database.php` file, under the `redis` key.
43-
44-
## Async Redis Queue
45-
46-
The default Redis connection also interacts with the queues. Since you might want to dispatch jobs on Redis from the server, you can encounter an anti-pattern of using a blocking I/O connection (like PhpRedis or PRedis) within the WebSockets server.
47-
48-
To solve this issue, you can configure the built-in queue driver that uses the Async Redis connection when it's possible, like within the WebSockets server. It's highly recommended to switch your queue to it if you are going to use the queues within the server controllers, for example.
49-
50-
Add the `async-redis` queue driver to your list of connections. The configuration parameters are compatible with the default `redis` driver:
51-
52-
```php
53-
'connections' => [
54-
'async-redis' => [
55-
'driver' => 'async-redis',
56-
'connection' => env('WEBSOCKETS_REDIS_REPLICATION_CONNECTION', 'default'),
57-
'queue' => env('REDIS_QUEUE', 'default'),
58-
'retry_after' => 90,
59-
'block_for' => null,
60-
],
61-
]
62-
```
63-
64-
Also, make sure that the default queue driver is set to `async-redis`:
65-
66-
```
67-
QUEUE_CONNECTION=async-redis
68-
```

src/Channels/Channel.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use BeyondCode\LaravelWebSockets\Contracts\ChannelManager;
66
use BeyondCode\LaravelWebSockets\DashboardLogger;
7+
use BeyondCode\LaravelWebSockets\Events\SubscribedToChannel;
8+
use BeyondCode\LaravelWebSockets\Events\UnsubscribedFromChannel;
79
use BeyondCode\LaravelWebSockets\Server\Exceptions\InvalidSignature;
810
use Illuminate\Support\Str;
911
use Ratchet\ConnectionInterface;
@@ -89,6 +91,12 @@ public function subscribe(ConnectionInterface $connection, stdClass $payload): b
8991
'channel' => $this->getName(),
9092
]);
9193

94+
SubscribedToChannel::dispatch(
95+
$connection->app->id,
96+
$connection->socketId,
97+
$this->getName(),
98+
);
99+
92100
return true;
93101
}
94102

@@ -106,6 +114,12 @@ public function unsubscribe(ConnectionInterface $connection): bool
106114

107115
unset($this->connections[$connection->socketId]);
108116

117+
UnsubscribedFromChannel::dispatch(
118+
$connection->app->id,
119+
$connection->socketId,
120+
$this->getName()
121+
);
122+
109123
return true;
110124
}
111125

src/Channels/PresenceChannel.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace BeyondCode\LaravelWebSockets\Channels;
44

55
use BeyondCode\LaravelWebSockets\DashboardLogger;
6+
use BeyondCode\LaravelWebSockets\Events\SubscribedToChannel;
7+
use BeyondCode\LaravelWebSockets\Events\UnsubscribedFromChannel;
68
use BeyondCode\LaravelWebSockets\Server\Exceptions\InvalidSignature;
79
use Ratchet\ConnectionInterface;
810
use stdClass;
@@ -60,7 +62,7 @@ public function subscribe(ConnectionInterface $connection, stdClass $payload): b
6062
// and in this case the events will only be triggered when the first tab is opened.
6163
$this->channelManager
6264
->getMemberSockets($user->user_id, $connection->app->id, $this->getName())
63-
->then(function ($sockets) use ($payload, $connection) {
65+
->then(function ($sockets) use ($payload, $connection, $user) {
6466
if (count($sockets) === 1) {
6567
$memberAddedPayload = [
6668
'event' => 'pusher_internal:member_added',
@@ -72,6 +74,13 @@ public function subscribe(ConnectionInterface $connection, stdClass $payload): b
7274
(object) $memberAddedPayload, $connection->socketId,
7375
$connection->app->id
7476
);
77+
78+
SubscribedToChannel::dispatch(
79+
$connection->app->id,
80+
$connection->socketId,
81+
$this->getName(),
82+
$user
83+
);
7584
}
7685

7786
DashboardLogger::log($connection->app->id, DashboardLogger::TYPE_SUBSCRIBED, [
@@ -128,6 +137,13 @@ public function unsubscribe(ConnectionInterface $connection): bool
128137
(object) $memberRemovedPayload, $connection->socketId,
129138
$connection->app->id
130139
);
140+
141+
UnsubscribedFromChannel::dispatch(
142+
$connection->app->id,
143+
$connection->socketId,
144+
$this->getName(),
145+
$user
146+
);
131147
}
132148
});
133149
});

src/Events/ConnectionClosed.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace BeyondCode\LaravelWebSockets\Events;
4+
5+
use Illuminate\Foundation\Events\Dispatchable;
6+
use Illuminate\Queue\SerializesModels;
7+
8+
class ConnectionClosed
9+
{
10+
use Dispatchable, SerializesModels;
11+
12+
/**
13+
* The WebSockets app id that the user connected to.
14+
*
15+
* @var string
16+
*/
17+
public $appId;
18+
19+
/**
20+
* The Socket ID associated with the connection.
21+
*
22+
* @var string
23+
*/
24+
public $socketId;
25+
26+
/**
27+
* Create a new event instance.
28+
*
29+
* @param string $appId
30+
* @param string $socketId
31+
* @return void
32+
*/
33+
public function __construct(string $appId, string $socketId)
34+
{
35+
$this->appId = $appId;
36+
$this->socketId = $socketId;
37+
}
38+
}

src/Events/ConnectionPonged.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace BeyondCode\LaravelWebSockets\Events;
4+
5+
use Illuminate\Foundation\Events\Dispatchable;
6+
use Illuminate\Queue\SerializesModels;
7+
8+
class ConnectionPonged
9+
{
10+
use Dispatchable, SerializesModels;
11+
12+
/**
13+
* The WebSockets app id that the user connected to.
14+
*
15+
* @var string
16+
*/
17+
public $appId;
18+
19+
/**
20+
* The Socket ID associated with the connection.
21+
*
22+
* @var string
23+
*/
24+
public $socketId;
25+
26+
/**
27+
* Create a new event instance.
28+
*
29+
* @param string $appId
30+
* @param string $socketId
31+
* @return void
32+
*/
33+
public function __construct(string $appId, string $socketId)
34+
{
35+
$this->appId = $appId;
36+
$this->socketId = $socketId;
37+
}
38+
}

src/Events/NewConnection.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace BeyondCode\LaravelWebSockets\Events;
4+
5+
use Illuminate\Foundation\Events\Dispatchable;
6+
use Illuminate\Queue\SerializesModels;
7+
8+
class NewConnection
9+
{
10+
use Dispatchable, SerializesModels;
11+
12+
/**
13+
* The WebSockets app id that the user connected to.
14+
*
15+
* @var string
16+
*/
17+
public $appId;
18+
19+
/**
20+
* The Socket ID associated with the connection.
21+
*
22+
* @var string
23+
*/
24+
public $socketId;
25+
26+
/**
27+
* Create a new event instance.
28+
*
29+
* @param string $appId
30+
* @param string $socketId
31+
* @return void
32+
*/
33+
public function __construct(string $appId, string $socketId)
34+
{
35+
$this->appId = $appId;
36+
$this->socketId = $socketId;
37+
}
38+
}

src/Events/SubscribedToChannel.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace BeyondCode\LaravelWebSockets\Events;
4+
5+
use Illuminate\Foundation\Events\Dispatchable;
6+
use Illuminate\Queue\SerializesModels;
7+
use stdClass;
8+
9+
class SubscribedToChannel
10+
{
11+
use Dispatchable, SerializesModels;
12+
13+
/**
14+
* The WebSockets app id that the user connected to.
15+
*
16+
* @var string
17+
*/
18+
public $appId;
19+
20+
/**
21+
* The Socket ID associated with the connection.
22+
*
23+
* @var string
24+
*/
25+
public $socketId;
26+
27+
/**
28+
* The channel name.
29+
*
30+
* @var string
31+
*/
32+
public $channelName;
33+
34+
/**
35+
* The user received on presence channel.
36+
*
37+
* @var string
38+
*/
39+
public $user;
40+
41+
/**
42+
* Create a new event instance.
43+
*
44+
* @param string $appId
45+
* @param string $socketId
46+
* @param string $channelName
47+
* @param stdClass|null $user
48+
* @return void
49+
*/
50+
public function __construct(string $appId, string $socketId, string $channelName, ?stdClass $user = null)
51+
{
52+
$this->appId = $appId;
53+
$this->socketId = $socketId;
54+
$this->channelName = $channelName;
55+
$this->user = $user;
56+
}
57+
}

0 commit comments

Comments
 (0)