小白兔晒黑了

导航

 

扩展视频https://www.bilibili.com/video/BV1oJ411U7bc/

1 第一集 进程(Process)、线程(thread)、协程(Coroutine)

视频地址  https://edu.51cto.com/center/course/lesson/index?id=412750

当今无数的 Web 服务和互联网服务,本质上大部分都是 IO 密集型服务,什么是 IO 密集型服务?意思是处理的任务大多是和网络连接或读写相关的高耗时任务,高耗时是相对 CPU 计算逻辑处理型任务来说,两者的处理时间差距不是一个数量级的。

IO 密集型服务的瓶颈不在 CPU 处理速度,而在于尽可能快速的完成高并发、多连接下的数据读写。

 

 

总结

  • 协程(Coroutine)是纯用户态的线程
  • 传统线程:cpu抢占式调度
  • 协程:由swoole调度 创建和切换消耗更低
  • 线程是cpu调度的最小单位
  • 线程之间可以共享资源

 

 

 

 

 

 

 

 

1.1 Master:

处理核心事件驱动(主进程)

1.2 Reactor:    

处理TCP连接,收发数据的线程,Swoole的主线程在Accept新的连接后,会将这个连接分配给一个固定的Reactor线程,并由这个线程负责监听此socket,在socket可读时读取数据,并进行协议解析,将请求投递到Worker进程,在数据可写时将数据发送给TCP客户端;
  a:负责维护客户端TCP连接,处理网络IO,处理协议,收发数据
  b:完全是异步非阻塞的模式
  c:全部为C代码,除了Start\Shudown事件回调外,不执行任何PHP代码
  d:将TCP客户端发来的数据缓存,拼接,拆分成完整的一个请求数据包
  e:Reactor以多线程的方式运行

1.3 Manager进程:   

       a:swoole中worker/task进程都是由Manager进程Fork并管理的;
  b:子进程结束运行时,manager进程负责回收此子进程,避免成为僵尸进程,并创建新的子进程;
  c:服务器关闭时,manager进程将发送信号给所有的子进程,通知子进程关闭服务
  d:服务器reload时,manager进程会逐个关闭/重启子进程

1.4 :Worker进程:

处理客户端的请求
  a:接收由Reactor线程投递的请求数据包,并执行PHP回调函数处理的数据
  b:生成响应数据并发给Reactor线程,由Reactor线程发送给TCP客户端;
  c:可以是异步非阻塞模式,也可以是同步阻塞模式
  d:Worker以多进程的方式运行;

1.5 Task进程:

异步工作进程
  a:接收由Worker进程通过swoole_server->task/taskwait方法投递的任务;
  b:处理任务,并将结果数据返回(swoole_server->finsish)给Worker进程;
  c:完全是同步阻塞模式
  d:TaskWorker以多进程的方式运行:

Reactor,Worker,TaskWorker的关系  

      a:可以理解为Reactor就是nginx,Worker就是php-fpm,Reactor线程异步并行的处理网络请求,然后再转发给Worker进程中去处理,Reactor和Worker间通过UnixSocket进行通信;
  b:在php-fpm的应用中,经常会将一个任务异步投递到Redis等队列中,并在后台启动一些php进程异步的去处理这些任务,Swoole提供的的Worker是一套更完整的方案,将任务的投递,队列,php任务处理进程管理合为一体,通过底层提供的API可以非常简单地实现异步任务的处理,另外
    TaskWorker还可以在任务执行完成后,再返回一个结果反馈到Worker.
  c:Swoole的Reactor,Worker,TaskWorker之间可以紧密的联系起来,提供更高级的使用方式;
  d:一个更通俗的的比喻,假设Server是一个工厂,那Reactor就是销售,接收客户订单,而Worker就是工厂,当销售接到订单后,Worker去工作生产出客户要的东西,而TaskWorker可以理解为行政人员,可以帮助Worker干些杂事,让Worker专心工作;
  e:底层会为Worker进程,TaskWorker进程分配一个唯一的ID;
  f:不同的Worker和Task进程之间可以通过sendMessage接口进行通信

1.6 Task进程

    a:Task和Worker进程的通信通过unix socket进行
  b:Task数据传递大小小于8K直接管道传递,大于8K写入临时文件传递
  c:Task传递对象
      (1):可以通过序列化传递一个对象的拷贝(非应用)
      (2):Task对象的改变不会反应到worker中(两个进程中是独立的)
      (3):数据库连接,网络连接对象不可传递
  d:Task的Onfinish的回调会传递给调用该task方法的worker进程(原路返回可寻)
  e:Task用途:模拟Mysql连接池

1.7 Timer定时器


a:Timer基于epoll的超时机制实现
  b:使用堆存放Timer,提高检索效率

2 开发环境的配置以及第一段协程代码

大家可以自行搭建,也可以使用我这里推荐的一个学习环境镜像
https://hub.docker.com/r/shenyisyn/swoole4.2.9/
基于php7.2 alpine镜像,swoole 4.2.9

https://www.cnblogs.com/pizixu/articles/12001717.html

2.1 go函数 创建协程

 创建协程 可以用  Coroutine::create或go (推荐后者,代码更简洁)

譬如有两个任务

1、从 1 输出到5 。每隔一秒输出

2、从 6输出到10,每隔一秒输出

常规写法::只能按顺序进行运行(相当于单进程 单线程里运行)

<?php
for ($i=1;$i<=5;$i++){
    echo $i.PHP_EOL;
    sleep(1);
}
for ($i=6;$i<=10;$i++){
    echo $i.PHP_EOL;
    sleep(1);
}
echo  '协程测试1';
View Code

协程方式: (可以交叉进行运行,一个协程执行一部分的时候进行协程的切换,协程的调度是由swoole来完成的,而不是CPU来抢占式的来调度)(协程怎么知道什么时候切换回来?:事件驱动https://www.cnblogs.com/staff/p/9709970.html)

<?php
go(function(){
    for ($i=1;$i<=5;$i++){
        echo $i.PHP_EOL;
        //相当于 PHP 的 sleep 函数,
        //不同的是 Coroutine::sleep 是协程调度器实现的,
        //底层会 yield 当前协程,让出时间片,并添加一个异步定时器,当超时时间到达时重新 resume 当前协程,恢复运行。
        Swoole\Coroutine::sleep(1);
    }
});

go(function (){
    for ($i=6;$i<=10;$i++){
        echo $i.PHP_EOL;
        Swoole\Coroutine::sleep(1);
    }
});

echo  '协程测试2'.PHP_EOL;

 

3 协程通信之channel入门

https://www.cnblogs.com/pizixu/articles/12002639.html

3.1 协程的通信

我们在实际场景进行相关业务开发的时候,很可能协程之间并不是独立存在的,而是有相关业务纠缠的,这里就需要用到了协程的通信;

3.1.1 单个协程

\seckill\swoolepro\class2.php

3.1,1.1 不用引用地址

<?php
//协程的通信
$count = 0;
go(function () use ($count){
   for ($i=1;$i<=3;$i++){
       $count ++;
       echo $count.PHP_EOL;
   }
});
echo '单个协程'.$count.PHP_EOL;
View Code

<?php
//协程的通信
$count = 0;
go(function () use (&$count){
   for ($i=1;$i<=3;$i++){
       $count ++;
       echo $count.PHP_EOL;
   }
});
echo '单个协程'.$count.PHP_EOL;
View Code

3.1.1.2 取地址就可以通信

一旦使用了引用就会重新开辟一个内存空间进行传址指向该引用的值;

3.1.2 两个协程

3.1.2.1 第一个协程使用引用,第二个不使用

<?php
//协程的通信
$count = 0;
//第一个协程使用了取地址
go(function () use (&$count){
   for ($i=1;$i<=3;$i++){
       $count ++;
       echo $count.PHP_EOL;
   }
});
//第2个协程不使用取地址
go(function () use ($count){
    echo '两个协程'.$count.PHP_EOL;
});

 可见这两个协程没有切换,而是第一个执行完。然后顺序执行了第二个

3.1.2.2 两个协程都用引用

<?php
//协程的通信
$count = 0;
// 两个协程都用引用
go(function () use (&$count){
   for ($i=1;$i<=3;$i++){
       $count ++;
       echo $count.PHP_EOL;
   }
});
// 两个协程都用引用
go(function () use (&$count){
    echo '两个协程'.$count.PHP_EOL;
});
//结果和上面一样
View Code

两个协程没有切换

3.1.2.3  两个协程都不使用引用

<?php
//协程的通信
$count = 0;
// 两个协程都不使用引用
go(function () use ($count){
   for ($i=1;$i<=3;$i++){
       $count ++;
       echo $count.PHP_EOL;
   }
});
// 两个协程都不使用引用
go(function () use ($count){
    echo '两个协程'.$count.PHP_EOL;
});
//也不进行协程切换。但第二个变量取主进程的变量值
//而不是第一个协程执行完以后的变量值
View Code

两个协程没有切换

3.1.2.4 第一个协程不使用,第二个使用的情况

<?php
//协程的通信
$count = 0;
// 第一个协程不使用
go(function () use ($count){
   for ($i=1;$i<=3;$i++){
       $count ++;
       echo $count.PHP_EOL;
   }
});
// 第二个使用引用
go(function () use (&$count){
    echo '两个协程'.$count.PHP_EOL;
});
//也不进行协程切换。但第二个变量取主进程的变量值
//而不是第一个协程执行完以后的变量值
View Code

 结果和上面一样 两个协程没有切换

3.1.3 两个协程的切换

3.1.3.1 第一个协程使用引用,第二个不使用

<?php
//协程的通信
$count = 0;
// 第一个协程使用引用
go(function () use (&$count){
   for ($i=1;$i<=3;$i++){
       $count ++;
       echo $count.PHP_EOL;
       //Swoole\Coroutine::sleep(1);
       //简写方法
       Co::sleep(1);
   }
});
// 第二个不使用
go(function () use ($count){
    echo '两个协程的切换'.$count.PHP_EOL;
});
View Code

 3.1.3.2 两个协程都用引用

<?php
//协程的通信
$count = 0;
// 两个协程都用引用
go(function () use (&$count){
   for ($i=1;$i<=3;$i++){
       $count ++;
       echo $count.PHP_EOL;
       //Swoole\Coroutine::sleep(1);
       //简写方法
       Co::sleep(1);
   }
});
// 两个协程都用引用
go(function () use (&$count){
    echo '两个协程的切换'.$count.PHP_EOL;
});
View Code

 结果和上面一样

3.1.3.3 两个协程都不使用引用

<?php
//协程的通信
$count = 0;
//  两个协程都不使用引用
go(function () use ($count){
   for ($i=1;$i<=3;$i++){
       $count ++;
       echo $count.PHP_EOL;
       //Swoole\Coroutine::sleep(1);
       //简写方法
       Co::sleep(1);
   }
});
// 两个协程都不使用引用
go(function () use ($count){
    echo '两个协程的切换'.$count.PHP_EOL;
});
View Code

3.1.3.4 第一个协程不使用,第二个使用的情况

<?php
//协程的通信
$count = 0;
//  第一个协程不使用
go(function () use ($count){
   for ($i=1;$i<=3;$i++){
       $count ++;
       echo $count.PHP_EOL;
       //Swoole\Coroutine::sleep(1);
       //简写方法
       Co::sleep(1);
   }
});
// ,第二个使用的情况
go(function () use (&$count){
    echo '两个协程的切换'.$count.PHP_EOL;
});
View Code

3.2 使用Coroutine\Channel (通道)进行协程的通讯

3.2.1 swoole中协程之间通讯的编程范式

  • 协程内部禁止使用全局变量和静态变量
  • 协程使用use关键字引用外部变量到当前作用域的时候禁止使用引用
  • 协程之间的通讯必须使用Channel
  • 与xdebug、xhprof、blackfire等zend扩展不兼容,例如不能使用xhprof对协程server进行性能分析采样

3.2.2 使用通道

<?php
//使用Coroutine\Channel形成协程的通讯
use Swoole\Coroutine as co;
//创建一个容量为1的通道
$chan = new co\Channel('1');

//  第一个协程 只负责计算(cpu)
go(function () use ($chan){
    $count = 0;
   for ($i=1;$i<=3;$i++){
       $count ++;
       echo $count.PHP_EOL;
       //Swoole\Coroutine::sleep(1);
       //简写方法
       Co::sleep(1); //每当遇到sleep时会经行协程的切换
   }
   $chan->push($count);
   echo '协程1已经发送数据到协程2'.PHP_EOL;
});
// 第二个协程 只负责输出 (IO)
go(function () use ($chan){
    echo '协程2输出数据:'.$chan->pop().PHP_EOL;
});

 

 如果 没有 $chan->push($count);这行

程序会卡在pop()

4 协程的切换和协程的挂起

https://www.cnblogs.com/pizixu/articles/12002811.html

4.1 协程的切换

<?php
//使用 sleep模拟IO阻塞,因此会引起协程的切换(进入调度队列)
//原理如下
//https://wiki.swoole.com/wiki/page/784.html
go(function (){
    for ($i=1;$i<=10;$i++){
        echo $i.PHP_EOL;
        Co::sleep(0.5);
    }
});

go(function (){
    for ($i=1;$i<=1000;$i++){
        echo 'a'.$i.PHP_EOL;
    }
});
View Code

 

。。。。

 

第一个协程输出1后,直接切换到第二个协程,直到第二个协程在0.5秒内执行完毕,再切换到第一个协程

4.2 协程的挂起yield与恢复resume

<?php
use Swoole\Coroutine as co;
//  $cid 协程的id
$cid = go(function (){
    for ($i=1;$i<=10;$i++){
        if ($i==5){
            //让出当前协程的执行权。 而不是基于 IO 的协程调度
            //此方法拥有另外一个别名:Coroutine::suspend()
            //必须与Coroutine::resume()方法成对使用。该协程yield以后,必须由其他外部协程resume,否则将会造成协程泄漏,被挂起的协程永远不会执行。
            co::yield();
        }
        echo $i.PHP_EOL;
    }
});

go(function () use ($cid){
    for ($i=1;$i<=1000;$i++){
        echo 'b'.$i.PHP_EOL;
    }
    //手动恢复某个协程,使其继续运行,不是基于 IO 的协程调度
    co::resume($cid);
});

。。。。

5 协程方式请求网页:curl、stream_socket和swoole协程客户端 对比 关于Runtime

https://www.cnblogs.com/pizixu/articles/12011479.html

5.1 curl或file_get_content等

<?php
//cURL
//不再会自动兼容协程内外环境,一旦开启,则一切阻塞操作必须在协程内调用 (
\Swoole\Runtime::enableCoroutine(true);
function getHtml($t=false)
{
    if ($t){
        sleep(3);
    }
    $url = 'https://www.showapi.com/apiGateway/generateSignUrl?showapi_apiCode=1196';
    $ch = curl_init() ;
    curl_setopt($ch,CURLOPT_URL,$url);  //需要获取的URL地址
    curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);//如果成功只将结果返回,不自动输出任何内容。
    curl_setopt($ch,CURLOPT_HEADER,0);    //如果你想把一个头包含在输出中,设置这个选项为一个非零值。
    $ret = curl_exec($ch);
    curl_close($ch);
    return $ret;
}
go(function (){
    echo getHtml('aaa').PHP_EOL;
    echo "协程1".PHP_EOL;
});


go(function (){
    echo getHtml().PHP_EOL;
    echo "协程2".PHP_EOL;
});

 让原来的同步 IO 的代码变成可以协程调度异步 IO,即一键协程化

 

如果不加\Swoole\Runtime::enableCoroutine(true); 则会变

5.2 stream_socket

用于流媒体

https://blog.csdn.net/m0_68949064/article/details/124568417

https://wiki.swoole.com/#/runtime?id=%e5%b8%b8%e8%a7%81%e7%9a%84hook%e5%88%97%e8%a1%a8

 

 

5.3 Coroutine\Http\Client

协程 HTTP/WebSocket 客户端

<?php
use Swoole\Coroutine\Http\Client as httpClient;
//https://wiki.swoole.com/#/coroutine_client/client
go(function (){
    $client = new httpClient('www.douban.com','80') ;
    $client->get('/404');
    echo $client->body.PHP_EOL;
    echo '以上协程1豆瓣'.PHP_EOL;
    $client->close();
});


go(function (){
    $client = new httpClient('www.baidu.com','80') ;
    $client->get("/404");
    echo $client->body.PHP_EOL;
    echo '以上协程2百度'.PHP_EOL;
    $client->close();
});

 

 

 可见百度比豆瓣服务器更快

 

6 Mysql 协程

https://www.cnblogs.com/pizixu/articles/12015989.html

6.1 方式1 开启一键协程(不推荐)

<?php
//开启一键协程化
\Swoole\Runtime::enableCoroutine(true);
$dsn = "mysql:host=10.10.10.114;dbname=test";

go(function() use($dsn){
    $pdo = new PDO($dsn,'ttt','123456');
    $stat = $pdo->query("select sleep(3); select * from users  where  id=1;");

    $stat->setFetchMode(PDO::FETCH_ASSOC);
    $stat->nextRowset();  //获得下一个结果集 https://www.imooc.com/video/3016
    $rows = $stat->fetchAll();
    var_dump($rows);
    $pdo = null;
});

go(function() use($dsn){
    $pdo = new PDO($dsn,'ttt','123456');
    $stat = $pdo->query("select * from users  where  id=2;");
    $stat->setFetchMode(PDO::FETCH_ASSOC);
    $rows = $stat->fetchAll();
    var_dump($rows);
    $pdo = null;
});

 

 如果不开启一键协程

 

 6.2 方式2 使用mysql协程客户端(推荐)

<?php
//Mysql协程客户端
use Swoole\Coroutine as co;
go(function (){
    $mysql = new co\MySQL();
    $mysql->connect([
        'host'=>'10.10.10.114',
        'user'=>'ttt',
        'password'=>'123456',
        'database'=>'test'
    ]);
    $row1 = $mysql->query("select sleep(3);");
    $row2 = $mysql->query("select * from users where id=1");
    var_dump($row2);
});

go(function (){
    $mysql = new co\MySQL();
    $mysql->connect([
        'host'=>'10.10.10.114',
        'user'=>'ttt',
        'password'=>'123456',
        'database'=>'test'
    ]);
    $row2 = $mysql->query("select * from users where id=2");
    var_dump($row2);
});

7 第7集 使用Channel多协程运行顺序控制的基本方法

https://edu.51cto.com/center/course/lesson/index?id=412744

https://wiki.swoole.com/#/coroutine?id=%e4%bb%80%e4%b9%88%e6%98%afchannel

seckill\swoolepro\class7.php

7.1 未使用channel

<?php

use Swoole\Coroutine as co;
function query(array $sqls){
    $mysql = new co\MySQL();
    $config = array(
        'host'=>'10.10.10.114',
        'user'=>'ttt',
        'password'=>'123456',
        'database'=>'test'
      );
    
    
    $dsn = "mysql:host=10.10.10.114;dbname=test";
    $pdo = new \PDO($dsn,'ttt','123456');
    
    $conn = $mysql->connect($config);
    
    foreach ($sqls as $sql){
        $statement = $mysql->prepare($sql);
        $rows = $statement->execute();
        var_dump($rows);
    }
    echo PHP_EOL;
}

go(function (){
    query(["select sleep(2);","select * from users where id=1",]);
});

go(function (){
    query(["select * from users where id=2"]);
});

echo 'done'.PHP_EOL;
View Code

 

 

 执行顺序 1 done

2 id=2

3 卡2秒

4 id=1

 7.2 使用channel后

<?php

use Swoole\Coroutine as co;
function query(array $sqls){
    $mysql = new co\MySQL();
    $config = array(
        'host'=>'10.10.10.114',
        'user'=>'ttt',
        'password'=>'123456',
        'database'=>'test'
      );
    $conn = $mysql->connect($config);
    
    foreach ($sqls as $sql){
        $statement = $mysql->prepare($sql);
        $rows = $statement->execute();
        var_dump($rows);
    }
    echo PHP_EOL;
}

go(function (){
    //https://wiki.swoole.com/wiki/page/p-channel.html
    //Channel可用于多进程环境下,底层在读取写入时会自动加锁,应用层不需要担心数据同步问题
    $chan = new co\Channel(2);//2代表两个协程
    go(function () use ($chan){
        query(["select sleep(3);","select * from users where id=1",]);
        $chan->push(1);
    });
    
    go(function () use ($chan){
        query(["select * from users where id=2"]);
        $chan->push(2);
    });
    for ($i=0;$i<2;$i++){
        $chan->pop();
    }
    echo 'done'.PHP_EOL;
    
});

 

done到最后来了

只要没有push进来,pop就会阻塞

 

8 模拟go语言的waitGroup控制协程的运行顺序

https://www.cnblogs.com/pizixu/articles/12027825.html

8.1 创建waitGroup

8.1.1 首先创建一个文件夹

app   ---sync
   --WaitGroup.php

8.1.2 终端执行 composer init

自动生成composer.json文件

8.1.3  在生成的composer.json中加入如下代码

 "autoload": {
        "psr-4": {
            "App\\": "app/"
        }
    }

 

 8.1.4:执行composer dump-autoload

8.1.5 将生成的vendor目录手动上传到服务器

8.1.6 WaitGroup中代码

<?php
namespace App\sync;
use Swoole\Coroutine\Channel;
//channel控制协程的运行顺序
class WaitGroup
{
    private $chan;  //通道
    private $count; //协程数量
    function __construct()
    {
        $this->chan = new Channel(1); //初始化容量为1 可以通过Done修改
        $this->count = 0;
    }
    //设置协程的数量
    public function Add(int $coroutineCount)
    {
        $this->count+=$coroutineCount;
    }
    public function Done()
    {
        //写入一个数据
        $this->chan->push(1); //随便加一个数
    }
    //Add-> Done...-> Wait的顺序
    public function Wait()
    {
        for ($i=0;$i<$this->count;$i++){
            $this->chan->pop();
        }
    }
}

8.1.7 class8.php中的代码

<?php
require_once "vendor/autoload.php";
use App\sync\WaitGroup;
use Swoole\Coroutine as co;
function query(array $sqls){
    $mysql=new co\MySQL();
    $conn=$mysql->connect([
            'host'=>'10.10.10.114',
            'user'=>'ttt',
            'password'=>'123456',
            'database'=>'test'
        ]);
    foreach($sqls as $sql){

        $statement=$mysql->prepare($sql);
        if ($statement == false)
        {
            var_dump($mysql->errno);
            var_dump($mysql->error);
        }
        $rows=$statement->execute();
        foreach($rows as $row){
            foreach($row as $k=>$v){
                echo $k."=>".$v.";";
            }
        }
    }
    echo PHP_EOL;
}

go(function(){
    $wg=new WaitGroup();
    $wg->Add(2);//设置协程的数量
    //协程1
    go(function() use($wg){
        query(["select sleep(3)","select * from users where id=1"]);
        $wg->Done();
    });
    //协程2
    go(function() use($wg){
        query(["select * from users where id=2"]);
        $wg->Done();
    });
    $wg->Wait();
    //协程3
    go(function (){
        echo "我是被阻塞的协程3".PHP_EOL;
    });
    echo "done".PHP_EOL;
});

 

 

9 引入第三方日志库monolog与go语言风格的defer使用

9.1 引入一个第三方日志库:monolog

为了方便演示 今天的功能,我们引入一个第三方日志库:monolog

链接网址:https://github.com/Seldaek/monolog

这是一个目前在PHP中比较流行的日志库,可以非常方便把日志写入到控制台,文件,redis,elasticsearch等

composer require monolog/monolog

如果不行 就切换到阿里云镜像 composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

然后把vendor目录手动上传

9.2 go语言风格的defer使用

Go语言的 defer 语句会将其后面跟随的语句进行延迟处理,在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行,也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行。

<?php

require_once "vendor/autoload.php";

use App\sync\WaitGroup;
use Swoole\Coroutine as co;


use Monolog\Logger;
use Monolog\Handler\StreamHandler;
//use Monolog\Handler\FirePHPHandler;

function query(array $sqls)
{
    $mysql = new co\MySQL();
    $conn = $mysql->connect([
        'host'=>'10.10.10.114',
        'user'=>'ttt',
        'password'=>'123456',
        'database'=>'test'
        ]);
    foreach ($sqls as $sql) {

        $statement = $mysql->prepare($sql);
        if ($statement == false) {
            var_dump($mysql->errno, $mysql->error);
        }
        $rows = $statement->execute();
        foreach ($rows as $row) {
            foreach ($row as $k => $v) {
                echo $k . "=>" . $v . ";";
            }
        }
    }
    echo PHP_EOL;
}

go(function () {
    $wg = new WaitGroup();
    $wg->Add(2);//设置协程的数量
    $logger=new Logger("mylog");
    //https://www.cnblogs.com/i6010/articles/13100810.html
    //StreamHandler:把记录写进PHP流,主要用于日志文件。
    //INFO (200): 关键事件。
    $logger->pushHandler(new StreamHandler(__DIR__.'/my_info.log', Logger::INFO));
    //$logger->pushHandler(new FirePHPHandler());  //可以设置多个多种方式的Handler

    //WARNING (300): 出现非错误的异常。
    $logger->pushHandler(new StreamHandler(__DIR__.'/my_error.log', Logger::ERROR));
    //var_dump($logger);

    go(function () use ($wg,$logger) {
        query(["select sleep(3)", "select * from users where id=1"]);
        //先被 defer 的语句最后被执行
        defer(function ()use ($wg,$logger)
        {
            $logger->info("延迟执行1(先被 defer 的语句最后被执行)",["uid"=>1,"sleep"=>3]);
            $wg->Done();
        });

        defer(function ()use($logger){

            $logger->info("延迟执行3");
            echo   "延迟执行3".PHP_EOL;
        });
        $logger->error("未拦截异常");
        $logger->notice("未拦截异常");
        $logger->warning("未拦截异常");
        throw new Exception("未拦截异常");



    });
    go(function () use ($wg,$logger) {
        query(["select * from users where id=2"]);
        $logger->info("延迟执行2:没有用defer,第一个被记录",["uid"=>2,"sleep"=>0]);
        $wg->Done();
    });
    $wg->Wait();
    echo "done" . PHP_EOL;
});

 

 

$logger->error("未拦截异常");
$logger->notice("未拦截异常");
$logger->warning("未拦截异常");

 

 

10 第10集 使用协程实现一个简单问答(判断题)

https://edu.51cto.com/center/course/lesson/index?id=412741

<?php
//使用协程实现一个简单的问答(判断题)
use Swoole\Coroutine as co;
$ques = [
    'PHP是不是最好的语言?'=>1,
    '996恶心吗?'=>1    ,
    '加班是一种福气吗?'=>0
];
/*foreach ($ques as $que=>$ans){
    echo $que.PHP_EOL;
    $get = fgets(STDIN);
    if ($get == $ans){
        echo '正确'.PHP_EOL;
    }else{
        echo '错误'.PHP_EOL;
    }
}*/

$chan = new co\Channel();
go(function () use ($ques,$chan){
    $ques['end']=1;
    foreach ($ques as $que=>$ans){
        $chan->push($que);
    }
});

go(function () use ($ques,$chan){
    while (true){
        $getQue = $chan->pop();
        if ($getQue=='end'){
            break;
        }
        echo $getQue.PHP_EOL;
        $getAns = fgets(STDIN);
        if($getAns==$ques[$getQue]){
            echo '正确'.PHP_EOL;
        }else{
            echo '错误'.PHP_EOL;
        }
    }
});
View Code

11 使用协程实现一个简单问答(答完再计算成绩)

自己写的 不一定对

<?php
//使用协程实现一个简单的问答(判断题)
use Swoole\Coroutine as co;
require_once "vendor/autoload.php";
$ques = [
    'PHP是不是最好的语言?'=>1,
    '996恶心吗?'=>1    ,
    '加班是一种福气吗?'=>0
];
/*foreach ($ques as $que=>$ans){
    echo $que.PHP_EOL;
    $get = fgets(STDIN);
    if ($get == $ans){
        echo '正确'.PHP_EOL;
    }else{
        echo '错误'.PHP_EOL;
    }
}*/

$chan = new co\Channel();
go(function () use ($ques,$chan){
    $ques['end']=1;
    foreach ($ques as $que=>$ans){
        $chan->push($que);
    }

});

$count = 0;
go(function () use ($ques,$chan,$count){
    while (true){
        $getQue = $chan->pop();
        if ($getQue=='end'){
            break;
        }
        echo $getQue.PHP_EOL;
        $getAns = fgets(STDIN);
        if($getAns==$ques[$getQue]){
            //echo '正确'.PHP_EOL;
            $count ++;
        }else{
            //echo '错误'.PHP_EOL;
        }
    }
    defer(function ()use($count){
        echo '得分'.$count.PHP_EOL;
    });
});
View Code

12 数据库连接池

https://www.cnblogs.com/polax/p/16738612.html

posted on 2022-09-29 20:19  小白兔晒黑了  阅读(726)  评论(0编辑  收藏  举报