由于可能存在查询误差,需要多个网站对比
分类: 知识图谱
知识图谱
Linux文件系统 FAQ
rm cannot remove xxx Read-only file system 无法删除文件
df dir 查看无法删除的目录所在的文件系统
df /snap/remmina/
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/nvme0n1p2 479152840 195515620 259227828 43% /
fsck -y / #执行修复文件系统 的挂载根目录 或者 有问题的文件所在的挂载目录;
shutdown -r now
再删除ubuntu系统中的snap应用文件时上述方法没有解决
使用df -h 查看 发现remmina中的 /snap/remmina/4978 的挂载点是/dev/loop12
sudo umount /dev/loop12 去掉挂载后,再删除就可以了 ,原因是磁盘的挂载目录无法直接被删除
laravel广播使用笔记,基于laravel-websocket扩展包
阅读文档
安装需要的用的依赖
//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;
}
}
参考
FAQ: mysqld_safe mysqld from pid file xxx.pid ended
处理一个兼职人员部署的服务器故障
1.php故障
Warning: require_once(ROOTPATHconfig/bluebee.inc.php): failed to open stream: No such file or directory in /web/code/mygym/mygym_pc/include/head.php on line 23
Fatal error: require_once(): Failed opening required 'ROOTPATHconfig/bluebee.inc.php' (include_path='.:/usr/local/php54/lib/php') in /web/code/mygym/mygym_pc/include/head.php on line 23
boss反馈,一个兼职人员开发部署的项目,出现了这个问题,根据经验初步判断,是文件不存在了,或者目录没有权限,登录之后发现,这个phper根本没有linux基础,以及不懂php-fpm运行机制,文件用户和组是 nginx 然后给了777权限
解决方法
service php-fpm status 显示 php-fpm 是www 用户跑的,但是当前服务器没有该用户
执行如下命令,创建www用户并加入www用户组
groupadd www
useradd -g www -M -s /sbin/nologin www
更改代码目录权限
//更改用户组
chown -R www.www /web/code
//更改目录权限
chmod -R 755 /web/code
文件权限根据情况可以给644
2.php报错提示mysql连接失败
service mysqld status 显示启动失败,查看/var/log/mysqld.log 日志 报错如下
mysqld_safe mysqld from pid file /var/lib/mysql/mysqld.pid ended
/etc/my.cnf配置如下
# For advice on how to change settings please see
# http://dev.mysql.com/doc/refman/5.6/en/server-configuration-defaults.html
[mysqld]
skip-grant-tables
#
# Remove leading # and set to the amount of RAM for the most important data
# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
# innodb_buffer_pool_size = 128M
#
# Remove leading # to turn on a very important data integrity option: logging
# changes to the binary log between backups.
# log_bin
#
# Remove leading # to set options mainly useful for reporting servers.
# The server defaults are faster for transactions and fast SELECTs.
# Adjust sizes as needed, experiment to find the optimal values.
# join_buffer_size = 128M
# sort_buffer_size = 2M
# read_rnd_buffer_size = 2M
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
# Recommended in standard MySQL setup
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
发现是使用rpm包使用的社区版安装的。
尝试了各种发,都没有用,最后参考如下问答解决
restorecon -r /var/lib/mysql
service mysqld start
原因分析
可能是 /var/lib/mysql 目录,用户组 不是 mysql,
移动复制或修改文件时导致了 SELinux文件属性 丢失
首先保证 mysql 数据目录/var/lib/mysql 用户组 必须是 mysql
然后使用 restorecon 恢复文件的安全上下文
restorecon指令参考
如何实现给每个注册用户生成固定独立的URL地址
使用二级域名作为独立url, nginx使用通配符配置域名绑定
这种方案浪费域名资源
官方文档 https://nginx.org/en/docs/http/server_names.html
本地测试
host文件增加三个配置
127.0.0.1 abc.pan-domain.stu
127.0.0.1 efg.pan-domain.stu
127.0.0.1 yangliuan.pan-domain.stu
nginx配置
server {
listen 80;
#通配符绑定
server_name *.pan-domain.stu;
root /home/yangliuan/Code/Study/PHP/pan-domain;
index index.php;
location ~ [^/]\.php(/|$) {
fastcgi_pass unix:/dev/shm/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
location ~ ^/(\.user.ini|\.ht|\.git|\.svn|\.project|LICENSE|README.md) {
deny all;
}
}
测试结果
对其中一个域名yangliuan.pan-domain.stu 进行优先级测试,增加单独的站点配置和绑定,
server {
listen 80;
server_name yangliuan.pan-domain.stu;
root /home/yangliuan/Code/Study/PHP/test;
index index.php;
location ~ [^/]\.php(/|$) {
#fastcgi_pass remote_php_ip:9000;
fastcgi_pass unix:/dev/shm/php-cgi.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
location ~ ^/(\.user.ini|\.ht|\.git|\.svn|\.project|LICENSE|README.md) {
deny all;
}
}
结果如下
说明 server_name指令中 指定名称的优先级大于通配符
yangliuan.pan-domain.stu 的优先级大于 *.pan-domain.stu
如果云平台可以调用平台的域名解析接口,添加子域名解析 或者使用*.xxxx.com 泛域名解析
最简单实用的方案,给每个用户生成一个固定的独立子路由
- 使用用户昵称 命名
- 用户唯一标识 命名
PHP开发生态 常用笔记
软件包生态介绍
其实PHP有三套软件包生态:PEAR、PECL和Composer。
当然PEAR可以忽略不计:作为软件包,其托管的代码基本移植成了Composer包;
作为拓展安装工具,pecl是pear的别名,直接用pecl就完事。
所以PHP软件生态主要是两套:PECL和Composer。
开发辅助工具
phpstan 静态检测工具
PHP-CS-Fixer 编码格式化修复工具
laravel-ide-helper laravel IDE工具
PHP
m9rco/algorithm-php php实现算法
Intervention/image 最好用图片工具
guanguans/notify 项目出现异常时,发送通知到钉钉等…
ezyang/htmlpurifier XSS 过滤
php-casbin 权限控制
BaconQrCode 二维码工具
vinkla/hashids 唯一hashid生成工具
ratchetphp/Ratchet websocket库 php5.4起,适合旧版本
文件压缩
https://github.com/maennchen/ZipStream-PHP zip压缩
https://github.com/alchemy-fr/Zippy 支持.zip,.tar,.tar.gz,tar.bz2,
https://github.com/wapmorgan/UnifiedArchive 支持zip,rar,7z,tar
框架选择总结
业务快速开发用laravel,性能选webman,想用协程选hyperf,异步用ReactPHP
Laravel
php-fpm模式
PHP生态最好的web开发框架,社区活跃,简单实用,具备敏捷开发特质,扩展包和解决方案多,本身集成功能特别多。start数最多。
缺点性能比较差
适合业务复杂或对性能要求不高的场景,提升性能需要单独处理。
什么是laravel
框架方法API文档 https://laravel.com/api/8.x/
laravel-boilerplate laravel样板项目
laravel-devinit 项目初始化工具
stechstudio/laravel-zipstream ZipStream-PHP的laravel封装
laravel-permission RBAC权限扩展包
php-casbin/laravel-authz 权限控制
laravel-enum enum类型支持
mews/purifier XSS 过滤
overtrue/laravel-lang 语言包支持
spatie/laravel-translatable 数据库多语言包
Astrotomic/laravel-translatable 数据库多语言包
jenssegers / laravel-mongodb mongodb ORM 支持
barryvdh/laravel-snappy html转pdf 使用 wkhtmltopdf
barryvdh/laravel-dompdf html转pdf 使用 dompdf
simple-qrcode 二维码生成工具 在线文档
Eloquent Filter – 模型关联查询过滤
Maatwebsite/Laravel-Excel excel 导入导出
yajra/laravel-oci8 Oracle DB driver for Laravel 4|5|6|7|8 via OCI8
protonemedia/laravel-ffmpeg ffmpeg
Laravel-Phone– 全球手机号和电话验证
laravel-search 搜索扩展包 驱动支持 Elasticsearch, Algolia, and ZendSearch
laravel-geoip 根据访问者的 IP 地址确定网站访问者的位置
laravel-queue-rabbitmq laravel rabbitmq驱动
l5-repository Laravel 5 – Repositories to abstract the database layer
阿里云文件存储 laravel-filesystem-oss
七牛云文件存储 flysystem-qiniu
Xethron/migrations-generator 数据库转换迁移文件 <= 5.5
kitloong/laravel-migrations-generator 数据库转换迁移文件 >=5.5
Laravel集成的Faker数据模拟 参考文章 Github https://fakerphp.github.io/
laravel-exception-notify 多种通道的 laravel 异常通知(钉钉群机器人、飞书群机器人、Server 酱、企业微信群机器人、息知)
laravel-modules laravel-plugin 插件机制
codestudiohq / laravel-totem laravel 定时任务管理仪表盘
性能加速组件
laravel-s laravel-swoole Laravel Octane
symfony
Symfony 是一个用于 Web 和控制台应用程序的 PHP框架和一组可重用的 PHP 组件。企业级开发框架。引领了很多php业界标准,很多框架的底层组件都使用symfony。
累计下载上亿次
ThinkPHP
基础入门框架,简单实用尤其是3.2.3版本,生态
php-casbin/think-authz 权限控制
好用的CMS框架 https://www.thinkcmf.com/
Yii
没用过不做介绍
Swoole
Swoole
是一个使用C++
语言编写的基于异步事件驱动和协程的并行网络通信引擎(PHP底层扩展),为PHP
提供协程、高性能网络编程支持。提供了多种通信协议的网络服务器和客户端模块,可以方便快速的实现TCP/UDP服务
、高性能Web
、WebSocket服务
、物联网
、实时通讯
、游戏
、微服务
等,使PHP
不再局限于传统的 Web 领域。CLI模式常驻内存运行
API发展由swoole公司维护和决定. 社区产生分裂 国内原开发者为主swoole 国外开发者为主 openswoole
官方文档 社区
学习成本较高,从业务开发和就业角度(岗位少)来说,不如学golang。对于掌握了底层知识的人来说,学习起来很快。岗位较少。
缺点对传统php-fpm模式下的原有生态组件有不兼容情况,需要重新造轮子
通过开启一键协程功能,对php阻塞函数进行Hook,来达到兼容目的。
退出终止和阻塞函数不能使用,静态变量非必要不能使用,会增加内存溢出风险
用户案例 大厂用的多
hyperf
Work erman (工人)
高性能php容器,网络引擎框架,性能非常高,php框架中
排行第一 。生态相对冷门,对底层基础有要求,很多东西需要自己构建,没有开箱即用的现成轮子,需要其他第三方组件 benchmark (基准) CLI模式常驻内存运行
workerman 基础容器框架
gatewayworker 基于Workerman开发的一个项目框架 用于快速开发TCP长连接应用,例如app推送服务端、即时IM服务端、游戏服务端、物联网、智能家居等等
webman 基于workerman开发的高性能HTTP服务框架。webman用于替代传统的php-fpm架构,提供超高性能可扩展的HTTP服务。你可以用webman开发网站,也可以开发HTTP接口或者微服务。适合中小型对性能要求高项目
以最小内核提供最大的扩展性与最强的性能。
webman仅提供最核心的功能(路由、中间件、session、自定义进程接口)。其余功能全部复用composer生态,这意味着你可以在webman里使用最熟悉的功能组件,例如在数据库方面开发者可以选择使用Laravel的illuminate/database
,也可以是ThinkPHP的ThinkORM
,还可以是其它组件如Medoo
。在webman里集成他们是非常容易的事情。
ReactPHP
ReactPHP是PHP中用于事件驱动编程的底层库。它的核心是一个事件循环,在此基础上它提供了底层实用程序,例如:流抽象、异步DNS解析器、网络客户端/服务器、HTTP客户端/服务器以及进程间通信。第三方库可以使用这些组件创建异步网络客户端/服务器等。
Yar
Yar(yet another RPC framework, 教主问我为啥都是Ya打头, 呵呵, 因为这样名字好起)是我在3个多月前, 为了解决一个实际的问题, 而开发的一个PHP扩展的, RPC框架, 和现有的RPC框架(xml-rpc, soap)不同, 这是一个轻量级的框架, 支持多种打包协议(msgpack, json, php), 并且最重要的一个特点是, 它是可并行化的..
Yaf
PHP framework written in c and built as a PHP extension.
Yaf and Phalcon, which is faster?
Phalcon
Phalcon is an open source web framework delivered as a C extension for the PHP language providing high performance and lower resource consumption.
参考
GoAccess可视化实时监控nginx日志
参考
- 极客时间 Nginx核心知识100讲
- 官网 https://goaccess.io/ https://goaccess.cc/
- https://www.fanhaobai.com/2017/06/go-access.html
安装
参考文档
https://goaccess.io/download https://goaccess.io/get-started
ubuntu
sudo apt install goaccess
使用
终端试试显示
goaccess 日志文件路径 -c
生成静态页显示
goaccess 日志文件路径 -o 报告html页面路径 --log-format=COMBINED
实时静态页显示
goaccess 日志文件路径 -o 报告html页面路径 --log-format=COMBINED --real-time-html
nohup 启动
nohup goaccess 日志文件路径 -o 报告html页面路径 --log-format=COMBINED --real-time-html 输出日志文件 2>&1 &
阿里云在线扩容磁盘遇到的问题
按照官方文档扩容
输入growpart 命令时 没有查找一下该命令的参数和含义,照着文档以为是截图中的序号,结果报如下错误
NOCHANGE: partition 2 is size 0. it cannot be grown
growpart <DeviceName> <PartionNumber> 应该输入分区好,即截图中1,/dev/vdb 是设备名称 ,/dev/vdb1是设备名称+分区号,即设备分区 总结: 服务器运行不了解的命令一定要先查一下文档手册,看个大概,梳理一下操作的大概意思,然后在操作 完善一下文件系统的文档,复习一下文件系统的知识 参考
PHP处理富文本html标签的方法
使用XML 相关扩展操作
https://www.php.net/manual/zh/refs.xml.php
示例代码
以处理图片地址为例子
$htmlContent = '<p>测试<img src='xxx.jpeg'></p>';
$htmlDom = new DOMDocument();
@$htmlDom->loadHTML($htmlContent);
$images = $htmlDom->getElementsByTagName('img');
//处理富文本中的图片
foreach ($images as $key => $image)
{
//获取img图片的src属性值
$src = $image->getAttribute('src');
//拼接成完整的url
$image->setAttribute('src', 'http://xxxx.com'.$src);
}
//获取body标签的内容
$body = $htmlDom->getElementsByTagName('body')->item(0);
//转换成html字符串
$content = $htmlDom->saveHTML($body);
//替换掉body标签
$content = str_replace(['<body>', '</body>'], '', $content);