阅读文档
安装需要的用的依赖
//websocket服务端
composer require beyondcode/laravel-websockets
//发布数据迁移文件
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="migrations"
//发布配置文件
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="config"
//安装队列管理面板 或者 使用php artisan queue:work也可以
composer require laravel/horizon
php artisan horizon:install
//执行迁移
php artisan migrate
//前端依赖
npm install laravel-echo pusher-js
修改配置
.env 配置
//广播驱动设置为pusher
BROADCAST_DRIVER=pusher
//队列驱动改为redis
QUEUE_CONNECTION=redis
//使用laravel-websockets做后端,pusher配置随便填写
PUSHER_APP_ID=yangliuan
PUSHER_APP_KEY=yangliuan
PUSHER_APP_SECRET=yangliuan
//注意一定要注释这行,否则laravel-websockets不生效
#PUSHER_APP_CLUSTER=mt1
//websocket端口号
LARAVEL_WEBSOCKETS_PORT=6001
config/app.php 配置
取消如下Provider的注释
/*
* Application Service Providers...
*/
...
// App\Providers\BroadcastServiceProvider::class,
config/broadcasting.php 配置
按如下修改
'connections' => [
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'cluster' => env('PUSHER_APP_CLUSTER'),
//本地开发关闭安全连接
'useTLS' => false,
//本地host配置
'host' => '127.0.0.1',
//端口
'port' => env('LARAVEL_WEBSOCKETS_PORT', 6001),
//协议
'scheme' => 'http',
],
],
config/websockets.php
'apps' => [
[
'id' => env('PUSHER_APP_ID'),
'name' => env('APP_NAME'),
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'path' => env('PUSHER_APP_PATH'),
'capacity' => null,
//是否开启客户端发送消息
'enable_client_messages' => false,
//是否开启统计
'enable_statistics' => true,
],
],
测试案例场景一
使用laravel excel 队列导出文件后,自动提示并下载,使用公共频道 demo地址
后端代码
注册频道
route/channels.php 文件,可以自定义频道名称
Broadcast::channel('excel', function () {
return true;
});
创建ExcelExportCompletedEvent事件
php artisan make:event ExcelExportCompletedEvent
<?php
use Illuminate\Broadcasting\Channel;//公共频道
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel; //存在频道可以加入和离开,需要授权
use Illuminate\Broadcasting\PrivateChannel; //私有频道,需要授权
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
//注意事件一定要实现ShouldBroadcast接口
class ExcelExportCompletedEvent implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
//public属性会自动转换为广播数据
//自定义广播数据 文档 https://learnku.com/docs/laravel/9.x/broadcasting/12223#b2f5d1
public $excel_path;
public $disk;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(string $excel_path, string $disk)
{
//文件路径
$this->excel_path = $excel_path;
//磁盘
$this->disk = $disk;
}
/**
* 监听频道
* 监听自己的定义频道名称
*
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('excel');
}
}
创建导出文件 ExcelDemoPictureQueryExport 细节略过详情看 laravel excel文档
创建通知队列,用于触发时间
php artisan make:job ExcelNotifyJob
class ExcelNotifyJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $attach;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(array $attach)
{
$this->attach = $attach;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
//发送广播通知
ExcelExportCompletedEvent::dispatch($this->attach['file_name'], $this->attach['disk']);
}
}
控制器代码
/**
* 字段导出图片 使用队列 并接受广播通知
*
* @param Request $request
* @return void
*/
public function queueImages(Request $request)
{
$file_name = 'excel-demo-'.date('YmdHis').\mt_rand(100000, 999999).'.xlsx';
$disk = 'public';
Excel::queue(new ExcelDemoPictureQueryExport(), $file_name, $disk)
//导出成功后,使用任务链调用excel通知job
//DOC:https://learnku.com/docs/laravel/8.5/queues/10395#dispatching-jobs
//DOC:https://docs.laravel-excel.com/3.1/exports/queued.html#appending-jobs
->chain([
new ExcelNotifyJob(compact('file_name', 'disk'))
]);
return response()->json();
}
//下载文件方法
public function store(Request $request)
{
$request->validate([
'storage_path' => 'bail|required|string',
'disk' => 'bail|nullable|string',
]);
$realPath = Storage::disk($request->input('disk') ?? 'public')
->path($request->input('storage_path'));
return response()->download($realPath)->deleteFileAfterSend();
}
前端代码
使用 Laravel Jetstream Inertia.js 构建
封装laravel-echo.js
import Echo from 'laravel-echo';
window.pusher = require('pusher-js');
const echo = new Echo({
broadcaster: 'pusher',
key: 'yangliuan',
wsHost: window.location.hostname,
wsPort: 6001,
forceTLS: false,
enabledTransports: ['ws', 'wss'],
})
export { echo }
Excel.vue
<template>
<app-layout title="Dashboard">
...
<a href="#" @click="queueImagesClick">
<div class="mt-3 flex items-center text-sm font-semibold text-indigo-700">
<div>队列导出图片并用广播接受通知</div>
<div class="ml-1 text-indigo-500">
<svg viewBox="0 0 20 20" fill="currentColor" class="w-4 h-4"><path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
</div>
</div>
</a>
</app-layout>
</template>
<script>
...
//导入laravel echo
import { echo } from '@/laravel-echo'
export default defineComponent({
components: {
AppLayout,
JetApplicationLogo,
Link
},
created() {
//监听公共频道excel,响应下载excel文件
echo.channel('excel')
.listen('ExcelExportCompletedEvent', (e) => {
alert('ExcelExportCompletedEvent')
this.downloadExcel(e.excel_path,e.disk)
console.log(e);
})
},
methods: {
//下载方法
downloadExcel(excel_path,disk) {
const download_url = '/api/files/download?storage_path=' + excel_path + '&disk=' + disk
window.open(download_url)
},
//点击时间调用队列导航图片接口
queueImagesClick() {
axios.post('/api/excel/export/queue-images').then( response => {
console.log(response)
})
}
}
})
</script>
//开启websocket服务
php artisan websockets:serve
//开启horizon队列
php artisan horizon
演示
授权频道案例
nginx代理websocket
websocket单独使用一个域名不和http接口共享域名,配置如下
server {
listen 80;
server_name websocket.exhibition.demo;
location / {
proxy_pass http://127.0.0.1:6001;
proxy_read_timeout 60;
proxy_connect_timeout 60;
proxy_redirect off;
# Allow the use of websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
第二种方式websocket和http接口公用一个域名
map $http_upgrade $type {
default "";
websocket "ws";
}
server {
listen 80;
server_name api2.exhibition.demo;
root /home/yangliuan/Code/Php/business-logic/laravel-exhibition/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
index index.php;
charset utf-8;
location = /favicon.ico {
access_log off; log_not_found off;
}
location = /robots.txt {
access_log off; log_not_found off;
}
//http
location / {
try_files $uri $uri/ /index.php?$query_string;
}
//websocket
location @ws {
proxy_pass http://127.0.0.1:6001;
proxy_set_header Host $host;
proxy_read_timeout 60;
proxy_connect_timeout 60;
proxy_redirect off;
# Allow the use of websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location ~ [^/]\.php(/|$) {
fastcgi_pass unix:/dev/shm/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
参考