ホームページ > バックエンド開発 > PHPチュートリアル > PHP 収量の高度な使用法を学ぶ

PHP 収量の高度な使用法を学ぶ

coldplay.xixi
リリース: 2023-04-09 06:16:01
転載
5343 人が閲覧しました

PHP 収量の高度な使用法を学ぶ

冒頭章

PHPyield に初めて触れたとき、私はこう思いました。フェルト、yield それはどのようなブラック テクノロジですか? Baidu: yield——コルーチン、ジェネレーター。多くの記事で IteratorGenerater について取り上げていますが、これは PHP イテレータの補足です。さらに数ページめくると、Go coroutine が見つかります。好奇心から、Go Coroutine をクリックして調べてみました。これには、ConcurrencyThreadPipeline Communication などの単語が含まれていました。注意: これはブラック テクノロジです。戻って PHP を確認すると、毎分 Go に切り替えたいと考えています。

#関連する学習の推奨事項:

PHP プログラミングの入門から習熟まで

PHP に参加するための生成構文

yield構文はバージョン 5.5 の PHP に追加されました。イテレータとともに使用されます。関数は プロセス制御コード、および goto,return 同様です。

以下は公式が提供する yield の小さな例です。実行結果から、コードが

yield $i に実行されると、return が返されることが分析できます。 $i, echo "$value\n" の後、goto for ($i = 1; $i $i が安全に送信されました。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">&lt;?phpfunction gen_one_to_three() {     for ($i = 1; $i &lt;= 7; $i++) {         //注意变量$i的值在不同的yield之间是保持传递的。         yield $i;     }}$generator = gen_one_to_three();foreach ($generator as $value) {     echo &quot;$value\n&quot;;}// output12...67</pre><div class="contentsignin">ログイン後にコピー</div></div>

どのような問題が発生しましたかコードを書くことは問題を解決することです。彼らが遭遇した問題を見てみましょう: PHP関係者に関しては、簡潔かつ簡潔な方法でyieldを全員に紹介する必要があります。一部のネットユーザーは、限られたリソース内で大規模なファイル操作を完了する必要があります。そして私たちの鳥の兄弟。現在の初級レベルの Yield チュートリアルに不満を抱いている PHPer のグループに直面し、タスク スケジューラを例として

yield

の高度な使用法を説明しました。 php.net: ジェネレーター構文、

PHP が大きなファイルを読み取る方法、

風雪コー​​ナー: コルーチンを使用して PHP でマルチタスク スケジューリングを実装する。

提案された質問、上記の答えを見ると、この PHP コルーチンはそれ以上のものではないと思います (

Go コルーチン

と比較)。 格言があります - 良い質問は答えよりも重要です 現在、大多数のネチズンは、利益を得るために、より適切で難しい質問をしていません。

yieldこの入出力構文の多くの例は、イテレータとして yield を使用するか、非常に大きなテキストを読み取るために低メモリを使用する例です。Excel

csv など、どんなに高度なものであっても、単純なタスクスケジューラーを実装するために使用され、このスケジューラーのコードは一見すると似ています。 質問させてください

良い質問と同じように、答えよりも価値があります

PHP を使用して、リクエストを受信して​​サーバーの時間を返すことができるソケット サーバーを実装します。

さて、これが最初の質問です。伏線です。公式回答
  1. 元のコードに、ソケットサーバーがリクエストを処理する際に他のソケットサーバーに依存し、クライアント機能も必要になるという要件を追加します。つまり、リクエストを受信したり、他のサーバーへのリクエストを開始したりできます。

これは 2 番目の質問であり、伏線でもあります。
  1. 元のソケット サーバーは同時に 1 人の顧客にのみサービスを提供できます。
ノンブロッキング I/O

ソケット サーバーを実現したいと考えています。このサーバーはソケット クライアント機能を備えています。

Concurrency
    をサポートし、受信したリクエストとアクティブに開始されたリクエストを処理します。マルチスレッドやマルチプロセスを使用する必要はありません。
  1. #この質問はまだ伏線です。これらの質問は非常にドライです。よく考えてください。質問 2 と 3 の答えはすべてスクリプトにあります: nio_server.php上記のコードでは、ユーザーがショッピング カートのアドオン アクションを要求するという特定のビジネスを列挙しましたが、ショッピング カート サービスに関しては、実現可能性を検証するために商品サービス、在庫サービス、優待サービスと対話する必要があります。アドオン アクションの。リクエストと比較には同期メソッドと非同期メソッドがあります。
従うべきコードがたくさんあるので、gitee リンクを貼っておきます。使用法については、readme.mdを参照してください。

最後の質問: PHP でコードを同期的に作成すると、プログラムは非同期で実行されますか?コードの調整方法。

ヒント: これは、
PHP
  1. yield
  2. 構文に関連しています。

もう一つのリマインダー: yield 文法上の特徴は何ですか、インとアウト!

コードを見ると、同期、非同期、出たり入ったり 何を思いましたか? 同期処理モードでコードを見ると、これら 3 つの関数

checkInventory

checkProduct

checkPromo

がリクエストを開始し、戻りを待ちます。その結果、これら 3 つの関数が実行された後、クライアントの要求が応答されます。 <p>异步处理模式下,这三个函数发起请求完毕后,代码就跳出循环了,然后是在<code>select()下的一个代码分支中接收请求, 并收集结果。每次收到结果后判断是否完成,完成则响应客户端。

那么能不能这样:在异步处理的流程中,当 Server收到 自己发起的 client 有数据响应后,代码跳到 nio_server.php 的 247行呢,这样我们的收到请求校验相关的代码就能放到这里,编码能就是同步,容易理解。不然,client 的响应处理放在 280 行以后,不通过抓包,真的很难理解,执行了第 247 行代码后,紧接着是从 280 行开始的。

诶~这里是不是有 进进出出 那种感觉了~ 代码从 247 行出去,开始监听发出 Client 响应,收到返回数据,带着数据再回到 247 行,继续进行逻辑校验,综合结果后,再响应给客户端。

用yield来解决问题

基于 yield 实现的,同步编码,"异步"I/OSocket Server 就实现了。代码。

这里 “异步” 打了引号,大佬别扣这个字眼了。 该是非阻塞I/O

不等大家的答案了,先上我的结果代码吧,代码呢都放在这个目录下了。

gitee https://gitee.com/xupaul/PHP-generator-yield-Demo/tree/master/yield-socket

运行测试代码

clone 代码到本地后,需要拉起4个 command 命令程序:

拉起3个第三方服务

## 启动一个处理耗时2s的库存服务$ php ./other_server.php 8081 inventory 2## 启动一个处理耗时4s的产品服务$ php ./other_server.php 8082 product 4## 监听8083端口,处理一个请求 耗时6s的 promo 服务$ php ./other_server.php 8083 promo 6
ログイン後にコピー

启动购物车服务

## 启动一个非阻塞购物车服务$ php ./async_cart_server.php 

## 或者启动一个一般购物车服务$ php ./cart_server.php
ログイン後にコピー

发起用户请求

$ php ./user_client.php
ログイン後にコピー

运行结果呢如下,通过执行的时间日志,可得这三个请求是并发发起的,不是阻塞通讯。

在看我们的代码,三个函数,发起socket请求,没有设置callback,而是通过yield from 接收了三个socket的返回结果。

也就是达到了,同步编码,异步执行的效果。

运行结果

非阻塞模式

client 端日志:

通过以上 起始时间结束时间 ,就看到这三个请求耗时总共就6s,也就按照耗时最长的promo服务的耗时来的。也就是说三个第三方请求都是并发进行的。

cart server 端日志:

而 cart 打印的日志,可以看到三个请求一并发起,并一起等待结果返回。达到非阻塞并发请求的效果。

阻塞模式

client 端日志:

以上是阻塞方式请求,可以看到耗时 12s。也就是三个服务加起来的耗时。

cart server 端日志:

cart 服务,依次阻塞方式请求第三方服务,顺序执行完毕后,共耗时12s,当然如果第一个,获第二个服务报错的话,会提前结束这个检查。会节约一点时间。

工作原理

这里就是用到了 yield 的工作特点——进进出出,在发起非阻塞socket请求后,不是阻塞方式等待socket响应,而是使用yield跳出当前执行生成器,等待有socket响应后,在调用生成器的send方法回到发起socket请求的函数内,在 yield from Async::all() 接收数据响应数据搜集完毕后,返回。

和Golang比一比

考虑到网速原因,我这就放上一个国内教程链接:Go 并发 教程

php的协程是真协程,而Go是披着协程外衣的轻量化线程(“协程”里,都玩上“锁”了,这就是线程)。

我个人偏爱,协程的,觉得线程的调度有一定随机性,因此需要锁机制来保证程序的正确,带来了额外开销。协程的调度(换入换出)交给了用户,保证了一段代码执行连续性(当然进程级上,还是会有换入换出的,除非是跨进程的资源访问,或者跨机器的资源访问,这时,就要用到分布式锁了,这里不展开讨论),同步编码,异步执行,只需要考虑那个哪个方法会有IO交互会协程跳出即可。

和NodeJS比划一下

Javascript 和 PHP 两个脚本语言有很多相似的地方,弱类型,动态对象,单线程,在Web领域生态丰富。不同的是,Javascript在浏览器端一开始就是异步的(如果js发起网络请求只能同步进行,那么你的网页渲染线程会卡住),例如AjaxsetTimeoutsetInterval,这些都是异步+回调的方式工作。

基于V8引擎而诞生的NodeJS,天生就是异步的,在提供高性能网络服务有很大的优势,不过它的IO编码范式么。。。刚开始是 回调——毁掉地狱,后来有了Promise——屏幕竖起来看,以及Generator——遇事不绝yield一下吧,到现在的Async/Await——语法糖?真香!

可以说JS的委员非常勤快,在异步编程范式的标准制定也做的很好(以前我尝试写NodeJS时,几个回调就直接把我劝退了),2009年诞生的NodeJS有点后来居上的意思。目前PHP只是赶上了协程,期待PHP的Async/Await语法糖的实现吧。

PHP yield 使用注意事项

一旦使用上 yield 后,就必须注意调用函数是,会得到函数结果,还是 生成器对象。PHP 不会自动帮你区别,需要你手动代码判断结果类型—— if ($re instanceof \Generator) {}, 如果你得到的是 生成器,但不希望去手动调用 current() 去执行它,那么在生成器前 使用 yield from 交给上游(框架)来解决。

爆改 Workerman

博客写到这,就开始手痒痒了,看到Workerman框架,我在基础上二开,使其能——同步编码,异步执行

代码已放到:PaulXu-cn/CoWorkerman.git

目前还是dev阶段,大家喜欢可以先 体验一波。

$ composer require paulxu-cn/co-workerman
ログイン後にコピー
ログイン後にコピー

一个简单的单线程 TCP Server

<?php// file: ./examples/example2/coWorkermanServer.php , 详细代码见github$worker = new CoWorker(&#39;tcp://0.0.0.0:8080&#39;);// 设置fork一个子进程$worker->count = 1;$worker->onConnect = function (CoTcpConnection  $connection) {
    try {
        $conName = "{$connection->getRemoteIp()}:{$connection->getRemotePort()}";
        echo PHP_EOL . "New Connection, {$conName} \n";

        $re = yield from $connection->readAsync(1024);
        CoWorker::safeEcho(&#39;get request msg :&#39; . $re . PHP_EOL );

        yield from CoTimer::sleepAsync(1000 * 2);

        $connection->send(json_encode(array(&#39;productId&#39; => 12, &#39;re&#39; =>true)));

        CoWorker::safeEcho(&#39;Response to :&#39; . $conName . PHP_EOL . PHP_EOL);
    } catch (ConnectionCloseException $e) {
        CoWorker::safeEcho(&#39;Connection closed, &#39; . $e->getMessage() . PHP_EOL);
    }};CoWorker::runAll();
ログイン後にコピー

这里设置fork 一个worker线程,处理逻辑中带有一个sleep() 2s的操作,依然不影响他同时响应多个请求。

启动测试程序

## 启动CoWorker服务$ php ./examples/example2/coWorkermanServer.php start## 启动请求线程$ php ./examples/example2/userClientFork.php
ログイン後にコピー

运行结果

绿色箭头——新的请求,红色箭头——响应请求

从结果上看到,这一个worker线程,在接收新的请求同时,还在回复之前的请求,各个连接交错运行。而我们的代码呢,看样子就是同步的,没有回调。

CoWorker购物车服务

好的,这里我们做几个简单的微服务模拟实际应用,这里模拟 用户请求端购物车服务库存服务产品服务。 模拟用户请求加购动作,购物车去分别请求 库存,产品 校验用户是否可以加购,并响应客户请求是否成功。

代码我就不贴了,太长了,麻烦移步 CoWorkerman/example/example5/coCartServer.php

运行命令

## 启动库存服务$ php ./examples/example5/otherServerFork.php 8081 inventory 1## 启动产品服务$ php ./examples/example5/otherServerFork.php  8082 product 2
ログイン後にコピー
## 启动CoWorker 购物车服务$ php ./examples/example5/coCartServer.php start
ログイン後にコピー
## 用户请求端$ php ./examples/example5/userClientFork.php
ログイン後にコピー

运行结果

黄色箭头——新的用户请求,蓝色箭头——购物车发起库存,产品检查请求,红色箭头——响应用户请求

从图中看到也是用1个线程服务多个连接,交错运行。

好的,那么PHP CoWorkerman 也能像 NodeJS 那样用 Async/Await 那样同步编码,异步运行了。

快来试试这个 CoWorkerman 吧:

$ composer require paulxu-cn/co-workerman
ログイン後にコピー
ログイン後にコピー

工作原理

先上图:

写真の上部は Workerman の作業レーン図、写真の下部分は CoWorkerman の作業レーン図です。

workerman worker プロセス がブロッキング関数に遭遇すると、IO が返されるまで待機します。この時点で新しいリクエストがある場合、アイドル状態のワーカーはこの新しい接続をめぐって競合します。

上の図では、worker5 での AsyncTCPConnection の使用状況を説明しました。ワーカー内で非ブロッキング リクエストが開始され、コールバック関数が登録され、プログラムは実行を継続しました。終わり。非同期リクエストが応答した場合は、別の方法で応答する必要があります (自分で別のリクエストを開始してリクエスタに通知するなど)。

下の図 CoWorkerman には、新しいリクエストを求めて競合する複数のワーカーもあります。ワーカー 1 が新しいリクエストを受信すると、ジェネレーターが生成され、非同期リクエストがコールバックに応答し、応答を要求した後、ジェネレーターが飛び出した場所 (yield) に戻り、コードの実行を続けます。

非同期リクエストを開始し、コールバック関数を登録します。これらのデフォルトのタスクは、CoWorkerman フレームワークで実行されています。コールバック関数の作業は、データの受信と送信です。リクエストを開始した人に送信されます。

#この例では、Promise:all() を呼び出すことによって複数のリクエストが開始され、結果が返されます。ジェネレーターの実行を続行する前に、すべての応答が返されるまで待ってください

プログラム

yield が飛び出した後、ワーカーはイベント ループ状態 ($event->loop())、つまりマルチチャネル監視になります。 : リクエスト ポート、サードパーティ クライアントの終了リクエストの応答ポート。このとき、

    に新しいリクエストがある場合、新しいリクエストをめぐって他の
  1. worker と競合します。競合が発生した場合、ワーカー内に新しいジェネレータが生成されます。 。
  2. クライアントが応答した場合、コールバック関数が呼び出されます。
  3. クライアントが応答した場合、ジェネレーター プログラムの実行を継続します。
1 から、

Worker が 1 つしかない場合、前のリクエストが受け入れられなかった場合でも、Worker は引き続き処理を受け入れることができると想定できます。完了しました。次のリクエストです。つまり、CoWorkerman は単一の Worker の下で実行でき、複数のリクエストを同時に処理できます。

もちろん、ここにも前提があります。ブロッキング機能は、単一の

Worker モードでは実行できません。一度ブロックされると、後続のリクエストはネットワーク カード上でブロックされます。したがって、コードをよく理解していない限り、サードパーティのライブラリを使用する場合は、マルチ Worker モードで CoWorkerman を実行することをお勧めします。また、ブロックする場合は、他の WorkerPocket の新しいリクエストです。

CoWorkerman の意味

    同期コードを使用して非同期リクエストを開始します。複数のリクエストを同時に実行できます。IO シリアル待機から待機に変更します。恐怖のない待ち時間を短縮するために並行して使用します。コードの可読性を低下させることなく、ビジネス プログラムの効率を向上させます。
  1. イベントループを1つのスレッドで行うことで、複数のリクエストを可能な限り処理することで、1リクエスト1スレッドによる頻繁なスレッド切り替えを軽減し、コアからの動作効率を向上させます。

CoWorkerman Ecological Niche

は、

Workerman Gateway など、純粋な Socket リクエストを処理するアプリケーションに適しています。 、または 大規模なフロント エンド 複数のサービスを統合するRPC結果は 統合後の最初の 3 ページに返されますこのようなシナリオです。

ログレコードは各プログラムの最も基本的な要件であり、ファイル書き込み機能がブロッキングしているため、メッセージキューやredisキューを使用するか、

LogstashをスキップしてElasticsearch#を直接スローすることを推奨します。 ##.

同僚には限界と独自の立場があります。

概要わかりました。ネットワーク非同期コーディングまでの PHP コルーチン コーディングはこれで終わりです。この記事を読んで疑問がたくさんある場合は、お問い合わせください。

yield

が文法をよく覚えていない場合は、このシリーズの前の記事を読んで復習してください。 うまくいった場合は、3回繰り返してください。

同僚

ありがとうございます!

以上がPHP 収量の高度な使用法を学ぶの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:learnku.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート