php| 201809 技术小结

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Redis 版,经济版 1GB 1个月
简介: 内容简介:- OpenSSL vs LibreSSL- swoole 4.1.0 添加 coroutine runtime 支持原生 redis/pdo/mysqli- php 实战 rabbitmq 任务队列: 多work + 协程- QPS 限制: 令牌桶算法 + php 实战

内容简介:

  • OpenSSL vs LibreSSL
  • swoole 4.1.0 添加 coroutine runtime 支持原生 redis/pdo/mysqli
  • php 实战 rabbitmq 任务队列: 多work + 协程
  • QPS 限制: 令牌桶算法 + php 实战

OpenSSL vs LibreSSL

上一篇 php| 初探 rabbitmq 中, 使用的 composer package 版的 php rabbitmq client. rabbitmq 还支持扩展板的 php rabbitmq client: ext-amqp. 抱着 扩展(ext, C语言编写) 比 包(package, php编写) 性能高 的朴实想法, 于是决定试试 ext-amqp 扩展. 然后就遇到了问题:

# 安装 amqp, 报错
pecl install amqp
checking for amqp using pkg-config... configure: error: librabbitmq not found

apk search rabbitmq # 查询相似依赖包
apk add rabbitmq-c-dev # 安装, 报错
ERROR: unsatisfiable constraints:
  openssl-dev-1.0.2o-r1:
    conflicts: libressl-dev-2.6.5-r0[pc:libcrypto=1.0.2o] libressl-dev-2.6.5-r0[pc:libssl=1.0.2o]
               libressl-dev-2.6.5-r0[pc:openssl=1.0.2o]
    satisfies: world[openssl-dev]
  libressl-dev-2.6.5-r0:
    conflicts: openssl-dev-1.0.2o-r1[pc:libcrypto=2.6.5] openssl-dev-1.0.2o-r1[pc:libssl=2.6.5]
               openssl-dev-1.0.2o-r1[pc:openssl=2.6.5]
    satisfies: rabbitmq-c-dev-0.8.0-r3[libressl-dev]

ok, 问题出来了: 系统中已经安装的 OpenSSL, 和 LibreSSL 不兼容

不兼容的原因, GitHub上有答案:

openssl-dev and libressl-dev provide many files that have the same path, therefore the two packages cannot be installed at the same time.

image

看来江湖要起一番争斗了: 扔掉 OpenSSL,拥抱 LibreSSL——远离心脏出血与溺亡

当然为此也争扎了一番, 说一下我的看法:

  • 之前一直使用的 OpenSSL(apk add openssl-dev), LibreSSL 是第一次接触
  • 发行版已经将原有的 OpenSSL 替换为 LibreSSL 了, Alpine Linux 自 3.5.0 起 -> 我喜欢使用 Alpine Linux
  • 打算切一下试试, 探一探江湖的水有多深

swoole 4.1.0 添加 coroutine runtime 支持原生 redis/pdo/mysqli 协程化

这个新特性非常令人激动, 很早就想试了, 不bb, 放测试结果.

不太熟悉协程的小伙伴, 可以移步 swoole| swoole 协程初体验

  • 4.1.0版本之前

要支持协程 redis, 需要 swoole 编译支持:

RUN curl -O https://gitee.com/swoole/swoole/repository/archive/v4.1.0.zip && unzip v4.1.0.zip && \
    apk add linux-headers openssl-dev nghttp2-dev hiredis-dev && \
    cd swoole && \
    phpize && \
    ./configure --enable-openssl --enable-async-redis --enable-http2 && make && make install && \
    docker-php-ext-enable swoole && \
    rm -rf v4.1.0.zip swoole

需要安装 hiredis, 并在编译参数中添加 --enable-async-redis

代码使用:

// 多协程版, 真正使用到协程带来的 IO 阻塞时的调度
for ($i = 0; $i < $cnt; $i++) {
    go(function () {
        $redis = new co\Redis();
        $redis->connect('redis', 6379);
        $redis->auth('123');
        $redis->get('key');
    });
}
  • 4.1.0 版本

编译:

RUN pecl install swoole && docker-php-ext-enable swoole

PS: 如果需要修改 swoole 编译参数开启其他功能, 还是需要采用上面的方式进行调整

测试代码:

$cnt = 2000;

// 普通版
for ($i=0; $i<$cnt; $i++) {
    test_redis();
}

// 协程版
Swoole\Runtime::enableCoroutine();
for ($i=0; $i<$cnt; $i++) {
    go(function () {
        test_redis();
    });
}

function test_redis() {
    $redis = new Redis();
    $redis->connect("redis", 6379);
    $redis->set('test', 'daydaygo');
    $r = $redis->get('test');
    // var_dump($r);
}

耗时对比:

time php origi_redis_co.php

# 普通版
real    0m 1.35s
user    0m 0.08s
sys    0m 0.41s

# 协程版
real    0m 0.74s
user    0m 0.10s
sys    0m 0.37s

只要开启 Swoole\Runtime::enableCoroutine(), 就可以轻松切换到协程

需要注意的点:

  • 目前只有原生 redis/pdo/mysqli 支持, 其他服务还是需要使用配套的协程 client, 比如 http, rabbitmq
  • Swoole\Runtime::enableCoroutine() 需要配合 go() 一起使用, 否则会报错 WARNING yield: Socket::yield() must be called in the coroutine

php 实战 rabbitmq 任务队列: 多work + 协程

任务队列是个老话题了, 使用 redis 自建或者手写一个都不叫事儿

  • 基于 swoole 的分布式多进程任务系统: kcloze/swoole-jobs
  • 基于 程序员的瑞士军刀 redis LIST 数据类型自建

自建的好处是灵活, 不好的地方就是要实现一些基础功能:

  • 监控: 有哪些队列, 堆积了有多少, 任务处理的速度
  • 重试
如果还在自建任务队列, 推荐试试 rabbitmq 先.

官方任务队列的 demo -> Tutorial two: Work Queues:

php new_task.php "A very hard task which takes two seconds.."
php worker.php

简单修改 worker.php 代码结构, 注意看注释:


<?php
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

$callback = function (AMQPMessage $msg) {
    // todo: 需要处理的任务

    // 处理完后进行消息确认
    $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
};

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('task_queue', false, true, false, false);
$channel->basic_qos(null, 1, null);
$channel->basic_consume('task_queue', '', false, false, false, false, $callback);

// 取一条进行测试
// if (count($channel->callbacks)) {
//     $channel->wait();
// }

// 循环直到处理完
while (count($channel->callbacks)) {
    $channel->wait();
}

$channel->close();
$connection->close();
  • 使用多进程加速

有了 Swoole\Process\Pool, 要实现多进程实在太轻松:

use Swoole\Process\Pool;

$pool = new Pool($workNum); // 需要开启的进程数
$pool->on('workerStart', function ($pool, $workerId) {
    // 上面的代码放进来即可
});
$pool->start();
  • 使用协程加速

swoole 也提供了 rabbitmq 的协程版 client, 项目地址 swoole/php-amqplib

使用 composer 引入:

{
    "name": "test",
    "description": "test",
    "minimum-stability": "dev", // 允许使用 dev 版的包
    "require": {
        "php-amqplib/php-amqplib": "dev-master" // 这里使用的 master 分支, 其他分支则改成 dev-branchname
    },
    "repositories": [
        {
            "type": "vcs", // 包的实际地址
            "url": "https://github.com/swoole/php-amqplib"
        }
    ]
}

对应的 worker 代码:

<?php

require "../vendor/autoload.php";

use PhpAmqpLib\Connection\AMQPSwooleConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Channel\AMQPChannel;

$callback = function (AMQPMessage $msg) {
    // todo: 消息处理
    var_dump($msg->body);

    // 处理完后确认消息
    /** @var AMQPChannel $ch */
    $ch = $msg->delivery_info['channel'];
    $ch->basic_ack($msg->delivery_info['delivery_tag']);
};

// 多协程调度
for ($i=0; $i<$coNum; $i++) {
    go(function () use ($callback) {
        $connection = new AMQPSwooleConnection('rabbitmq', 5672, 'guest', 'guest');
        $channel = $connection->channel();
        $channel->queue_declare('task_queue', false, true, false, false);
        $channel->basic_consume('task_queue', '', false, false, false, false, $callback);
    
        // 循环直到处理完
        while (count($channel->callbacks)) {
            $channel->wait();
        }
    });
}

协程版 AMQP 通过重写 PhpAmqpLib\Connection\AMQPSwooleConnection 实现, 底层使用 Swoole\Coroutine\Client

QPS 限制: 令牌桶算法 + php 实战

遇到外部接口的 QPS 限制, 感谢 dreamer_link 提供的方案

/**
 * 令牌桶实现限流
 * @link https://www.jianshu.com/p/9f76dd2757c7
 * @param string $key 设置key
 * @param int $initNum 周期内访问次数
 * @param int $expire 周期, 单位秒
 * @return bool
 */
public static function qpsLimit($key, $initNum, $expire)
{
    $time  = time();
    $redis = Yii::$app->redis->conn;
    $redis->watch($key);
    $limitVal = $redis->hGetAll($key);
    if ($limitVal) {
        $newNum   = min($initNum, ($limitVal['num'] - 1) + (($initNum / $expire) * ($time - $limitVal['time'])));
        if ($newNum > 0) {
            $redisVal = ['num' => $newNum, 'time' => time()];
        } else {
            // 当前时刻令牌消耗完
            return false;
        }
    } else {
        $redisVal = ['num' => $initNum, 'time' => time()];
    }
    $redis->multi();
    $redis->hMSet($key, $redisVal);
    if (!$redis->exec()) {
        // 访问频次过多
        return false;
    }
    return true;
}
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
2月前
|
SQL 缓存 PHP
PHP技术探究:优化数据库查询效率的实用方法
本文将深入探讨PHP中优化数据库查询效率的实用方法,包括索引优化、SQL语句优化以及缓存机制的应用。通过合理的优化策略和技巧,可以显著提升系统性能,提高用户体验,是PHP开发者不容忽视的重要议题。
|
2月前
|
安全 数据库连接 PHP
PHP编程中的关键性技术探究
在当今信息化社会,PHP作为一种流行的服务器端脚本语言,已经被广泛应用于网站开发和动态网页生成等领域。本文将深入探讨PHP编程中的关键性技术,包括数据库连接、安全性防护、性能优化等方面,旨在帮助读者更好地理解和运用PHP语言。
|
2月前
|
程序员 PHP
PHP程序员的成长之路:技术探索与实践
在当今数字化时代,PHP作为一种广泛应用的后端编程语言,对于程序员而言具有重要意义。本文从技术探索和实践的角度出发,探讨了PHP程序员在成长过程中所面临的挑战与机遇,以及如何通过持续学习与实践不断提升自身技能。
|
2月前
|
设计模式 程序员 PHP
PHP程序员的技术成长之路
技术成长是每个PHP程序员不断追求的目标,而这一过程并非只是关于学习新的语言特性或框架,更多的是关乎思维方式和解决问题的能力。本文将探讨PHP程序员在技术成长之路上所面临的挑战,并提出一些建议,帮助他们不断提升自己的技术水平。
25 5
|
5天前
|
设计模式 安全 关系型数据库
PHP开发涉及一系列步骤和技术
【7月更文挑战第2天】PHP开发涉及一系列步骤和技术
109 57
|
2月前
|
前端开发 关系型数据库 MySQL
使用PHP构建动态网站的技术指南
【5月更文挑战第27天】本文是使用PHP构建动态网站的指南,涵盖基本概念、技术栈选择(PHP、MySQL/MariaDB、HTML/CSS/JavaScript及Web服务器)、数据库交互示例、安全性考虑(SQL注入防护、输入验证、HTTPS使用、安全更新)和性能优化(缓存、查询优化、代码优化、输出压缩)。通过学习和实践,开发者能创建安全、高性能的动态网站。
|
22天前
|
算法 PHP 数据安全/隐私保护
PHP中的数据加密技术及应用
在Web开发中,数据安全始终是一个至关重要的问题。本文将介绍PHP中常用的数据加密技术,包括对称加密算法、非对称加密算法和哈希算法的原理和应用。通过深入了解这些加密技术,开发人员可以更好地保护用户数据和提高系统的安全性。
14 0
|
2月前
|
Cloud Native 持续交付 PHP
构建未来:云原生技术在企业数字化转型中的关键作用深入理解PHP中的命名空间
【5月更文挑战第31天】 随着企业加速其数字化转型的步伐,云计算已经成为支撑现代业务的一个不可或缺的基础设施。特别是云原生技术,以其独特的灵活性、可扩展性和敏捷性,正在改变我们构建和管理应用程序的方式。本文将深入探讨云原生技术的核心概念,包括容器化、微服务架构、持续集成与持续部署(CI/CD)、以及无服务器计算等,并讨论这些技术如何帮助企业实现更高效、更可靠的软件交付过程,从而推动业务增长和创新。
|
2月前
|
运维 Kubernetes PHP
构建高效自动化运维体系:基于容器技术的CI/CD实践深入理解PHP中的命名空间
【5月更文挑战第27天】在现代软件交付过程中,持续集成(CI)与持续部署(CD)已成为提升开发效率、保障产品质量的重要手段。本文旨在探讨如何利用容器技术实现CI/CD的自动化流程,从而构建一个高效的自动化运维体系。通过分析容器技术的核心优势和CI/CD流程的关键要素,我们提出了一种结合Docker、Kubernetes等工具的实践方案,并详细阐述了从代码提交到最终部署的全过程自动化实现方法。 【5月更文挑战第27天】在现代PHP开发中,命名空间是一个不可或缺的功能,它解决了代码库增长时可能出现的类名和函数名冲突问题。本文将深入探讨PHP命名空间的核心概念、实现原理及其在实际项目中的应用,帮助
|
2月前
|
存储 安全 算法
【PHP开发专栏】PHP加密与解密技术
【4月更文挑战第29天】本文探讨了PHP中的加密解密技术,涵盖基本概念如对称加密(AES、DES)、非对称加密(RSA、DSA)和哈希函数(MD5、SHA)。PHP提供内置函数支持加密,如`openssl_encrypt`、`openssl_pkey_new`、`hash`和`password_hash`。文章强调了最佳实践,如使用安全密钥、密钥管理和HTTPS,并给出用户注册登录的加密实战示例。通过理解和应用这些技术,开发者能增强Web应用的数据安全性。