xiaohuilam / laravel

Laravel 深入详解 —— 源代码解析,新手进阶指南
433 stars 80 forks source link

17. 通知/广播 (Broadcast) 机制 #18

Open xiaohuilam opened 5 years ago

xiaohuilam commented 5 years ago

鉴权

config/app.php 中我们能看到 Illuminate\Broadcasting\BroadcastServiceProvider 这个服务提供者被注册: https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/config/app.php#L128 在前面对服务提供者的讲解中,我们得出了一个服务提供者肯定会包含 bootregister 方法之一,这 BroadcastServiceProvider 就是一个包含而且只包含了 boot 方法的服务提供者: https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/app/Providers/BroadcastServiceProvider.php#L10-L20

第17行调用到了 Illuminate\Support\Facades\Broadcast::routes() 假面方法。 https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/app/Providers/BroadcastServiceProvider.php#L17

顾名思义,就是注册路由的。而这个方法,最终是穿透到了 Illuminate\Broadcasting\BroadcastManager::routes() https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastManager.php#L53-L73

第19行是为了在 config/app.php 卸载 BroadcastServiceProvider 时,直观的让 routes/channels.php 失效的做法,所以就不放在别处 https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/app/Providers/BroadcastServiceProvider.php#L19

第61~63行是如果缓存过路由,则跳出。

第62~第72行相当于与执行了

Route::group(['middleware' => ['web']], function () {
    Route::match(['get', 'post'], '/broadcasting/auth', '\\'.BroadcastController::class.'@authenticate');
});

方法调用到的 controller action 其实是 https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastController.php#L11-L20

这个假面方法 Broadcast::auth() 比较特殊,是根据 config('broadcasting.default') 分发到 Illuminate/Broadcasting/Broadcasters 具体的类中:

我们以 redis 的配置为例 (laravel-echo-server) 。 https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/RedisBroadcaster.php#L39-L61

第一步,如果 $request->channel_name 不以 private- 或者 presence- 开头,或者没有提供用户授权导致请求取不到用户,抛出 Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException 异常

第二步,将 $request->channel_name 中的 private- 或者 presence- 还原移除。

第三步,调用 Broadcaster:: verifyUserCanAccessChannel() https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/Broadcaster.php#L45-L71

routes/channels.php 中定义的 channel_name 匹配上的通道的鉴权回调执行,拿到返回值。如果 if ($result = $handler($request->user(), ...$parameters)) 返回成功的 RedisBroadcaster::validAuthenticationResponse(),否则抛出 Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException 异常

而成功的响应 RedisBroadcaster::validAuthenticationResponse() 逻辑为返回 user_iduser_info 给 websocket 客户端: https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/RedisBroadcaster.php#L63-L80

至此,鉴权部分完成。


推送

broadcast() 方法定义于 https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php#L216-L219

此方法最终走到了 https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastManager.php#L92-L101

PendingBroadcast 类比较特殊,有个 __destruct 解构方法: https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/PendingBroadcast.php#L50-L58

走到 https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php#L185-L229

调用了 https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php#L272-L281

还是回到了 BroadcastManager https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastManager.php#L103-L130 根据前文的 config('broadcasting.default') 配置,

第127~129行,是将广播事件 BroadcastEvent 入队。

根据我们对队列的了解,BroadcastEvent 应该有 handle() 方法 https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastEvent.php#L35-L50

第46~49行,是执行广播,调用了 RedisBroadcaster::broadcast() https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/RedisBroadcaster.php#L82-L103

消息进入 redis。

// TODO: laravel-echo-server 部分。