文章851
标签121
分类10

/tmp 下产生大量 swoole.task 临时文件 磁盘空间打满问题

今天一台测试服务器 /tmp 下产生大量 swoole.task 临时文件 大概几十万个 将磁盘打满了 大概时间也就7天以内 , 这些文件什么鬼 ?? 是worker to task 的队列长度堵塞 生成的文件吗?

在测试服务器上, 有很多 swoole 服务程序, swoole.task 临时文件仍在增长, 所以我就依次 kill 服务. 终于找到问题服务在哪里了
这个服务的 task进程 子进程 manager进程 都已经死掉了.

====================== 活着的进程 ================================
root      4651 11357  0 17:31 pts/0    00:00:00 Knock-swoole-main-master     
root      4741     1  0 17:31 pts/0    00:00:07 Knock-swoole-worker-0    

====================== 死掉的进程 ================================
root      4652  4651  0 17:31 pts/0    00:00:00 Knock-swoole-main-manager     
root      4694     1  0 17:31 pts/0    00:00:00 Knock-sws-swoole-fork-process 
root      4740     1  4 17:31 pts/0    00:01:50 Knock-swoole-task-0               

总结了下原因如下 :

swoole 起得多进程脚本 worker 投递任务给 task 进程
但 不知什么原因 manager 进程 死掉了 task 进程过期后没有重新拉起
worker 投递的数据超过8180字节 产生了临时文件 (文档)
而且因为投递的任务未执行导致 worker 仍会重复投递 直至磁盘打满

当我想复现下问题, 但因为投递数据不超过 8180字节 所以在kill掉 上述进程后并没有临时文件产生.

MySQL 乐观锁版本控制解决高并发问题

乐观锁是一种常用的并发控制机制,适用于读操作远多于写操作的场景。它通过版本控制来避免多个事务对同一数据的冲突,从而有效解决高并发问题。以下是实现乐观锁的基本概念、使用方法以及示例。

1. 乐观锁的概念

乐观锁的核心思想是:假设在操作过程中不会发生冲突,因此在更新数据时不加锁,而是在更新时检查数据是否被其他事务修改。常见的实现方式是通过版本号或时间戳来实现。

2. 实现乐观锁的步骤

2.1 数据表设计

在数据表中添加一个版本字段(version),用于跟踪数据的修改情况。例如:

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    age INT,
    version INT DEFAULT 0
);

2.2 读取数据

在读取数据时,获取当前的版本号:

SELECT id, name, age, version FROM users WHERE id = 1;

2.3 更新数据

在更新数据时,使用 WHERE 子句检查版本号是否与读取时相同。只有当版本号匹配时,更新才会成功:

UPDATE users
SET name = 'New Name', age = 30, version = version + 1
WHERE id = 1 AND version = 0;

如果更新的行数为 0,说明版本号不匹配,表示数据已被其他事务修改。

3. 处理更新结果

在执行更新后,检查受影响的行数:

$affectedRows = $db->exec($updateQuery);
if ($affectedRows === 0) {
    // 处理冲突:数据已被其他事务修改
    echo "Update failed: data has been modified by another transaction.";
} else {
    echo "Update successful.";
}

4. 示例代码

以下是一个完整的 PHP 示例,演示如何使用乐观锁进行更新操作:

<?php
// 假设已经建立数据库连接 $db

// 获取当前用户信息
$userId = 1;
$stmt = $db->prepare("SELECT id, name, age, version FROM users WHERE id = ?");
$stmt->execute([$userId]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);

// 检查用户是否存在
if ($user) {
    // 假设我们想更新用户的姓名和年龄
    $newName = "New Name";
    $newAge = 30;
    $currentVersion = $user['version'];

    // 执行更新
    $updateStmt = $db->prepare("UPDATE users SET name = ?, age = ?, version = version + 1 WHERE id = ? AND version = ?");
    $updateResult = $updateStmt->execute([$newName, $newAge, $userId, $currentVersion]);

    if ($updateResult && $updateStmt->rowCount() > 0) {
        echo "Update successful.";
    } else {
        echo "Update failed: data has been modified by another transaction.";
    }
} else {
    echo "User not found.";
}
?>

5. 优缺点

5.1 优点

  • 减少锁竞争:适合读多写少的场景,减少了锁的开销。
  • 提高性能:在低冲突的情况下,可以显著提高并发性能。

5.2 缺点

  • 冲突处理:在高冲突的情况下,乐观锁可能导致频繁的重试。
  • 实现复杂性:需要开发者自行管理版本号和冲突处理逻辑。

Client Error #418: I'm a teapot!

HTTP 状态码 418 是一个非常有趣且不常见的状态码,属于 RFC 2324,即“茶壶协议”(Hyper Text Coffee Pot Control Protocol, HTCP)。这个状态码的定义源于一个愚人节玩笑,并不被广泛使用。以下是关于这个错误的详细介绍。

1. 状态码背景

1.1 起源

  • 愚人节玩笑:418 状态码最初是由 RFC 2324 提出的,这是一个于 1998 年 4 月 1 日发布的幽默性文档。文档描述了一种控制咖啡壶的协议,规定了 418 状态码的含义。

1.2 418 的含义

状态码 418 表示:

"I'm a teapot"(我是一只茶壶)

当客户端请求一个咖啡壶进行冲泡时,如果请求的内容是咖啡,茶壶会返回这个错误。

2. 使用场景

虽然 418 状态码并非标准的 HTTP 状态码,但它有时被用作:

  • 幽默的响应:某些开发者在他们的 API 或 Web 应用中使用 418 响应,以增加趣味性或进行调试。
  • 错误处理:在某些情况下,开发者可能会使用它来表示请求中的某种“错误”。

3. 示例代码

如果您想在自己的应用中返回 418 响应,可以使用以下示例代码:

使用 PHP

<?php
http_response_code(418);
echo "I'm a teapot!";
?>

使用 Python Flask

from flask import Flask

app = Flask(__name__)

@app.route('/teapot')
def teapot():
    return "I'm a teapot!", 418

if __name__ == '__main__':
    app.run()

4. 结论

HTTP 状态码 418 是一个幽默的状态码,源于对茶壶的调侃。在实际开发中,它通常不被用于正式的应用程序,但可以作为一种轻松的方式来处理特定的请求或错误。

收藏 PHP 计算器

从网上找的韩顺平老师算法讲解的 使用堆栈 实现计算器

<?php
/**
 * Created by PhpStorm.
 * User: Len
 * Date: 2019/4/20
 * Time: 18:35
 * Desc: 使用堆栈实现一个计算器 仅支持 加减乘除
 */

namespace Library;


use Helper\Common;

class StackCalculateor
{
    // 默认是-1,表示该栈是空的
    public $top = -1;

    // $maxSize表示栈最大容量
    public $maxSize = 15;

    public $stack = array();

    /**
     * @des 运算
     * @param $str
     * @return bool|float|int
     * ------------------------------------------------------------
     */
    public static function run($str)
    {
        $numsStack = new self();
        $operStack = new self();

        // 字符串的指针
        $index = 0;

        // 声明一个用于组合联系数字的变量
        $keepNum = '';

        if (!mb_strlen($str)) return false;

        while (true) {
            $val = mb_substr($str, $index, 1);

            //如果是一个符号就入符号栈 否则入数栈
            if ($operStack->isOper($val) == true) {
                //符号入栈前需要判断一下 栈为空直接入栈 不为空需要比较当前运算符与栈顶端的运算符
                //如果当前运算符的优先级低于栈内的 则需要运算
                if ($operStack->isEmpty()) {
                    $operStack->push($val);
                } else {
                    while (!$operStack->isEmpty() && $operStack->PRI($val) <= $operStack->PRI($operStack->getTop())) {
                        //当前符号的优先级要直到高于栈内的时候才能入栈 否则要计算
                        //当前运算符的优先级低于栈内的 则运算
                        $num1 = $numsStack->pop();
                        $num2 = $numsStack->pop();
                        $oper = $operStack->pop();
                        $res = $numsStack->getResult($num1, $num2, $oper);
                        //计算完毕将结果入栈
                        $numsStack->push($res);
                    }
                    //把当前这个符号再入符号栈
                    $operStack->push($val);
                }

            } else {
                //考虑如果是连续数字的问题
                $keepNum .= $val;
                //先判断是否已经到字符串最后.如果已经到最后,就直接入栈.
                if ($index == mb_strlen($str) - 1) {
                    $numsStack->push($keepNum);//是数字直接入栈
                } else {
                    //要判断一下$ch字符的下一个字符是数字还是符号.
                    if ($operStack->isOper(mb_substr($str, $index + 1, 1))) {
                        $numsStack->push($keepNum);
                        $keepNum = '';
                    }
                }
            }

            $index++;//让$index指向下一个字符.

            if ($index == mb_strlen($str)) break;//已扫描到字符串的末尾 就退出while循环
        }

        /*
         4. 当扫描完毕后,就依次弹出数栈和符号栈的数据,并计算,最终留在数栈的值,就是运算结果,只有符号栈不空就一直计算
        */
        while (!$operStack->isEmpty()) {
            $num1 = $numsStack->pop();
            $num2 = $numsStack->pop();
            $oper = $operStack->pop();
            $res = $numsStack->getResult($num1, $num2, $oper);
            //计算完毕将结果入栈
            $numsStack->push($res);
        }

        return $res;
    }

    /**
     * @des 判断是否是一个运算符
     * @param $val
     * @return bool
     * ------------------------------------------------------------
     */
    public function isOper($val)
    {
        if ($val == '+' || $val == '-' || $val == '*' || $val == '/') return true;
    }

    /**
     * @des 判断栈是否为空
     * @return bool
     * ------------------------------------------------------------
     */
    public function isEmpty()
    {
        if ($this->top == -1) return true;
    }

    /**
     * @des 入栈
     * @param $val
     * ------------------------------------------------------------
     */
    public function push($val)
    {
        //先判断栈是否已经满了
        if ($this->top == $this->maxSize - 1) return;

        $this->top++;

        $this->stack[$this->top] = $val;
    }

    /**
     * 比较运算符的优先级
     * 我把 * 和/运算符的优先级看作1
     * +和- 看作0
     * 通过它们之间的比较就能得出它们的优先级谁更高
     * @param $oper
     * @return int
     * ------------------------------------------------------------
     */
    public function pri($oper)
    {
        if ($oper == '*' || $oper == '/')
            return 1;
        else if ($oper == '+' || $oper == '-')
            return 0;
    }

    /**
     * @des 返回栈顶端的值
     * @return mixed
     * ------------------------------------------------------------
     */
    public function getTop()
    {
        return $this->stack[$this->top];
    }

    /**
     * @des 出栈
     * @return null|number
     * ------------------------------------------------------------
     */
    public function pop()
    {
        //判断是否栈空
        if ($this->top == -1)
            return null;

        //把栈顶的值,取出
        $topVal = $this->stack[$this->top];

        $this->top--;

        return $topVal;

    }

    /**
     * @des 运算
     * @param $num1
     * @param $num2
     * @param $oper
     * @return float|int
     * ------------------------------------------------------------
     */
    public function getResult($num1, $num2, $oper)
    {
        switch ($oper) {
            case '+':
                $res = Common::sbcadd($num2, $num1);
                break;
            case '-':
                $res = Common::sbcsub($num2, $num1);
                break;
            case '*':
                $res = Common::sbcmul($num2, $num1);
                break;
            case '/':
                $res = Common::sbcdiv($num2, $num1);
                break;
        }

        return $res;
    }

    /**
     * 显示栈的所有数据的方法
     * ------------------------------------------------------------
     */
    public function showStack()
    {
        if ($this->top == -1) return;

        echo '当前栈的情况是....', PHP_EOL;

        for ($i = $this->top; $i > -1; $i--) {
            echo ' stack[' . $i . ']=' . $this->stack[$i], PHP_EOL;
        }
    }

}

使用 PHP cURL 通过代理访问网站

在 PHP 中,使用 cURL 发送 HTTP 请求时,可以通过代理服务器来访问网站。这对于绕过网络限制或匿名访问非常有用。以下是如何在 PHP 中使用 cURL 设置代理的详细步骤。

1. 安装 cURL

首先,确保您的 PHP 环境中已安装 cURL 扩展。可以通过以下方式检查:

<?php
phpinfo();
?>

在输出中查找 cURL 部分。如果未安装,请根据您的操作系统进行安装。

在 Ubuntu 上

sudo apt-get install php-curl

在 Windows 上

确保在 php.ini 文件中取消下面行的注释:

extension=php_curl.dll

2. 使用 cURL 设置代理

以下是通过代理访问网站的基本示例代码:

<?php
// 初始化 cURL
$ch = curl_init();

// 设置请求的 URL
curl_setopt($ch, CURLOPT_URL, "http://www.example.com");

// 设置代理服务器地址和端口
curl_setopt($ch, CURLOPT_PROXY, "http://your.proxy.server:port");

// 如果代理需要身份验证,设置用户名和密码
curl_setopt($ch, CURLOPT_PROXYUSERPWD, "username:password");

// 返回结果而不是输出到标准输出
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// 执行 cURL 请求
$response = curl_exec($ch);

// 检查是否发生错误
if (curl_errno($ch)) {
    echo 'Curl error: ' . curl_error($ch);
}

// 关闭 cURL 句柄
curl_close($ch);

// 输出响应
echo $response;
?>

3. 详细说明

3.1 设置代理

  • CURLOPT_PROXY: 设置代理服务器的地址和端口。可以是 HTTP、HTTPS 或 SOCKS 代理。例如:

    curl_setopt($ch, CURLOPT_PROXY, "http://127.0.0.1:8080");

3.2 代理身份验证

如果代理服务器需要身份验证,可以使用 CURLOPT_PROXYUSERPWD 设置用户名和密码。

curl_setopt($ch, CURLOPT_PROXYUSERPWD, "username:password");

3.3 处理响应

  • CURLOPT_RETURNTRANSFER: 设置为 true,以便将请求的响应作为字符串返回,而不是直接输出。

3.4 错误处理

使用 curl_errnocurl_error 函数检查并处理可能的错误。

4. 示例:访问 HTTPS 网站

如果您需要访问 HTTPS 网站,确保您的代理支持 HTTPS,并在设置代理时使用 https

curl_setopt($ch, CURLOPT_PROXY, "https://your.proxy.server:port");
">