swoole| swoole 协程用法笔记

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: swoole| swoole 协程用法笔记
swoole| swoole 协程用法笔记

协程方法一览

协程方法简明笔记:

  • Coroutine::set 协程设置

    • max_coroutine
  • Coroutine::stats 协程状态
  • Coroutine::list 遍历当前进程中的所有协程
  • Coroutine::getBackTrace 查看协程调用栈

    • 别名 Coroutine::listCoroutines()
  • Coroutine::create 创建协程

    • 短名 go()
    • 协程开销: 推荐使用 php>=7.2, 创建初始分配的栈内存更小, 并且会自动扩容
  • Coroutine::defer 类似 go 的 defer, PHP 有析构函数(__destruct())和自动回收机制, defer 的使用范围没那么大

    • 短名 defer()
  • Coroutine\Channel 类似 go 的 chan, 所有操作均为内存操作, 进程间内存隔离

    • 短名 chan()
    • 初始化是需要设置容量(capacity), 通道 空/满 会影响到后续的 push / pop
    • 必须在 swoole server 的 onWorkerStart 之后创建
  • Coroutine::getCid 当前协程id, 即 cid
  • Coroutine::exist
  • Coroutine::getPcid 获取父协程cid

    • 协程的嵌套会带来初始的先后顺序(父子关系), 最终执行还是要看协程的调度(没有稳定的父子关系)
  • Coroutine::getContext
  • Coroutine::yield 让出当前协程的执行权, 需要配合 resume 使用, 由其他协程唤醒

    • 别名 Coroutine::suspend
  • Coroutine::resume 唤醒其他协程
  • Coroutine::exec 协程版 shell_exec()
  • Coroutine::gethostbyname DNS查询, todo -> http client 是否需要
  • Coroutine::getaddrinfo
  • Coroutine::statvfs 获取文件系统信息(目前还不知道使用场景)
  • Coroutine\Client
  • Coroutine\Http\Client
  • Coroutine\Http2\Client
  • Coroutine\Socket
  • Coroutine\PostgreSQL

    • 安装 swoole 时, 需要加编译参数 --enable-coroutine-postgresql
    • 系统需要安装 libpg 库

开启协程 runtime 后(\Swoole\Runtime::enableCoroutine()), 可以不再使用:

  • Coroutine::fread
  • Coroutine::fgets
  • Coroutine::fwrite
  • Coroutine::sleep
  • Coroutine::readFile
  • Coroutine::writeFile
  • Coroutine\Redis 使用 ext-redis(phpredis) / predis
  • Coroutine\MySQL 使用 mysqlnd 模式的 pdo、mysqli 扩展

阻塞代码检验

swoole 中使用协程的 2 个要点:

  • 开协程: 这个容易, go() 一下就行了
  • 协程中执行 非阻塞代码: 除了看官方文档, 下面提供一个简单的检测 demo
go(function () {
    sleep(1); // 未开启协程 runtime, 此处会阻塞, 输出为 go -> main
    echo "go \n";
});
echo "main \n";

输出为: go -> main

\Swoole\Runtime::enableCoroutine();

go(function () {
    sleep(1); // 开启协程 runtime, 此处为阻塞, 输出为 main -> go
    echo "go \n";
});
echo "main \n";

输出为: main -> go, 发生了协程调度.

使用时将 sleep(1) 替换为需要检测的代码即可.

对短名称的个人看法

  • 建议关闭, 全部使用 \Swoole\Coroutine 命名空间保持一致性, 按需封装常用的几个, 比如 go() chan() defer()
  • ini 配置: swoole.use_shortname = 'Off'
if (! function_exists('go')) {
    function go(callable $callable)
    {
        \Hyperf\Utils\Coroutine::create($callable);
    }
}

if (! function_exists('defer')) {
    function defer(callable $callable): void
    {
        \Hyperf\Utils\Coroutine::defer($callable);
    }
}

其他

  • swoole 协程中比较重要的参数设置 max_coroutine, 更科学方式(看是否有需要): 压测后查看内存占用, 进而进行调整
  • wiki - 协程编程须知: 使用协程的注意事项
  • 协程 go+chan+defer: chan可以用在协程间交互, defer使用场景有待收集
  • 实现 defer: __destruct()
  • 实现 waitgroup: chan+count计数, 可以进一步封装成更方便的写法
  • 版本更新记录: 仔细看看, 就能体会到开发组在php 协程上的努力

并发调用

官方提供了 2 种方式:

  • setDefer 机制: 延迟收包, 多个请求并发收取相应结果; 确保协程组件支持 setDefer 机制(绝大部分协程组件都支持)
  • 子协程 + chan: 每个请求在子协程中执行, 通过 chan 实现请求收包时的协程调度

个人不建议使用, 并发调用可以达到更好的性能, 但是不符合常用的编程习惯, 多个请求需要同时完成, 推荐使用 waitGroup, 在此基础上, 还可以封装成更简单的写法

class WaitGroup
{
    /**
     * @var int
     */
    private $counter = 0;

    /**
     * @var SwooleChannel
     */
    private $channel;

    public function __construct()
    {
        $this->channel = new chan();
    }

    public function add(int $incr = 1): void
    {
        $this->counter += $incr;
    }

    public function done(): void
    {
        $this->channel->push(true);
    }

    public function wait(): void
    {
        while ($this->counter--) {
            $this->channel->pop();
        }
    }
}
目录
相关文章
|
3月前
|
Go 调度 Python
Golang协程和Python协程用法上的那些“不一样”
本文对比了 Python 和 Go 语言中协程的区别,重点分析了调度机制和执行方式的不同。Go 的协程(goroutine)由运行时自动调度,启动后立即执行;而 Python 协程需通过 await 显式调度,依赖事件循环。文中通过代码示例展示了两种协程的实际运行效果。
191 7
|
2月前
|
数据采集 网络协议 API
协程+连接池:高并发Python爬虫的底层优化逻辑
协程+连接池:高并发Python爬虫的底层优化逻辑
|
4月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
Go Python
使用python实现一个用户态协程
【6月更文挑战第28天】本文探讨了如何在Python中实现类似Golang中协程(goroutines)和通道(channels)的概念。文章最后提到了`wait_for`函数在处理超时和取消操作中的作
219 1
使用python实现一个用户态协程
|
调度 Python
python3 协程实战(python3经典编程案例)
该文章通过多个实战案例介绍了如何在Python3中使用协程来提高I/O密集型应用的性能,利用asyncio库以及async/await语法来编写高效的异步代码。
290 0
|
数据库 开发者 Python
实战指南:用Python协程与异步函数优化高性能Web应用
【7月更文挑战第15天】Python的协程与异步函数优化Web性能,通过非阻塞I/O提升并发处理能力。使用aiohttp库构建异步服务器,示例代码展示如何处理GET请求。异步处理减少资源消耗,提高响应速度和吞吐量,适用于高并发场景。掌握这项技术对提升Web应用性能至关重要。
271 10
|
数据处理 Python
深入探索:Python中的并发编程新纪元——协程与异步函数解析
【7月更文挑战第15天】Python 3.5+引入的协程和异步函数革新了并发编程。协程,轻量级线程,由程序控制切换,降低开销。异步函数是协程的高级形式,允许等待异步操作。通过`asyncio`库,如示例所示,能并发执行任务,提高I/O密集型任务效率,实现并发而非并行,优化CPU利用率。理解和掌握这些工具对于构建高效网络应用至关重要。
202 6
|
大数据 数据处理 API
性能飞跃:Python协程与异步函数在数据处理中的高效应用
【7月更文挑战第15天】在大数据时代,Python的协程和异步函数解决了同步编程的性能瓶颈问题。同步编程在处理I/O密集型任务时效率低下,而Python的`asyncio`库支持的异步编程利用协程实现并发,通过`async def`和`await`避免了不必要的等待,提升了CPU利用率。例如,从多个API获取数据,异步方式使用`aiohttp`并发请求,显著提高了效率。掌握异步编程对于高效处理大规模数据至关重要。
265 4