17. 通知/广播 (Broadcast) 机制
xiaohuilam opened this issue · comments
鉴权
在 config/app.php
中我们能看到 Illuminate\Broadcasting\BroadcastServiceProvider
这个服务提供者被注册:
Line 128 in 72bccf5
在前面对服务提供者的讲解中,我们得出了一个服务提供者肯定会包含
boot
或 register
方法之一,这 BroadcastServiceProvider
就是一个包含而且只包含了 boot
方法的服务提供者:laravel/app/Providers/BroadcastServiceProvider.php
Lines 10 to 20 in 72bccf5
第17行调用到了 Illuminate\Support\Facades\Broadcast::routes()
假面方法。
顾名思义,就是注册路由的。而这个方法,最终是穿透到了 Illuminate\Broadcasting\BroadcastManager::routes()
第19行是为了在 config/app.php
卸载 BroadcastServiceProvider
时,直观的让 routes/channels.php
失效的做法,所以就不放在别处
第61~63行是如果缓存过路由,则跳出。
第62~第72行相当于与执行了
Route::group(['middleware' => ['web']], function () {
Route::match(['get', 'post'], '/broadcasting/auth', '\\'.BroadcastController::class.'@authenticate');
});
方法调用到的 controller action
其实是
这个假面方法 Broadcast::auth()
比较特殊,是根据 config('broadcasting.default')
分发到 Illuminate/Broadcasting/Broadcasters 具体的类中:
- LogBroadcaster.php
- NullBroadcaster.php
- PusherBroadcaster.php
- RedisBroadcaster.php
我们以 redis
的配置为例 (laravel-echo-server
) 。
第一步,如果 $request->channel_name
不以 private-
或者 presence-
开头,或者没有提供用户授权导致请求取不到用户,抛出 Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
异常
第二步,将 $request->channel_name
中的 private-
或者 presence-
还原移除。
第三步,调用 Broadcaster:: verifyUserCanAccessChannel()
将 routes/channels.php
中定义的 channel_name
匹配上的通道的鉴权回调执行,拿到返回值。如果 if ($result = $handler($request->user(), ...$parameters))
返回成功的 RedisBroadcaster::validAuthenticationResponse()
,否则抛出 Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
异常
而成功的响应 RedisBroadcaster::validAuthenticationResponse()
逻辑为返回 user_id
和 user_info
给 websocket 客户端:
至此,鉴权部分完成。
推送
broadcast()
方法定义于
laravel/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php
Lines 216 to 219 in 72bccf5
此方法最终走到了
PendingBroadcast
类比较特殊,有个 __destruct
解构方法:
走到
laravel/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php
Lines 185 to 229 in 72bccf5
调用了
laravel/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php
Lines 272 to 281 in 72bccf5
还是回到了 BroadcastManager
根据前文的
config('broadcasting.default')
配置,
第127~129行,是将广播事件 BroadcastEvent
入队。
根据我们对队列的了解,BroadcastEvent
应该有 handle()
方法
第46~49行,是执行广播,调用了 RedisBroadcaster::broadcast()
消息进入 redis。
// TODO: laravel-echo-server 部分。