× 警告!旧版文档已经暂停维护,请查看新版文档。点击前往新版文档

Coroutine

4.0版本开始Swoole提供了完整的协程(Coroutine)+ 通道(Channel)特性,带来全新的CSP编程模型。应用层可使用完全同步的编程方式,底层自动实现异步IO

go(function () {
    $redis = new Swoole\Coroutine\Redis();
    $redis->connect('127.0.0.1', 6379);
    $val = $redis->get('key');
});

4.0.0或更高版本仅支持PHP7
4.0.1版本开始去除了--enable-coroutine编译选项,改为动态配置

协程可以理解为纯用户态的线程,其通过协作而不是抢占来进行切换。相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低。Swoole可以为每一个请求创建对应的协程,根据IO的状态来合理的调度协程,这会带来了以下优势:

  1. 开发者可以无感知的用同步的代码编写方式达到异步IO的效果和性能,避免了传统异步回调所带来的离散的代码逻辑和陷入多层回调中导致代码无法维护
  2. 同时由于底层封装了协程,所以对比传统的PHP层协程框架,开发者不需要使用yield关键词来标识一个协程IO操作,所以不再需要对yield的语义进行深入理解以及对每一级的调用都修改为yield,这极大的提高了开发效率

可以满足大部分开发者的需求。对于私有协议,开发者可以使用协程的TCP或者UDP接口去方便的封装。

环境要求

  • PHP版本要求:>= 7.0
  • 基于ServerHttp\ServerWebSocket\Server进行开发,底层在onRequet, onReceive, onConnect等事件回调之前自动创建一个协程,在回调函数中使用协程API
  • 使用Coroutine::creatego方法创建协程,在创建的协程中使用协程API

相关配置

Serverset方法中增加了一个配置参数max_coroutine,用于配置一个Worker进程最多同时处理的协程数目。因为随着Worker进程处理的协程数目的增加,其占用的内存也会增加,为了避免超出php的memory_limit限制,请根据实际业务的压测结果设置该值,默认为3000

使用示例

$http = new swoole_http_server("127.0.0.1", 9501);

$http->on("request", function ($request, $response) {
    $client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP);
    $client->connect("127.0.0.1", 8888, 0.5);
    //调用connect将触发协程切换
    $client->send("hello world from swoole");
    //调用recv将触发协程切换
    $ret = $client->recv();
    $response->header("Content-Type", "text/plain");
    $response->end($ret);
    $client->close();
});

$http->start();

当代码执行到connect()和recv()函数时,底层会触发进行协程切换,此时可以去处理其他的事件或者接受新的请求。当此客户端连接成功或者后端服务回包后,底层会恢复协程上下文,代码逻辑继续从切换点开始恢复执行。开发者整个过程不需要关心整个切换过程。

注意事项

  1. 全局变量:协程使得原有的异步逻辑同步化,但是在协程的切换是隐式发生的,所以在协程切换的前后不能保证全局变量以及static变量的一致性。
  2. xdebugxhprofblackfirezend扩展不兼容,例如不能使用xhprof对协程server进行性能分析采样,需要使用官方出品的tracker工具。

协程组件

  1. TCP/UDP Client:Swoole\Coroutine\Client
  2. HTTP/WebSocket Client:Swoole\Coroutine\HTTP\Client
  3. HTTP2 Client:Swoole\Coroutine\HTTP2\Client
  4. Redis Client:Swoole\Coroutine\Redis
  5. Mysql Client:Swoole\Coroutine\MySQL
  6. PostgreSQL Client:Swoole\Coroutine\PostgreSQL
  • 在协程Server中需要使用协程版Client,可以实现全异步Server
  • 其他程序中可以使用go关键词手工创建协程
  • 同时Swoole提供了协程工具集:Swoole\Coroutine,提供了获取当前协程id,反射调用等能力。

  • wjbb1983

    我做了一个基于协程的Task队列组件,实现了协程场景下的所有TaskWorker功能,也实现了包括(异步模式、协程同步模式、Defer模式、Task集合模式)在内的多种运行模式。 可以通过设置队列长度、最大并发数等针对自己的项目优化。 我已经应用到我司的项目中,目前初步测试良好,大家可以试一下。 https://github.com/fdreamsu/SwArcher

  • 13480641615

    也就是说现在异步不建议使用异步回掉 而是使用Coroutine了

  • 13480641615

    也就是说现在异步不建议使用异步回掉 而是使用Coroutine了

  • 18516572616

    ERROR is_available (ERROR 10002): Socket#206 has already been bound to another coroutine#1, reading of the same socket in multiple coroutines at the same time is not allowed. 在协程里面使用单例模式报的错 , 静态调用也不行, 哪个大佬帮忙看看?

  • 13262732358

    go(function () use ($db) { $data=$db->query($sql); }); 主线程要$data要怎么写 go 能返回值吗 我主线程要用到这个结果集

  • 15923963135

    我需要在重新加载worker进程加载的文件。这个功能可以用协程监听文件,然后文件变动后,重新加载吗?

  • lyongde

    用户态,这个词咋理解

  • 18664893531

    如何在协成找中使用mysql、redis连接池

  • Y无声息

    关于协程,提个建议,建议增加协程优先级机制,避免开大量协程使用场景导致协程饿死,进而导致协程数量爆发式增长,然后内存爆炸。再增加一个可以控制协程数量的调度方式(目前需要开发者自己控制协程数量)。或者是我用的姿势不对。

  • 沈唁

    swoole4.4增加了抢占式调度,可以看https://segmentfault.com/a/1190000019253487

  • hhxsv5

    希望能支持Kafka协程客户端。

  • 18717776385

    用户态即非特权状态,在此状态下,执行的代码会被硬件限定,某些操作无法执行,比如写入其它进程的存储空间。

  • 19925932873

    请问,在协程代码在运行的过程中,做到代码的热更新?