php 创建守护进程

function createDeamon() {
        set_time_limit(0);

        // 只允许在cli下面运行  
        if (php_sapi_name() != "cli") {
            die("only run in command line mode\n");
        }

        umask(0); //把文件掩码清0  

        if (pcntl_fork() != 0) { //是父进程,父进程退出  
            exit();
        } 

        posix_setsid(); //设置新会话组长,脱离终端  

        if (pcntl_fork() != 0) { //第二次fock子进程  
            exit();
        }


        chdir("/"); //改变工作目录  

        $user = posix_getpwnam(self::getConfig('deamon', 'user'));
        if ($user) {
            $uid = $user['uid'];
            $gid = $user['gid'];
            $result = posix_setuid($uid);
            posix_setgid($gid);
        } else {
            die('守护进程用户权限设置失败,请重新设置!');
        }

        //关闭打开的文件描述符  
        fclose(STDIN);
        fclose(STDOUT);
        fclose(STDERR);
        
        global $STDIN, $STDOUT, $STDERR;
        $outputfile = self::getConfig('deamon', 'outputfile');
        $dirname = dirname($outputfile);
        if( !file_exists($dirname) ){
            mkdir($dirname);
        }
        
        $STDIN = fopen('/dev/null', "a");
        $STDOUT = fopen($outputfile, "a");
        $STDERR = fopen($outputfile, "a");
    }
发表在 php, php函数集 | 留下评论

mysql 忘记root密码,重置mysql root

1. service mysql stop
2. 进入到mysql安装目录 比如/usr/mysql
3. 在安装目录执行(不再安装目录执行可能报错) ./bin/mysqld_safe –skip-grant-tables –skip-networking &
4. mysql -uroot -p (回车、回车)进入到mysql命令行
5. update mysql.user set Password=password(‘123′) where user=’root’ and Host = ‘localhost’;
flush privileges;
(如果没有 Password字段,则把Password换成authentication_string)
6. 退出mysql命令行
7. service mysql restart
8. service mysql stop
9. service mysql start
(7 ~ 9步保证mysql正常启动)
10. mysql -uroot -p123
11. set password for ‘root’@’localhost’=password(‘123’);
(保证新密码完全生效)

发表在 mysql | 留下评论

go, 监控git是否更新, 并且自动构建

使用方式 ./command -p [git 目录] -s [每s秒查看一次git版本变化,默认60]

package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"
)

var gitVersion = ""
var wg sync.WaitGroup
var path string
var sleepTime int

func ExecCommand(command string, params []string, dir string) string{
	cmd := exec.Command(command, params...)

	if dir != "" {
		cmd.Dir = dir
	}

	var out bytes.Buffer
	cmd.Stdout = &out
	err := cmd.Run()
	if err != nil {
		log.Fatal("error   ", err)
	}

	return out.String()
}

func InitArgs() {
	flag.StringVar(&path, "p", "", "git路径")
	flag.IntVar(&sleepTime, "s", 60, "每多少秒检查一次git version")
	flag.Parse()
}

func dirExists(p string) bool{
	fi, err := os.Stat(p)
	if err != nil {
		return false
	}

	return fi.IsDir()
}

func getVerson(vChan chan <- string){
	for {
		ExecCommand("git", []string{"pull"}, path)
		out := ExecCommand("git", []string{ "log", "-1"}, path)
		lines := strings.Split(out, "\n")
		vChan <- strings.Split(lines[0], " ")[1]
		time.Sleep( time.Second * time.Duration(sleepTime) )
	}

	wg.Done()
}


func do(vChan <- chan string){
	for{
		_v := <- vChan
		if _v != gitVersion {
			fmt.Println("获取到新版本,开始更新...")
			gitVersion = _v

			res2 := ExecCommand("yarn", []string{}, path)
			res3 := ExecCommand("yarn", []string{"build"}, path)
			fmt.Println("已经构建完成:", res2, res3)

			fmt.Println("当前版本为:"+gitVersion)
		}
	}

}

func main() {
	InitArgs()

	if path == "" {
		log.Fatal("路径参数不能为空")
	}

	if !dirExists(path)  {
		log.Fatal("目录不存在:" + path)
	}

	fmt.Println("开始监控目录:",path)

	vChan := make(chan string, 1)

	wg.Add(2)
	go getVerson(vChan)
	go do(vChan)
	wg.Wait()
}
发表在 go | 标签为 | 留下评论

yii2 实现灵活的限流方式

yii2自带的限流方式使用做在某路径下的,但是想要实现在某种规则下才限流,该怎么做呢?
(如果对限流的逻辑不明白,请搜索“漏斗算法”)
1. 对自带的限流工具做一下简单的修改,个人觉得,自带的限流算法有个小小的问题,在超限调用接口的时候,不应当记录次数
(library是我个人创建的放公共包的目录)

<?php
namespace app\library;

use yii\web\TooManyRequestsHttpException;

class RateLimiter extends \yii\filters\RateLimiter
{
    public function checkRateLimit($user, $request, $response, $action)
    {
        list($limit, $window) = $user->getRateLimit($request, $action);
        list($allowance, $timestamp) = $user->loadAllowance($request, $action);

        $current = time();

        $allowance += (int) (($current - $timestamp) * $limit / $window);
        if ($allowance > $limit) {
            $allowance = $limit;
        }

        if ($allowance < 1) {
            //只注释了下面这一行代码,如果次数不足,则不更新时间
//            $user->saveAllowance($request, $action, 0, $current);
            $this->addRateLimitHeaders($response, $limit, 0, $window);
            throw new TooManyRequestsHttpException($this->errorMessage);
        }

        $user->saveAllowance($request, $action, $allowance - 1, $current);
        $this->addRateLimitHeaders($response, $limit, $allowance - 1, (int) (($limit - $allowance + 1) * $window / $limit));
    }


}

2. 创建限流的对象(代码仅仅做个抛砖引玉,可以根据个人需要优化哈,也可以搞一个独立的限流工具)

        $this->rateLimiter = Yii::createObject([
            'class' => \app\library\RateLimiter::class,
            'errorMessage' => '您已经提交过,请勿重复提交',//超限提示
            'enableRateLimitHeaders' => false,//是否把限流的详情展示到header中
            'user' => new class implements RateLimitInterface {//实现限流对象
                public function __construct()
                {//限流使用session,这里可以改成你想要的,也可以封装成store工厂
                 //提示,使用session实现的限流算法可能是不安全的(达不到限流的目的),请自行调整
                    $session = Yii::$app->getSession();
                    $session->open();
                    $this->store = $session;
                }

                public function getRateLimit($request, $action)
                {
                    if ($action->id == 'xxxx') {//这里可以针对不同的action做到不同的限流方式
                        return [1, 300];//300秒之内只能执行一次
                    }
                    return [2, 1];//1秒之内只能执行2次
                }

                public function loadAllowance($request, $action)
                {
                    return $this->get($action->id);//获取上一次的限流情况
                }

                public function saveAllowance($request, $action, $allowance, $timestamp)
                {
                    $this->save($action->id, $allowance, $timestamp);//保存本次的限流情况
                }

                public function get($id)//返回的是[上一次的使用后剩余次数,上一次的更新时间]
                {
                    return [$this->store[$id . '-allowance'], $this->store[$id . '-timestamp']];
                }

                public function save($id, $allowance, $timestamp)
                {//保存上本次的限流详情
                    $this->store[$id . '-allowance'] = $allowance;//剩余次数
                    $this->store[$id . '-timestamp'] = $timestamp;//调用时间
                }
            }
        ]);

3. 使用

//在你想要限流的地方加入下面的代码
$this->rateLimiter->checkRateLimit($this->rateLimiter->user, Yii::$app->request, Yii::$app->response, $this->action);
发表在 php, yii2 | 标签为 , , | 留下评论

yii2 验证码

yii2的验证码很简单,使用yii2写接口的时候,它自带的验证码需要简单修改一下。
1. 在 根目录的library下创建一个Captcha.php(library是我自己创建的一个公共的包目录)

<?php
namespace app\library;

use \Yii;
use yii\captcha\CaptchaAction;
use yii\helpers\Url;
use yii\web\Response;

class Captcha extends CaptchaAction
{
    public function run()
    {
        //修改刷新方式,只要参数中包含刷新的key,则刷新验证码
        if (Yii::$app->request->getQueryParam(self::REFRESH_GET_VAR) !== null) {
            $code = $this->getVerifyCode(true);
        }else{
            $code = $this->getVerifyCode();
        }

        $this->setHttpHeaders();
        Yii::$app->response->format = Response::FORMAT_RAW;

        return $this->renderImage($code);
    }
}

2. 调用(比如在IndexController.php)中

    public function actionCaptcha()
    {
        $params = [
            'class' => 'app\library\Captcha',
            'fixedVerifyCode' => null,
            'backColor' => 0x000300,//背景颜色
            'maxLength' => 6, //最大显示个数
            'minLength' => 5,//最少显示个数
            'padding' => 6,//间距
            'height' => 40,//高度
            'width' => 130,  //宽度
            'foreColor' => 0xffffff,     //字体颜色
            'offset' => 5,        //设置字符偏移量 有效果
            'controller' => $this,
        ];
        echo (\Yii::createObject($params))->run();
    }

3. 验证方式

       //。。。
       $params = [
            'class' => 'app\library\Captcha',
            'testLimit' => 1,//超过这个错误次数,就刷新验证码
            'controller' => $this,
        ];

        $res = (\Yii::createObject($params))->validate($data['code'], true);//第一个参数是前端传过来的验证码,第二个参数是否验证大小写
        if(!$res) throw new \Exception("验证码错误");
       //。。。
发表在 php, yii2 | 标签为 , | 留下评论

es自定义时间字段索引

curl -X PUT -H ‘Content-Type: application/json’ “localhost:9200/indexName” -d ‘
{
“mappings”: {
“dynamic_date_formats”: [“yyyy-MM-dd HH:mm:ss”]
}
}’

https://www.elastic.co/guide/en/elasticsearch/reference/7.16/dynamic-field-mapping.html

发表在 es | 留下评论

elasticsearch 快照迁移

1. 启用es
并且配置:- path.repo=/usr/share/elasticsearch/backup

2. 创建快照仓库
curl -X PUT “localhost:9200/_snapshot/baolin?pretty” -H ‘Content-Type: application/json’ -d’
{
“type”: “fs”,
“settings”: {
“location”: “baolin”,
“compress”: true
}
}

3. 查看仓库状态
curl -X POST “localhost:9600/_snapshot/baolin/_verify?pretty”

4. 创建快照策略(每天一个快照),只能是UTC,不支持其他时间地区
curl -X PUT “localhost:9200/_slm/policy/baolin-day?pretty” -H ‘Content-Type: application/json’ -d’
{
“schedule”: “0 30 16 * * ?”,
“name”: ““,
“repository”: “baolin”,
“config”: {
“indices”: “baolin*”,
“include_global_state”: true
},
“retention”: {
“expire_after”: “30d”,
“min_count”: 5,
“max_count”: 50
}
}

保存30天,快照数量5 ~ 50个

5. 查看快照策略
curl -X GET “localhost:9200/_slm/policy/baolin-day?pretty”

# 查看所有的仓库
curl -H “Content-Type: application/json” -X GET http://localhost:9200/_snapshot/_all
# 查看某一个具体的仓库的快照情况
curl -H “Content-Type: application/json” -X GET ‘http://localhost:9200/_snapshot/baolin/_all?pretty‘
# 列出所有当前正在运行的快照以及显示他们的详细状态信息
GET /_snapshot/_status?pretty
# 列出所有当前正在运行的快照以及显示他们的详细状态信息
GET /_snapshot/baolin/_status?pretty
# 查看指定快照的详细状态信息即使不是正在运行
GET /_snapshot/baolin/snapshot_2/_status?pretty
#删除某一个快照
DELETE /_snapshot/baolin/snapshot_2

#手动备份快照,不实用slm的情况下
curl -H “Content-Type: application/json” -XPUT ‘http://localhost:9200/_snapshot/baolin/baolin_20220107?pretty&wait_for_completion=true’

6. 执行策略
curl -X POST “localhost:9200/_slm/policy/baolin-day/_execute?pretty”

7. 快照恢复(顺序不能错)

a. 启用新集群
b. 创建仓库
curl -X PUT “localhost:9200/_snapshot/baolin?pretty” -H ‘Content-Type: application/json’ -d’
{
“type”: “fs”,
“settings”: {
“location”: “baolin”,
“compress”: true
}
}

c. 把快照移动到当前集群仓库位置
cp -rf ../es/data/elasticsearch/backup/baolin/* data/elasticsearch/backup/baolin/

d. 在新集群中查看快照
curl -X GET “localhost:9600/_snapshot/baolin/_all?pretty” -H ‘Content-Type: application/json’

f. 恢复快照
curl -X POST “localhost:9600/_snapshot/baolin/[快照名字]/_restore?pretty” -H ‘Content-Type: application/json’ -d’
{
“indices”: “baolin*”
}

curl -X GET “localhost:9600/_snapshot/baolin/baolin-day-2022.01.07-o0zryaxtq1mzlvxeqnwm4q/_restore?pretty” -H ‘Content-Type: application/json’ -d’
{
“indices”: “baolin*”
}

发表在 es | 留下评论

Class ‘GuzzleHttp\Psr7\Utils’ not found

composer require guzzlehttp/psr7:^1.6.1 guzzlehttp/promises:^1.3.1

发表在 php, 框架 | 留下评论

go fmt.Printf 格式化占位符详解

%v 输出结构体 {10 30}

%+v 输出结构体显示字段名 {one:10 tow:30}

%#v 输出结构体源代码片段 main.Point{one:10, tow:30}

%T 输出值的类型 main.Point

%t 输出格式化布尔值 true

%d`输出标准的十进制格式化 100

%b`输出标准的二进制格式化 99 对应 1100011

%c`输出定整数的对应字符 99 对应 c

%x`输出十六进制编码 99 对应 63

%f`输出十进制格式化 99 对应 63

%e`输出科学技科学记数法表示形式 123400000.0 对应 1.234000e+08

%E`输出科学技科学记数法表示形式 123400000.0 对应 1.234000e+08

%s 进行基本的字符串输出 “\”string\”” 对应 “string”

%q 源代码中那样带有双引号的输出 “\”string\”” 对应 “\”string\””

%p 输出一个指针的值 &jgt 对应 0xc00004a090

% 后面使用数字来控制输出宽度 默认结果使用右对齐并且通过空格来填充空白部分

%2.2f 指定浮点型的输出宽度 1.2 对应 1.20

%*2.2f 指定浮点型的输出宽度对齐,使用 `-` 标志 1.2 对应 *1.20

发表在 go | 留下评论

抽奖算法之1

抽奖逻辑:有10个奖品,每个奖品的概率不一样,设计一种算法至少抽中其中的一个
算法讲解:10个奖品,每个奖品的概率是一个范围,把所有的范围都加到一起作为整体x,然后随机1 ~ x数字n,n落到哪个区间内,就中了哪个奖品

$奖品概率 = [10,35,50,....];
$x = array_sum($奖品概率);
$n = mt_rand();
foreach($奖品概率  as $k => $item){
   if($n < $item){
      return $k;
   }

   $n -= $item;
}
发表在 php, php函数集, php小程序 | 留下评论