无题

  • laravel-后台API token管理、用户管理、权限管理、操作日志、文章管理、分类管理
    ~~ vue后台模板(使用已开源的) ~~
  • socket、swoole、easyswoole
  • phpmywind改造成自适应的(html-php混编)
  • mysql-数据优化、索引优化、事务、锁机制
  • redis 队列等场景使用
  • python 爬数据
  • go

vue后台模板(使用已开源的)

todo-list

  • 用docker部署配置nginx负载均衡

  • 用docker部署mysql主从同步

  • 用docker部署mysql读写分离

  • 用docker部署集群mysql

  • 用docker部署集群redis

  • redis备份与恢复

  • mac多php、mysql、nginx、apache版本安装

  • thinkpad共享文件夹-mac映射共享文件夹

  • vscode远程编辑配置

  • laravel-admin 参考 部署 moell-peng/mojito

  • easyswoole-长连接认证-laravel的各种认证对比

问题

  • 如何查看(设置)PHP当前启动了多少个进程、线程、协程

吞吐量(TPS)、QPS、并发数、响应时间(RT)概念

QPS

原理:每天80%的访问集中在20%的时间里,这20%时间叫做峰值时间。

公式:( 总PV数 80% ) / ( 每天秒数 20% ) = 峰值时间每秒请求数(QPS) 。

机器:峰值时间每秒QPS / 单台机器的QPS = 需要的机器 。

每天300w PV 的在单台机器上,这台机器需要多少QPS?

( 3000000 0.8 ) / (86400 0.2 ) = 139 (QPS)。

一般需要达到139QPS,因为是峰值。

每秒查询率QPS是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。

每秒查询率

因特网上,经常用每秒查询率来衡量域名系统服务器的机器的性能,其即为QPS。

对应fetches/sec,即每秒的响应请求数,也即是最大吞吐能力。

计算机语言

一种计算机编程语言。用于数据分析和报表产出。运作的平台是MRDCL。支持的数据文件包括ASC格式和CSI格式。

其中CSI格式为QPS独有数据格式。是极其专业的用于数据分析、数据清理和报表产出的语言,目前应用最广的是市场调研行业。中国国内运用的相对比较少。

开发的原因,需要对吞吐量(TPS)、QPS、并发数、响应时间(RT)几个概念做下了解,查自百度百科,记录如下:

  1. 响应时间(RT)
      响应时间是指系统对请求作出响应的时间。直观上看,这个指标与人对软件性能的主观感受是非常一致的,因为它完整地记录了整个计算机系统处理请求的时间。由于一个系统通常会提供许多功能,而不同功能的处理逻辑也千差万别,因而不同功能的响应时间也不尽相同,甚至同一功能在不同输入数据的情况下响应时间也不相同。所以,在讨论一个系统的响应时间时,人们通常是指该系统所有功能的平均时间或者所有功能的最大响应时间。当然,往往也需要对每个或每组功能讨论其平均响应时间和最大响应时间。
      对于单机的没有并发操作的应用系统而言,人们普遍认为响应时间是一个合理且准确的性能指标。需要指出的是,响应时间的绝对值并不能直接反映软件的性能的高低,软件性能的高低实际上取决于用户对该响应时间的接受程度。对于一个游戏软件来说,响应时间小于100毫秒应该是不错的,响应时间在1秒左右可能属于勉强可以接受,如果响应时间达到3秒就完全难以接受了。而对于编译系统来说,完整编译一个较大规模软件的源代码可能需要几十分钟甚至更长时间,但这些响应时间对于用户来说都是可以接受的。

  2. 吞吐量(Throughput)
    吞吐量是指系统在单位时间内处理请求的数量。对于无并发的应用系统而言,吞吐量与响应时间成严格的反比关系,实际上此时吞吐量就是响应时间的倒数。前面已经说过,对于单用户的系统,响应时间(或者系统响应时间和应用延迟时间)可以很好地度量系统的性能,但对于并发系统,通常需要用吞吐量作为性能指标。
      对于一个多用户的系统,如果只有一个用户使用时系统的平均响应时间是t,当有你n个用户使用时,每个用户看到的响应时间通常并不是n×t,而往往比n×t小很多(当然,在某些特殊情况下也可能比n×t大,甚至大很多)。这是因为处理每个请求需要用到很多资源,由于每个请求的处理过程中有许多不走难以并发执行,这导致在具体的一个时间点,所占资源往往并不多。也就是说在处理单个请求时,在每个时间点都可能有许多资源被闲置,当处理多个请求时,如果资源配置合理,每个用户看到的平均响应时间并不随用户数的增加而线性增加。实际上,不同系统的平均响应时间随用户数增加而增长的速度也不大相同,这也是采用吞吐量来度量并发系统的性能的主要原因。一般而言,吞吐量是一个比较通用的指标,两个具有不同用户数和用户使用模式的系统,如果其最大吞吐量基本一致,则可以判断两个系统的处理能力基本一致。

  3. 并发用户数
      并发用户数是指系统可以同时承载的正常使用系统功能的用户的数量。与吞吐量相比,并发用户数是一个更直观但也更笼统的性能指标。实际上,并发用户数是一个非常不准确的指标,因为用户不同的使用模式会导致不同用户在单位时间发出不同数量的请求。一网站系统为例,假设用户只有注册后才能使用,但注册用户并不是每时每刻都在使用该网站,因此具体一个时刻只有部分注册用户同时在线,在线用户就在浏览网站时会花很多时间阅读网站上的信息,因而具体一个时刻只有部分在线用户同时向系统发出请求。这样,对于网站系统我们会有三个关于用户数的统计数字:注册用户数、在线用户数和同时发请求用户数。由于注册用户可能长时间不登陆网站,使用注册用户数作为性能指标会造成很大的误差。而在线用户数和同事发请求用户数都可以作为性能指标。相比而言,以在线用户作为性能指标更直观些,而以同时发请求用户数作为性能指标更准确些。

  4. QPS每秒查询率(Query Per Second)
      每秒查询率QPS是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准,在因特网上,作为域名系统服务器的机器的性能经常用每秒查询率来衡量。对应fetches/sec,即每秒的响应请求数,也即是最大吞吐能力。 (看来是类似于TPS,只是应用于特定场景的吞吐量)

转载自文章

JMeter端口占用处理

windows 环境下的端口循环回收需要消耗2~4分钟。由此猜测可能是由于 windows下压测端口数有限,端口资源被占满,没有及时循环回收,导致报错

修改注册表

使用 win + R 快捷键打开 cmd,输入 regedit 命令打开注册表

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters

右键新建参数(DWORD),并设置值。

l MaxUserPort    十进制 65534

l TcpTimedWaitDelay 十进制 30

l KeepAliveTime      006ddd00

l KeepAliveInterval    000003e8

l MaxDataRetries 5

参考资料

Jmeter测试会出现端口占用情况
windows下Jmeter压测端口占用问题

nginx简单认证访问

生成的swagger文档页面没有用户限制 需要简单的认证后才可以访问

  1. 安装工具
    sudo apt install apache2-utils

  2. 生成账号密码
    htpasswd -c ./pwd user

  3. 配置文件

    server {
    listen       80;
    server_name  local.server.com;
    **auth_basic "User Authentication";
    auth_basic_user_file /etc/nginx/pwd;**
    location / {
        root   /data/www;
        index  index.html;
    }
    }
  4. 针对性配置

    location ~ /.*/swagger-ui.html.* {
      #密码登录
      auth_basic "接口文档";
      auth_basic_user_file /opt/nginxpwd;
      # 不同的路径进入到不同地址,还不知道怎么改成一个通配符,自动填充
      if ( $uri ~ /smsapi/.* ) {
        proxy_pass http://smsapi;
      }
      if ( $uri ~ /find/.* ) {
        proxy_pass http://find;
      }
      proxy_redirect off;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

相关资料

简单实现 nginx 用户登录认证
nginx 拦截 swagger 登录

gitlab重置root的密码


-sh-4.2$ gitlab-rails console production
-------------------------------------------------------------------------------------
 GitLab:       12.0.3 (08a51a9db93)
 GitLab Shell: 9.3.0
 PostgreSQL:   10.7
-------------------------------------------------------------------------------------
Loading production environment (Rails 5.1.7)
irb(main):001:0> user = User.where(id: 1).first
=> #<User id:1 @root>
irb(main):004:0> user.password=12345678
=> 12345678
irb(main):005:0> user.save!
Enqueued ActionMailer::DeliveryJob (Job ID: 60439440-6443-46d1-acda-e29172be146a) to Sidekiq(mailers) with arguments: "DeviseMailer", "password_change", "deliver_now", #<GlobalID:0x00007f09e949b958 @uri=#<URI::GID gid://gitlab/User/1>>
=> true
irb(main):006:0> exit
-sh-4.2$ exit
[root@qas-k8s-gitlab gitlab]# gitlab-ctl restart
ok: run: alertmanager: (pid 43507) 0s
ok: run: gitaly: (pid 43523) 0s
ok: run: gitlab-monitor: (pid 43542)
————————————————

原文地址

easyswoole用ioc重写系统日志类

easyswoole 3.3.7

定义自己的日志类

<?php

namespace App\Ioc;

use EasySwoole\Log\LoggerInterface;
use EasySwoole\Log\Logger;

class Log implements LoggerInterface
{

    private $logger;

    function __construct()
    {
        $this->logger = new Logger(EASYSWOOLE_LOG_DIR);
    }

    function log(?string $msg,int $logLevel = self::LOG_LEVEL_INFO,string $category = 'debug')
    {
        return $this->logger->log($msg, $logLevel, $category);
    }

    function console(?string $msg,int $logLevel = self::LOG_LEVEL_INFO,string $category = 'debug')
    {
        var_dump($msg);
        $this->logger->console($msg, $logLevel, $category);
    }
}

IOC覆盖系统日志类

项目根目录创建文件 bootstrap.php

<?php

use App\Ioc\Log;
use EasySwoole\Component\Di;
use EasySwoole\EasySwoole\SysConst;

Di::getInstance()->set(SysConst::LOGGER_HANDLER, Log::class);

分析

EasySwoole\EasySwoole\Core.php

查看源码中的registerErrorHandler方法

....
        //初始化配置Logger
        $logger = Di::getInstance()->get(SysConst::LOGGER_HANDLER);
        if(!$logger instanceof LoggerInterface){
            $logger = new DefaultLogger(EASYSWOOLE_LOG_DIR);
        }
        Logger::getInstance($logger);

        //初始化追追踪器
        $trigger = Di::getInstance()->get(SysConst::TRIGGER_HANDLER);
        if(!$trigger instanceof TriggerInterface){
            $trigger = new DefaultTrigger(Logger::getInstance());
        }
        Trigger::getInstance($trigger);

        //在没有配置自定义错误处理器的情况下,转化为trigger处理
        $errorHandler = Di::getInstance()->get(SysConst::ERROR_HANDLER);
        if(!is_callable($errorHandler)){
            $errorHandler = function($errorCode, $description, $file = null, $line = null){
                $l = new Location();
                $l->setFile($file);
                $l->setLine($line);
                Trigger::getInstance()->error($description,$errorCode,$l);
            };
        }
        set_error_handler($errorHandler);
....

查看 easyswoole-事件文档

查看 easyswoole-生命周期

easyswoole之消息队列nqs

easyswoole版本: 3.3.7

文档提供有安装和docker-compose示例代码,启动的时候会报告错误(如果你不调试可能也看不到错误).

错误1: TOPIC_NOT_FOUND 你需要先创建一个topic 登录到nqs中创建

错误2: UPSTREAM_ERROR: Failed to query any nsqlookupd 这个时候应该是没有数据需要执行命令 curl -d 'hello world 2' 'http://127.0.0.1:4151/pub?topic=test'

错误3: [2020-06-24 09:28:50][debug][warning]:[Swoole\Client::recv(): recv() failed. Error: Resource temporarily unavailable [11] at file:/var/www/eseckill/vendor/easyswoole/nsq/src/Connection/AbstractMonitor.php line:52 作者回应是断线问题, 需要自己处理断线重连.

相关资料

官方文档
github-nqs

easyswoole自义命令加载自定义配置文件

需求: 目前框架默认只会加载两个配置文件 dev.php 和 produce.php 只有本地开发和线上生产两个环境, 但是现在需要一个线上测试环境的配置.

版本: easyswoole-3.3.7

loadEnv 加载配置文件的方法

启动命令

启动命令会执行loadEnv方法, 我们自己复制Command/DefaultCommand/Start.php 文件加载自己的配置文件覆盖之前的配置

在自己的项目中 App/Command/test.php 增加一个文件 如何自定义命令点我

<?php
/**
 * Created by PhpStorm.
 * User: yf
 * Date: 2019-01-24
 * Time: 23:44
 */

namespace EasySwoole\EasySwoole\Command\DefaultCommand;

use EasySwoole\EasySwoole\Command\CommandInterface;
use EasySwoole\EasySwoole\Command\Utility;
use EasySwoole\EasySwoole\Config;
use EasySwoole\EasySwoole\Core;
use EasySwoole\EasySwoole\ServerManager;
use EasySwoole\EasySwoole\SysConst;

class Start implements CommandInterface
{

    public function commandName(): string
    {
        // TODO: Implement commandName() method.
        return 'start';
    }

    public function exec(array $args): ?string
    {
        // TODO: Implement exec() method.
        Utility::opCacheClear();
        $response = Utility::easySwooleLog();
        $mode = 'develop';
        if (!Core::getInstance()->isDev()) {
            $mode = 'produce';
        }
        $conf = Config::getInstance();
        if (in_array("d", $args) || in_array("daemonize", $args)) {
            $conf->setConf("MAIN_SERVER.SETTING.daemonize", true);
        }
        //create main Server
        Core::getInstance()->globalInitialize()->createServer();
        //重点这一行
        Config::getInstance()->loadEnv( EASYSWOOLE_ROOT.'/test.php');
        $serverType = $conf->getConf('MAIN_SERVER.SERVER_TYPE');
        switch ($serverType) {
            case EASYSWOOLE_SERVER:
                {
                    $serverType = 'SWOOLE_SERVER';
                    break;
                }
            case EASYSWOOLE_WEB_SERVER:
                {
                    $serverType = 'SWOOLE_WEB';
                    break;
                }
            case EASYSWOOLE_WEB_SOCKET_SERVER:
                {
                    $serverType = 'SWOOLE_WEB_SOCKET';
                    break;
                }
            case EASYSWOOLE_REDIS_SERVER:
                {
                    $serverType = 'SWOOLE_REDIS';
                    break;
                }
            default:
                {
                    $serverType = 'UNKNOWN';
                }
        }
        $response = $response . Utility::displayItem('main server', $serverType) . "\n";
        $response = $response . Utility::displayItem('listen address', $conf->getConf('MAIN_SERVER.LISTEN_ADDRESS')) . "\n";
        $response = $response . Utility::displayItem('listen port', $conf->getConf('MAIN_SERVER.PORT')) . "\n";
        $list = ServerManager::getInstance()->getSubServerRegister();
        $index = 1;
        foreach ($list as $serverName => $item) {
            if (empty($item['setting'])) {
                $type = $serverType;
            } else {
                $type = $item['type'] % 2 > 0 ? 'SWOOLE_TCP' : 'SWOOLE_UDP';
            }
            $response = $response . Utility::displayItem("sub server:{$serverName}", "{$type}@{$item['listenAddress']}:{$item['port']}") . "\n";
            $index++;
        }
        $ips = swoole_get_local_ip();
        foreach ($ips as $eth => $val) {
            $response = $response . Utility::displayItem('ip@' . $eth, $val) . "\n";
        }

        $data = $conf->getConf('MAIN_SERVER.SETTING');
        if(empty($data['user'])){
            $data['user'] = get_current_user();
        }

        if(!isset($data['daemonize'])){
            $data['daemonize'] = false;
        }

        foreach ($data as $key => $datum){
            $response = $response . Utility::displayItem($key,$datum) . "\n";
        }

        $response = $response . Utility::displayItem('swoole version', phpversion('swoole')) . "\n";
        $response = $response . Utility::displayItem('php version', phpversion()) . "\n";
        $response = $response . Utility::displayItem('easy swoole', SysConst::EASYSWOOLE_VERSION) . "\n";
        $response = $response . Utility::displayItem('develop/produce', $mode) . "\n";
        $response = $response . Utility::displayItem('temp dir', EASYSWOOLE_TEMP_DIR) . "\n";
        $response = $response . Utility::displayItem('log dir', EASYSWOOLE_LOG_DIR) . "\n";
        echo $response;
        Core::getInstance()->start();
        return null;
    }

    public function help(array $args): ?string
    {
        // TODO: Implement help() method.
        $logo = Utility::easySwooleLog();
        return $logo . <<<HELP_START
\e[33mOperation:\e[0m
\e[31m  php easyswoole start [arg1] [arg2]\e[0m
\e[33mIntro:\e[0m
\e[36m  to start current easyswoole server \e[0m
\e[33mArg:\e[0m
\e[32m  daemonize \e[0m                   run in daemonize
\e[32m  produce \e[0m                     load produce.php
HELP_START;
    }
}

mvn 编码 GBK 的不可映射字符

问题提示为字符编码错误 要么是文件中的编码问题要么就是IDEA要么就是CMD的编码问题

出现这个问题是自己的mvn没有配置环境变量 那么默认就是使用GBK编码方式. 所以当项目中出现有中文字符就换报这个BGK不可映射字符问题

(方法1)设置环境变量(全局配置)

变量名 MAVEN_OPTS
变量值 -Xms256m -Xmx512m -Dfile.encoding=UTF-8
保存,退出cmd.重新打开cmd 运行mvn -v命令即可

(方法2)配置pom.xml(需要对每个模块配置)

在配置pom.xml加入下面一行配置项

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.hconec.cn</groupId>
        <artifactId>hconec-common</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.xxxx.cn</groupId>
    <artifactId>xxxx-common-php</artifactId>
    <name>xxxx-common-php</name>
    <description>sdfsdfsdf</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.xxxx.cn</groupId>
            <artifactId>xxxx-common-core</artifactId>
        </dependency>
    </dependencies>

</project>

相关资料

修改maven的编码格式为utf-8
maven编码gbk的不可映射字符解决办法