魔王卷子

魔王卷子 查看完整档案

北京编辑烟台南山学院  |  软件技术 编辑北京早耕田国际广告有限公司  |  PHP工程师 编辑 www.baoguoxiao.com 编辑
编辑

一个默默无闻的PHP程序员,专注于后端!

个人动态

魔王卷子 关注了专栏 · 11月5日

advanced-php

advanced php,托大了,托大了

关注 1440

魔王卷子 赞了文章 · 10月8日

Laravel 支持markdown编辑器解决方案

原文连接:https://www.wjcms.net/archive...

laravel-markdown-editor--markdown编辑器

说明

此扩展包兼容laravel5.8以上版本

准备工作

安装扩展包

composer require wjcms/laravel-markdown-editor

配置providers

//cconfig/app.php
'providers' => [
    //添加如下一行
    wjcms\laravelmd\LaravelmdServiceProvider::class,
]

拷贝相关文件到项目文件夹中

php artisan vendor:publish --provider="wjcms\laravelmd\LaravelmdServiceProvider"

使用

1.在blade模版引入

@include('layouts.md.md')

2.父模版中需要添加上

#注意在scripts上边需要引入jquery
@stack('styles')

@stack('scripts')

3.修改md.blade.php文件的 imageUploadURL修改为接口路径

4.创建service服务uploadservice.php,实现如下方法。

public function upload(UploadedFile $file)
    {
        $path = '/uploads/'.$file->store(date('y/m'), 'uploads');
        return $this->save($file, $path);
    }

//注意这里还需要创建Attachment模型和数据库(包含path,extension,name三个字段)
    protected function save(UploadedFile $file, $path)
    {
        return Attachment::create([
            'path'=>$path,
            'extension'=>$file->extension(),
            'name'=>$file->getClientOriginalName()
        ]);
    }

5.admin控制器创建方法

/**
     * 图片上传方法
     */
    public function uploadPic(Request $request, UploadService $uploadService)
    {
        $res = $uploadService->upload($request->file('editormd-image-file'));
        return response()->json([
            'success'=>1,
            'message'=>'图片上传成功',
            'url'=> $res->path
        ]);
    }

6.routes/web.php文件添加路由

use App\Http\Controllers\Admin;
//注意这里是laravel8的写法,之前版本自行修改
Route::prefix('admin')->name('admin.')->group(function () {
    Route::post('upload', [Admin\AdminController::class,'uploadPic'])->name('upload');
}

就可以发现markdown编辑器可以使用了。

查看原文

赞 1 收藏 0 评论 0

魔王卷子 回答了问题 · 9月27日

求救,php无限极分类不用递归怎么处理成层级结构?

/**
 * 把返回的数据集转换成Tree
 * @param array $list 要转换的数据集
 * @param string $pid parent标记字段
 * @param string $level level标记字段
 * @return array
 * @author 麦当苗儿 <zuojiazi@vip.qq.com>
 */
function list_to_tree($list, $pk='id', $pid = 'pid', $child = '_child', $root = 0) {
    // 创建Tree
    $tree = array();
    if(is_array($list)) {
        // 创建基于主键的数组引用
        $refer = array();
        foreach ($list as $key => $data) {
            $refer[$data[$pk]] =& $list[$key];
        }
        foreach ($list as $key => $data) {
            // 判断是否存在parent
            $parentId =  $data[$pid];
            if ($root == $parentId) {
                $tree[] =& $list[$key];
            }else{
                if (isset($refer[$parentId])) {
                    $parent =& $refer[$parentId];
                    $parent[$child][] =& $list[$key];
                }
            }
        }
    }
    return $tree;
}

来源:https://gitee.com/liu21st/one...

关注 3 回答 2

魔王卷子 收藏了文章 · 7月21日

出现大量TIME_WAIT连接的排查与解决

Last-Modified: 2019年7月10日21:58:43

项目生产环境出现大量TIME_WAIT(数千个), 需要一一排查

先上总结:

  • nginx 未开启 keep-alive 导致大量主动断开的tcp连接
  • nginx 与 fastcgi(php-fpm) 的连接默认是短连接, 此时必然出现 TIME_WAIT

状态确认

统计TIME_WAIT 连接的本地地址

netstat -an | grep TIME_WAIT | awk '{print $4}' | sort | uniq -c | sort -n -k1

#    ... 前面很少的略过
#    2 127.0.0.1:56420
#    442 192.168.1.213:8080
#    453 127.0.0.1:9000

分析:

  • 8080端口是nginx对外端口
  • 9000端口是php-fpm的端口

8080 对外web端口

经过确认, nginx 的配置文件中存在一行

# 不启用 keep-alive
keepalive_timeout 0;

尝试抓取 tcp 包

tcpdump tcp -i any -nn port 8080 | grep "我的ip"

# 其中某一次连接的输出如下
# 20:52:54.647907 IP 客户端.6470 > 服务端.8080: Flags [S], seq 2369523978, win 64240, options [mss 1460,nop,wscale 8,nop,nop,sackOK], length 0
# 20:52:54.647912 IP 服务端.8080 > 客户端.6470: Flags [S.], seq 1109598671, ack 2369523979, win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
# 20:52:54.670302 IP 客户端.6470 > 服务端.8080: Flags [.], ack 1, win 256, length 0
# 20:52:54.680784 IP 客户端.6470 > 服务端.8080: Flags [P.], seq 1:301, ack 1, win 256, length 300
# 20:52:54.680789 IP 服务端.8080 > 客户端.6470: Flags [.], ack 301, win 123, length 0
# 20:52:54.702935 IP 服务端.8080 > 客户端.6470: Flags [P.], seq 1:544, ack 301, win 123, length 543
# 20:52:54.702941 IP 服务端.8080 > 客户端.6470: Flags [F.], seq 544, ack 301, win 123, length 0
# 20:52:54.726494 IP 客户端.6470 > 服务端.8080: Flags [.], ack 545, win 254, length 0
# 20:52:54.726499 IP 客户端.6470 > 服务端.8080: Flags [F.], seq 301, ack 545, win 254, length 0
# 20:52:54.726501 IP 服务端.8080 > 客户端.6470: Flags [.], ack 302, win 123, length 0
上述具体的ip已经被我批量替换了, 不方便暴露服务器ip

分析:

  • 可以看到4次挥手的开始是由服务端主动发起的(记住TIME_WAIT只会出现在主动断开连接的一方)
  • 个人理解是, nginx 在配置"不启用keep-alive"时, 会在http请求结束时主动断开连接.
  • 尝试开启http的keep-alive

修改 nginx 配置

keepalive_timeout 65;

reload nginx

nginx -s reload

再次抓包

tcpdump tcp -i any -nn port 8080 | grep "我的ip"

# 21:09:10.044918 IP 客户端.8217 > 服务端.8080: Flags [S], seq 1499308169, win 64240, options [mss 1460,nop,wscale 8,nop,nop,sackOK], length 0
# 21:09:10.044927 IP 服务端.8080 > 客户端.8217: Flags [S.], seq 2960381462, ack 1499308170, win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
# 21:09:10.070694 IP 客户端.8217 > 服务端.8080: Flags [.], ack 1, win 256, length 0
# 21:09:10.077437 IP 客户端.8217 > 服务端.8080: Flags [P.], seq 1:302, ack 1, win 256, length 301
# 21:09:10.077443 IP 服务端.8080 > 客户端.8217: Flags [.], ack 302, win 123, length 0
# 21:09:10.198117 IP 服务端.8080 > 客户端.8217: Flags [P.], seq 1:671, ack 302, win 123, length 670
# 21:09:10.222957 IP 客户端.8217 > 服务端.8080: Flags [F.], seq 302, ack 671, win 254, length 0
# 21:09:10.222980 IP 服务端.8080 > 客户端.8217: Flags [F.], seq 671, ack 303, win 123, length 0
# 21:09:10.247678 IP 客户端.8217 > 服务端.8080: Flags [.], ack 672, win 254, length 0

注意看上面很有意思的地方:

  • tcp 的挥手只有3次, 而非正常的4次. 个人理解是, 服务端在收到 FIN 时, 已经确认自己不会再发送数据, 因此就将 FIN 与 ACK 一同合并发送
  • 此时是客户端主动断开tcp连接, 因此服务端不会出现 TIME_WAIT

再次查看连接状态

netstat -an | grep TIME_WAIT | awk '{print $4}' | sort | uniq -c | sort -n -k1
#      ...忽略上面
#      1 127.0.0.1:60602
#      1 127.0.0.1:60604
#    344 127.0.0.1:9000

此时发现已经没有处于 TIME_WAIT 的连接了.

9000 fast-cgi 端口

经过网上查找资料, 整理:

  • nginx 与 fast-cgi 的默认连接是短连接, 每次连接都需要经过一次完整的tcp连接与断开

当前 nginx 配置

upstream phpserver{
    server 127.0.0.1:9000 weight=1;
}

修改nginx配置使其与fastcgi的连接使用长连接

upstream phpserver{
    server 127.0.0.1:9000 weight=1;
    keepalive 100
}

fastcgi_keep_conn on;

说明:

  • upstream 中的 keepalive 指定nginx每个worker与fastcgi的最大长连接数, 当长连接不够用时, 此时新建立的连接会在请求结束后断开(由于此时指定了 HTTP1.1, fastcgi不会主动断开连接, 因此nginx这边会出现大量 TIME_WAIT, 需谨慎(未验证)
  • 由于php-fpm设置了最大进程数为100, 因此此处的 keepalive 数量指定 100 (未测试)

此处题外话, 如果 nginx 是作为反向代理, 则需增加如下配置:

# 将http版本由1.0修改为1.1
proxy_http_version 1.1;
# 清除"Connection"头部
proxy_set_header Connection "";    
  • 配置 proxy_pass 将请求转发给后端
  • 这里, 理解一下 proxy_passfastcgi_pass 区别

    客户端 --http-->  前端负载均衡Nginx --proxy_pass--> 业务服务器Nginx --fastcgi_pass--> 业务服务器 php-fpm

再次确认 tcp 连接情况

netstat -antp  | grep :9000 | awk '{print $(NF-1)}' | sort | uniq -c
#      6 ESTABLISHED
#      1 LISTEN

ok, 问题解决.

另一种解决方法:

若 nginx 与 fast-cgi 在同一台服务器上, 则使用 unix域 会更为高效, 同时避免了 TIME_WAIT 的问题.

题外

经过上面优化后, TIME_WAIT数量从上千个大幅下降到几十个, 此时发现TIME_WAIT中的存在大量的 127.0.0.1:6379, 6379是redis服务的默认端口....

赶紧改业务代码去, 将 $redis->connect(...) 改成 $redis->pconnect(...)

说明:

  • pconnect 表示 php-fpm 与 redis 建立 tcp 连接后, 在本次http请求结束后仍维持该连接, 下次新的请求进来时可以复用该连接, 从而复用了tcp连接.
查看原文

魔王卷子 赞了文章 · 7月21日

出现大量TIME_WAIT连接的排查与解决

Last-Modified: 2019年7月10日21:58:43

项目生产环境出现大量TIME_WAIT(数千个), 需要一一排查

先上总结:

  • nginx 未开启 keep-alive 导致大量主动断开的tcp连接
  • nginx 与 fastcgi(php-fpm) 的连接默认是短连接, 此时必然出现 TIME_WAIT

状态确认

统计TIME_WAIT 连接的本地地址

netstat -an | grep TIME_WAIT | awk '{print $4}' | sort | uniq -c | sort -n -k1

#    ... 前面很少的略过
#    2 127.0.0.1:56420
#    442 192.168.1.213:8080
#    453 127.0.0.1:9000

分析:

  • 8080端口是nginx对外端口
  • 9000端口是php-fpm的端口

8080 对外web端口

经过确认, nginx 的配置文件中存在一行

# 不启用 keep-alive
keepalive_timeout 0;

尝试抓取 tcp 包

tcpdump tcp -i any -nn port 8080 | grep "我的ip"

# 其中某一次连接的输出如下
# 20:52:54.647907 IP 客户端.6470 > 服务端.8080: Flags [S], seq 2369523978, win 64240, options [mss 1460,nop,wscale 8,nop,nop,sackOK], length 0
# 20:52:54.647912 IP 服务端.8080 > 客户端.6470: Flags [S.], seq 1109598671, ack 2369523979, win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
# 20:52:54.670302 IP 客户端.6470 > 服务端.8080: Flags [.], ack 1, win 256, length 0
# 20:52:54.680784 IP 客户端.6470 > 服务端.8080: Flags [P.], seq 1:301, ack 1, win 256, length 300
# 20:52:54.680789 IP 服务端.8080 > 客户端.6470: Flags [.], ack 301, win 123, length 0
# 20:52:54.702935 IP 服务端.8080 > 客户端.6470: Flags [P.], seq 1:544, ack 301, win 123, length 543
# 20:52:54.702941 IP 服务端.8080 > 客户端.6470: Flags [F.], seq 544, ack 301, win 123, length 0
# 20:52:54.726494 IP 客户端.6470 > 服务端.8080: Flags [.], ack 545, win 254, length 0
# 20:52:54.726499 IP 客户端.6470 > 服务端.8080: Flags [F.], seq 301, ack 545, win 254, length 0
# 20:52:54.726501 IP 服务端.8080 > 客户端.6470: Flags [.], ack 302, win 123, length 0
上述具体的ip已经被我批量替换了, 不方便暴露服务器ip

分析:

  • 可以看到4次挥手的开始是由服务端主动发起的(记住TIME_WAIT只会出现在主动断开连接的一方)
  • 个人理解是, nginx 在配置"不启用keep-alive"时, 会在http请求结束时主动断开连接.
  • 尝试开启http的keep-alive

修改 nginx 配置

keepalive_timeout 65;

reload nginx

nginx -s reload

再次抓包

tcpdump tcp -i any -nn port 8080 | grep "我的ip"

# 21:09:10.044918 IP 客户端.8217 > 服务端.8080: Flags [S], seq 1499308169, win 64240, options [mss 1460,nop,wscale 8,nop,nop,sackOK], length 0
# 21:09:10.044927 IP 服务端.8080 > 客户端.8217: Flags [S.], seq 2960381462, ack 1499308170, win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0
# 21:09:10.070694 IP 客户端.8217 > 服务端.8080: Flags [.], ack 1, win 256, length 0
# 21:09:10.077437 IP 客户端.8217 > 服务端.8080: Flags [P.], seq 1:302, ack 1, win 256, length 301
# 21:09:10.077443 IP 服务端.8080 > 客户端.8217: Flags [.], ack 302, win 123, length 0
# 21:09:10.198117 IP 服务端.8080 > 客户端.8217: Flags [P.], seq 1:671, ack 302, win 123, length 670
# 21:09:10.222957 IP 客户端.8217 > 服务端.8080: Flags [F.], seq 302, ack 671, win 254, length 0
# 21:09:10.222980 IP 服务端.8080 > 客户端.8217: Flags [F.], seq 671, ack 303, win 123, length 0
# 21:09:10.247678 IP 客户端.8217 > 服务端.8080: Flags [.], ack 672, win 254, length 0

注意看上面很有意思的地方:

  • tcp 的挥手只有3次, 而非正常的4次. 个人理解是, 服务端在收到 FIN 时, 已经确认自己不会再发送数据, 因此就将 FIN 与 ACK 一同合并发送
  • 此时是客户端主动断开tcp连接, 因此服务端不会出现 TIME_WAIT

再次查看连接状态

netstat -an | grep TIME_WAIT | awk '{print $4}' | sort | uniq -c | sort -n -k1
#      ...忽略上面
#      1 127.0.0.1:60602
#      1 127.0.0.1:60604
#    344 127.0.0.1:9000

此时发现已经没有处于 TIME_WAIT 的连接了.

9000 fast-cgi 端口

经过网上查找资料, 整理:

  • nginx 与 fast-cgi 的默认连接是短连接, 每次连接都需要经过一次完整的tcp连接与断开

当前 nginx 配置

upstream phpserver{
    server 127.0.0.1:9000 weight=1;
}

修改nginx配置使其与fastcgi的连接使用长连接

upstream phpserver{
    server 127.0.0.1:9000 weight=1;
    keepalive 100
}

fastcgi_keep_conn on;

说明:

  • upstream 中的 keepalive 指定nginx每个worker与fastcgi的最大长连接数, 当长连接不够用时, 此时新建立的连接会在请求结束后断开(由于此时指定了 HTTP1.1, fastcgi不会主动断开连接, 因此nginx这边会出现大量 TIME_WAIT, 需谨慎(未验证)
  • 由于php-fpm设置了最大进程数为100, 因此此处的 keepalive 数量指定 100 (未测试)

此处题外话, 如果 nginx 是作为反向代理, 则需增加如下配置:

# 将http版本由1.0修改为1.1
proxy_http_version 1.1;
# 清除"Connection"头部
proxy_set_header Connection "";    
  • 配置 proxy_pass 将请求转发给后端
  • 这里, 理解一下 proxy_passfastcgi_pass 区别

    客户端 --http-->  前端负载均衡Nginx --proxy_pass--> 业务服务器Nginx --fastcgi_pass--> 业务服务器 php-fpm

再次确认 tcp 连接情况

netstat -antp  | grep :9000 | awk '{print $(NF-1)}' | sort | uniq -c
#      6 ESTABLISHED
#      1 LISTEN

ok, 问题解决.

另一种解决方法:

若 nginx 与 fast-cgi 在同一台服务器上, 则使用 unix域 会更为高效, 同时避免了 TIME_WAIT 的问题.

题外

经过上面优化后, TIME_WAIT数量从上千个大幅下降到几十个, 此时发现TIME_WAIT中的存在大量的 127.0.0.1:6379, 6379是redis服务的默认端口....

赶紧改业务代码去, 将 $redis->connect(...) 改成 $redis->pconnect(...)

说明:

  • pconnect 表示 php-fpm 与 redis 建立 tcp 连接后, 在本次http请求结束后仍维持该连接, 下次新的请求进来时可以复用该连接, 从而复用了tcp连接.
查看原文

赞 3 收藏 2 评论 3

魔王卷子 回答了问题 · 5月25日

解决php框架的ORM 是否能屏蔽MySQL数据库高低版本之间的语法问题?

请问你公司研发了mysql9了吗?赶紧拉出来赚钱啊。国产数据库就靠你们了

关注 5 回答 4

魔王卷子 赞了文章 · 5月21日

微信域名拦截检测API源码 检测域名是否能在微信正常打开

可一键检测域名是否被微信屏蔽(网站是否可以在微信客户端中打开),适合做防红防拦截功能,便于及时更换域名。

<?php
/** ----------------------------------
* wx域名检测
* Time:2020-5-15
* www.likeyunba.com
-------------------------------------- **/

header('Content-type: text/json;charset=utf-8'); 
if(empty($_REQUEST['url'])){
    exit('{"code":0,"msg":"参数不正确"}');
}
$api = get_headers('http://mp.weixinbridge.com/mp/wapredirect?url='.$_REQUEST['url']);

if($api[1] !== 'Location: '.$_REQUEST['url'].''){
        $value = array('code'=>202,'msg'=>'域名被封');
}else{
        $value = array('code'=>200,'msg'=>'域名正常');
}
echo json_encode($value,JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE);
?>

测试:

http://www.likeyunba.com/api/wx_domain_check.php?url=https://www.qq.com/
http://www.likeyunba.com/api/wx_domain_check.php?url=https://www.taobao.com/

学习交流WeChat:face6009
个人网站:https://www.likeyunba.com/

查看原文

赞 6 收藏 4 评论 1

魔王卷子 关注了用户 · 5月9日

谢顶道人 @laolixiuxiu

排头兵@滴滴出行
公众号:高性能API社区

关注 643

魔王卷子 回答了问题 · 4月25日

ubuntu20.04如何安装php的Yaf扩展?

你在php.ini里面添加extension=yaf.so了吗?

关注 2 回答 1

魔王卷子 回答了问题 · 4月25日

php多进程间通信的问题

建议参考 workerman。

关注 4 回答 3

认证与成就

  • 获得 47 次点赞
  • 获得 25 枚徽章 获得 1 枚金徽章, 获得 5 枚银徽章, 获得 19 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

  • PHP版迁移

    使用illuminate组件实现了独立的Laravel迁移功能

注册于 2015-12-10
个人主页被 1.4k 人浏览