百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

swoole学习五协程初探 swoole多协程

lipiwang 2024-11-01 14:10 8 浏览 0 评论

  • 协程的基本概念

借用大佬的总结就是用户端的线程,也就是用户程序创建、切换、销毁的不经过CPU调度的线程。

目前原生支持协程的语言应该只有Go吧,swoole作为PHP的扩展也实现了协程。

有几个特点:

  1. CPU是不知道协程的存在,所以CPU操作的还是进程
  2. 协程存在于用户态,所以没有用户态与内核态的切换
  3. 协程需要用户程序自己实现切换调度
  4. 协程切换的时机是程序显示切换(yield->resume)或者遇到I/O阻塞的时候
  5. 协程的上下文需要程序自己去处理

很幸运的是,swoole已经为我们处理好了这一切。

我们学习的版本是Swoole版本:4.5.2

  • 来个Demo理解下协程
<?php


use Swoole\Coroutine;
use Swoole\Runtime;

// 这里创建三个协程,分别去执行
// 当sleep产生阻塞时,当前协程就会挂起,执行下一个

// 开启协程支持  sleep函数如果不开启是不会产生
// IO阻塞的情况
Swoole\Runtime::enableCoroutine();

// 创建一个协程
Swoole\Coroutine::create(function () {
    // 模拟I/O阻塞了
    sleep(2);
    echo 1 . ':' . PHP_EOL;
});
Swoole\Coroutine::create(function () {
    sleep(1);
    echo 2 . ':' . PHP_EOL;
});
Swoole\Coroutine::create(function () {
    sleep(1);
    echo 3 . ':' . PHP_EOL;
});

执行发现并不是顺序执行的,第一个等待两秒,但是程序直接执行了第二个,这就是协程带来的变化,非阻塞

gaoz@nobodyMBP hyperf-skeleton % php swoole_demo/coroutine/coroutine.php 
2:
3:
1:

再来看普通的阻塞执行

<?php

// 这三个会依次执行
// 阻塞等待

// 执行闭包函数
(function () {
    // 阻塞2s
    sleep(2);
    echo "1:" . PHP_EOL;
})();
(function () {
    sleep(1);
    echo "2:" . PHP_EOL;
})();
(function () {
    sleep(1);
    echo "3:" . PHP_EOL;
})();

执行发现是顺序执行,也就是阻塞执行的,我们目前php-fpm模式下就是这样执行的

gaoz@nobodyMBP hyperf-skeleton % php swoole_demo/coroutine/not_coroutine.php 
1:
2:
3:

我们换成数据库查询在看对比下了

<?php

\Swoole\Runtime::enableCoroutine(true);

// 协程话之后,swoole自动调度协程的执行

$start =  microtime_float();
// 创建一个协程 执行这个查询
\Swoole\Coroutine::create(function(){
    print_r(selectDB([10000,10001,10002]));
});
echo "finished---3". PHP_EOL;

// 创建第二个协程 执行这个查询
\Swoole\Coroutine::create(function(){
    print_r(selectDB([10003,10004,10005]));
});
echo "finished---5". PHP_EOL;

echo PHP_EOL;

echo '---1---';

$end = microtime_float();

echo "use-time: ". ($end-$start);

function selectDB($ids)
{
    $mysqlconn = new mysqli('127.0.0.1', 'root', 'root', 'test');
    $result = $mysqlconn->query("
    select id, name from heros where id in (".implode(',', $ids).")
");

    $arr = [];
    foreach ($result->fetch_all(MYSQLI_ASSOC) as $k=>$info) {
        $arr[] = $info;
    }

    return $arr;

}


function microtime_float()
{
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
}

异步执行,结果顺序不确定奥

gaoz@nobodyMBP hyperf-skeleton % php swoole_demo/coroutine/coroutine_mysql.php    
finished---3
finished---5

---1---use-time: 0.00034785270690918Array
(
    [0] => Array
        (
            [id] => 10003
            [name] => 牛魔
        )

    [1] => Array
        (
            [id] => 10004
            [name] => 吕布
        )

    [2] => Array
        (
            [id] => 10005
            [name] => 亚瑟
        )

)
Array
(
    [0] => Array
        (
            [id] => 10000
            [name] => 夏侯惇
        )

    [1] => Array
        (
            [id] => 10001
            [name] => 钟无艳
        )

    [2] => Array
        (
            [id] => 10002
            [name] => 张飞
        )

)
<?php

//阻塞模式  一个一个的执行

$start =  microtime_float();
print_r(selectDB([10000,10001,10002]));
echo "finished---3". PHP_EOL;
print_r(selectDB([10003,10004,10005]));
echo "finished---5". PHP_EOL;

echo PHP_EOL;

echo '---1---';

$end =  microtime_float();
echo "use-time: ". ($end-$start);

function selectDB($ids)
{
    $mysqlconn = new mysqli('127.0.0.1', 'root', 'root', 'test');
    $result = $mysqlconn->query("
    select id, name from heros where id in (".implode(',', $ids).")
");

    $arr = [];
    foreach ($result->fetch_all(MYSQLI_ASSOC) as $k=>$info) {
        $arr[] = $info;
    }

    return $arr;

}

function microtime_float()
{
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
}

阻塞顺序执行,一目了然

gaoz@nobodyMBP hyperf-skeleton % php swoole_demo/coroutine/not_coroutine_mysql.php
Array
(
    [0] => Array
        (
            [id] => 10000
            [name] => 夏侯惇
        )

    [1] => Array
        (
            [id] => 10001
            [name] => 钟无艳
        )

    [2] => Array
        (
            [id] => 10002
            [name] => 张飞
        )

)
finished---3
Array
(
    [0] => Array
        (
            [id] => 10003
            [name] => 牛魔
        )

    [1] => Array
        (
            [id] => 10004
            [name] => 吕布
        )

    [2] => Array
        (
            [id] => 10005
            [name] => 亚瑟
        )

)
finished---5

---1---use-time: 0.034359931945801

由上面的例子可以看到,协程对于应对I/O密集的操作是很有效果的,我们在平时的工作中一般遇到的恰恰是I/O密集型的,如数据库读取、网络连接等等,CPU密集型的php一般也搞不定的,那我们在遇到这样的问题时可以考虑使用swoole来应对。

回到Hyperf, swoole为每个请求都会创建一个协程处理,如HttpServer 的onRequest事件发生之后,swoole会自动创建一个协程处理,[可以通过enable_coroutine: false 关闭],而当我们使用 Swoole\Runtime::enableCoroutine(true); 启动协程hook之后,大部分php原有的产生io的函数都会转换成协程版本。所以一般来说这样就不会再有阻塞的发生了。

  • 自建协程

除了swoole在某些时刻的自动创建的协程,我们还可以手动创建,如下面这样

Swoole\Coroutine::create(function () {
    // xxxx
});
  • 协程容器

调度器是提供协程容器的,所有协程api都必须要在协程容器中执行,swoole默认创建协程容器

// 创建调度器
$scheduler = new Swoole\Coroutine\Scheduler();

// 添加一个协程任务
$scheduler->add(function($num){
    echo "start-one". PHP_EOL;
    \Swoole\Coroutine::sleep(1);

    echo $num. PHP_EOL;

}, 11);

// 添加第二个协程任务
$scheduler->add(function($num){
    echo "start-two". PHP_EOL;

    echo $num. PHP_EOL;
}, 12);


// 启动调度器开始执行
$scheduler->start();
  • 协程间通讯

协程之间是隔离的,一个协程的变量不能被另一个读取,协程间通信需要通过channel完成,channel可以理解为一个消息队列,协程A把数据push

到channel,协程B从channel中读取数据

<?php

use Swoole\Coroutine;

// 创建通道   配置容量1,每次只能有一条消息,消费完才能push,
// 当通道满了之后,生产者协程就要阻塞等待了
$channel = new Swoole\Coroutine\Channel(1);
Swoole\Coroutine::create(function()use($channel){
    for ($i=0; $i<3; $i++) {
        // 往通道中push数据
        $channel->push($i);
    }
});


Swoole\Coroutine::create(function()use($channel){
    for ($i=0; $i<3; $i++) {
        echo "current-chan-status";
        print_r($channel->stats());

        Coroutine::sleep(1);
        // 从通道获取数据
        $data = $channel->pop();
        echo "get-from-chan: ". $data. PHP_EOL;
    }
});


总结:

swoole本身的异步多进程再加上协程对于提高php应对高并发的业务是完全没有问题的。我们需要在实际工作中多多应用。


相关文档:

swoole关于协程的讲解:https://wiki.swoole.com/#/coroutine

hyperf关于协程的讲解:https://hyperf.wiki/2.0/#/zh-cn/coroutine

本文代码:https://github.com/nobody05/hyperf_study


相关推荐

ubuntu单机安装open-falcon极度详细操作

备注:以下操作均由本人实际操作并得到验证,喜欢的同学可尝试操作安装。步骤一1.1环境准备(使用系统:ubuntu18.04)1.1.1安装redisubuntu下安装(参考借鉴:https://...

Linux搭建promtail、loki、grafana轻量日志监控系统

一:简介日志监控告警系统,较为主流的是ELK(Elasticsearch、Logstash和Kibana核心套件构成),虽然优点是功能丰富,允许复杂的操作。但是,这些方案往往规模复杂,资源占用高,...

一文搞懂,WAF阻止恶意攻击的8种方法

WAF(Web应用程序防火墙)是应用程序和互联网流量之间的第一道防线,它监视和过滤Internet流量以阻止不良流量和恶意请求,WAF是确保Web服务的可用性和完整性的重要安全解决方案。它...

14配置appvolume(ios14.6配置文件)

使用AppVolumes应用程序功能,您可以管理应用程序的整个生命周期,包括打包、更新和停用应用程序。您还可以自定义应用程序分配,以向最终用户提供应用程序的特定版本14.1安装appvolume...

目前流行的缺陷管理工具(缺陷管理方式存在的优缺点)

摘自:https://blog.csdn.net/jasonteststudy/article/details/7090127?utm_medium=distribute.pc_relevant.no...

开源数字货币交易所开发学习笔记(2)——SpringCloud

前言码云(Gitee)上开源数字货币交易所源码CoinExchange的整体架构用了SpringCloud,对于经验丰富的Java程序员来说,可能很简单,但是对于我这种入门级程序员,还是有学习的必要的...

开发JAX-RPC Web Services for WebSphere(下)

在开发JAX-RPCWebServicesforWebSphere(上)一文中,小编为大家介绍了如何创建一个Web服务项目、如何创建一个服务类和Web服务,以及部署项目等内容。接下来小编将为大...

CXF学习笔记1(cxf client)

webservice是发布服务的简单并实用的一种技术了,个人学习了CXF这个框架,也比较简单,发布了一些笔记,希望对笔友收藏并有些作用哦1.什么是webServicewebService让一个程序可...

分布式RPC最全详解(图文全面总结)

分布式通信RPC是非常重要的分布式系统组件,大厂经常考察的Dubbo等RPC框架,下面我就全面来详解分布式通信RPC@mikechen本篇已收于mikechen原创超30万字《阿里架构师进阶专题合集》...

Oracle WebLogic远程命令执行0day漏洞(CVE-2019-2725补丁绕过)预警

概述近日,奇安信天眼与安服团队通过数据监控发现,野外出现OracleWebLogic远程命令执行漏洞最新利用代码,此攻击利用绕过了厂商今年4月底所发布的最新安全补丁(CVE-2019-2725)。由...

Spring IoC Container 原理解析(spring中ioc三种实现原理)

IoC、DI基础概念关于IoC和DI大家都不陌生,我们直接上martinfowler的原文,里面已经有DI的例子和spring的使用示例《InversionofControlContainer...

Arthas线上服务器问题排查(arthas部署)

1Arthas(阿尔萨斯)能为你做什么?这个类从哪个jar包加载的?为什么会报各种类相关的Exception?我改的代码为什么没有执行到?难道是我没commit?分支搞错了?遇到问题无法在...

工具篇之IDEA功能插件HTTP_CLENT(idea2021插件)

工具描述:Java开发人员通用的开发者工具IDEA集成了HTTPClient功能,之后可以无需单独安装使用PostMan用来模拟http请求。创建方式:1)简易模式Tools->HTTPCl...

RPC、Web Service等几种远程监控通信方式对比

几种远程监控通信方式的介绍一.RPCRPC使用C/S方式,采用http协议,发送请求到服务器,等待服务器返回结果。这个请求包括一个参数集和一个文本集,通常形成“classname.meth...

《github精选系列》——SpringBoot 全家桶

1简单总结1SpringBoot全家桶简介2项目简介3子项目列表4环境5运行6后续计划7问题反馈gitee地址:https://gitee.com/yidao620/springbo...

取消回复欢迎 发表评论: