function createDeamon($gid, $uid) {
// 只允许在cli下面运行
if (php_sapi_name() != "cli") {
die("only run in command line mode\n");
}
set_time_limit(0);
if (pcntl_fork() != 0) { //是父进程,父进程退出
exit();
}
\posix_setsid(); //设置新会话组长,脱离终端
chdir("/"); //改变工作目录
umask(0); //把文件掩码清0
if (pcntl_fork() != 0) { //第二次fock子进程
exit();
}
\posix_setuid($uid);
\posix_setgid($gid);
//关闭打开的文件描述符
fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
//重定向标准输出和错误输出,个人觉得不需要这样,因为常住内存,所以一直占着io链接总感觉不太好,不如自定义msg方法
/*
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 创建守护进程
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');
(保证新密码完全生效)
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()
}
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);
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("验证码错误");
//。。。
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
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*"
}
'
Class ‘GuzzleHttp\Psr7\Utils’ not found
composer require guzzlehttp/psr7:^1.6.1 guzzlehttp/promises:^1.3.1
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