Open xiaohuilam opened 5 years ago
在 config/app.php 中我们能看到 Illuminate\Broadcasting\BroadcastServiceProvider 这个服务提供者被注册: https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/config/app.php#L128 在前面对服务提供者的讲解中,我们得出了一个服务提供者肯定会包含 boot 或 register 方法之一,这 BroadcastServiceProvider 就是一个包含而且只包含了 boot 方法的服务提供者: https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/app/Providers/BroadcastServiceProvider.php#L10-L20
config/app.php
Illuminate\Broadcasting\BroadcastServiceProvider
boot
register
BroadcastServiceProvider
第17行调用到了 Illuminate\Support\Facades\Broadcast::routes() 假面方法。 https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/app/Providers/BroadcastServiceProvider.php#L17
Illuminate\Support\Facades\Broadcast::routes()
顾名思义,就是注册路由的。而这个方法,最终是穿透到了 Illuminate\Broadcasting\BroadcastManager::routes() https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastManager.php#L53-L73
Illuminate\Broadcasting\BroadcastManager::routes()
第19行是为了在 config/app.php 卸载 BroadcastServiceProvider 时,直观的让 routes/channels.php 失效的做法,所以就不放在别处 https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/app/Providers/BroadcastServiceProvider.php#L19
routes/channels.php
第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
controller action
这个假面方法 Broadcast::auth() 比较特殊,是根据 config('broadcasting.default') 分发到 Illuminate/Broadcasting/Broadcasters 具体的类中:
Broadcast::auth()
config('broadcasting.default')
我们以 redis 的配置为例 (laravel-echo-server) 。 https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/RedisBroadcaster.php#L39-L61
redis
laravel-echo-server
第一步,如果 $request->channel_name 不以 private- 或者 presence- 开头,或者没有提供用户授权导致请求取不到用户,抛出 Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException 异常
$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
Broadcaster:: verifyUserCanAccessChannel()
将 routes/channels.php 中定义的 channel_name 匹配上的通道的鉴权回调执行,拿到返回值。如果 if ($result = $handler($request->user(), ...$parameters)) 返回成功的 RedisBroadcaster::validAuthenticationResponse(),否则抛出 Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException 异常
channel_name
if ($result = $handler($request->user(), ...$parameters))
RedisBroadcaster::validAuthenticationResponse()
而成功的响应 RedisBroadcaster::validAuthenticationResponse() 逻辑为返回 user_id 和 user_info 给 websocket 客户端: https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/RedisBroadcaster.php#L63-L80
user_id
user_info
至此,鉴权部分完成。
broadcast() 方法定义于 https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php#L216-L219
broadcast()
此方法最终走到了 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
PendingBroadcast
__destruct
走到 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') 配置,
BroadcastManager
第127~129行,是将广播事件 BroadcastEvent 入队。
BroadcastEvent
根据我们对队列的了解,BroadcastEvent 应该有 handle() 方法 https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastEvent.php#L35-L50
handle()
第46~49行,是执行广播,调用了 RedisBroadcaster::broadcast() https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/RedisBroadcaster.php#L82-L103
RedisBroadcaster::broadcast()
消息进入 redis。
// TODO: laravel-echo-server 部分。
鉴权
在
config/app.php
中我们能看到Illuminate\Broadcasting\BroadcastServiceProvider
这个服务提供者被注册: https://github.com/xiaohuilam/laravel/blob/72bccf58caa5c32de22821209a03f0c4bde9ff75/config/app.php#L128 在前面对服务提供者的讲解中,我们得出了一个服务提供者肯定会包含boot
或register
方法之一,这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行相当于与执行了
方法调用到的
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_id
和user_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 部分。