成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Mix PHP V2 實(shí)例:AliCloud 短信協(xié)程池異步發(fā)送守護(hù)程序

qc1iu / 521人閱讀

摘要:前些時(shí)間我們發(fā)布了實(shí)例協(xié)程池異步郵件發(fā)送守護(hù)程序范例,這一次我們提供一個(gè)使用大廠通過協(xié)程化來并行執(zhí)行短信發(fā)送任務(wù),本文是一個(gè)代碼簡單性能極強(qiáng)的范例。

前些時(shí)間我們發(fā)布了 Mix PHP V2 實(shí)例:協(xié)程池異步郵件發(fā)送守護(hù)程序 范例,這一次我們提供一個(gè)使用大廠 SDK 通過 Swoole Hook 協(xié)程化來并行執(zhí)行短信發(fā)送任務(wù),本文是一個(gè)代碼簡單、IO 性能極強(qiáng)的范例。

請先升級到 mix-framework >= v2.0.5。

本范例依然使用消息隊(duì)列的方式接收短信發(fā)送任務(wù),消息中間件使用:

redis

生產(chǎn)者
通??蚣苤惺褂?Redis 會(huì)安裝一個(gè)類庫來使用,本例使用原生代碼,便于理解。
// 連接
$redis = new Redis();
if (!$redis->connect("127.0.0.1", 6379)) {
    throw new Exception("Redis connect failed.");
}
$redis->auth("");
$redis->select(0);
// 投遞任務(wù)
for($i = 0; $i < 3; $i++){
    $data = [
        "phone"         => "***",
        "templateCode"  => "SMS_***",
        "templateParam" => ["code" => 123456],
    ];
    $redis->lpush("queue:sms", serialize($data));
}
消費(fèi)者

使用的是 ali 云的短信服務(wù),查看官方 PHP SDK 文檔 ,使用的庫為:

composer require alibabacloud/client

通過查看該庫的 composer 依賴文件,我們得知該庫基于 guzzlehttp 開發(fā),因?yàn)?Mix PHP 提供了無需修改代碼就可 Hook Guzzle 庫可在協(xié)程中使用的工具 Mix PHP V2 生態(tài):讓 Guzzle 支持 Swoole 的 Hook 協(xié)程,所以能基本確定該庫可在 Swoole 協(xié)程中使用。

首先我們安裝 https://github.com/mix-php/guzzle-hook 讓 alibabacloud/client 可在協(xié)程中使用:

composer require mix/guzzle-hook

然后在項(xiàng)目的 composer.json 文件中增加 extra 配置項(xiàng),如下:

"extra": {
    "include_files": [
      "vendor/mix/guzzle-hook/src/functions_include.php"
    ]
}

更新自動(dòng)加載:

composer dump-autoload

下面我們采用 Mix PHP V2 的守護(hù)程序、協(xié)程池來完成一個(gè)超高性能的短信發(fā)送程序。

首先我們在配置 applications/console/config/main.php 中注冊一個(gè)命令:

// 命令
"commands"         => [

        "smser" => [
            "Smser",
            "description" => "SMS send daemon demo.",
            "options"     => [
                [["d", "daemon"], "description" => "Run in the background"],
            ],
        ],

],

注冊的命令中指定的 Smser 命令類,接下來我們編寫一個(gè) SmserCommand 類:

applications/console/src/Commands/SmserCommand.php

 */
class SmserCommand
{

    const ACCESS_KEY = "***";
    const ACCESS_SECRET = "***";

    /**
     * 退出
     * @var bool
     */
    public $quit = false;

    /**
     * 主函數(shù)
     */
    public function main()
    {
        // 守護(hù)處理
        $daemon = Flag::bool(["d", "daemon"], false);
        if ($daemon) {
            ProcessHelper::daemon();
        }
        // 捕獲信號
        ProcessHelper::signal([SIGHUP, SIGINT, SIGTERM, SIGQUIT], function ($signal) {
            $this->quit = true;
            ProcessHelper::signal([SIGHUP, SIGINT, SIGTERM, SIGQUIT], null);
        });
        // 設(shè)置ali云全局參數(shù)
        AlibabaCloud::accessKeyClient(static::ACCESS_KEY, static::ACCESS_SECRET)->regionId("cn-hangzhou")->asDefaultClient();
        // 手動(dòng)關(guān)閉Swoole文件Hook,因?yàn)閍li云依賴的uuid庫有文件hook協(xié)程兼容問題,Swoole 4.4已經(jīng)適配該問題
        Coroutine::enableHook(SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_FILE);
        // 協(xié)程池執(zhí)行任務(wù)
        xgo(function () {
            $maxWorkers = 20;
            $maxQueue   = 20;
            $jobQueue   = new Channel($maxQueue);
            $dispatch   = new Dispatcher([
                "jobQueue"   => $jobQueue,
                "maxWorkers" => $maxWorkers,
            ]);
            $dispatch->start(SmserWorker::class);
            // 投放任務(wù)
            $redis = app()->redisPool->getConnection();
            while (true) {
                if ($this->quit) {
                    $dispatch->stop();
                    return;
                }
                try {
                    $data = $redis->brPop(["queue:sms"], 3);
                } catch (Throwable $e) {
                    $dispatch->stop();
                    return;
                }
                if (!$data) {
                    continue;
                }
                $data = array_pop($data); // brPop命令最后一個(gè)鍵才是值
                $jobQueue->push($data);
            }
        });
        // 等待事件
        Event::wait();
    }

}
$data = $redis->brPop(["queue:sms"], 3); 外部的異常捕獲可得知,當(dāng) Redis 連接出錯(cuò)時(shí),比如 Redis 重啟、連接異常時(shí)協(xié)程池會(huì)安全退出,也就是說當(dāng)進(jìn)程異常退出后用戶需使用 supervisor、pm2 等工具重啟守護(hù)進(jìn)程。

上面是一個(gè) Mix PHP 協(xié)程池的使用代碼,基本可以直接復(fù)制使用,框架默認(rèn)包含了協(xié)程池的 Demo,本次實(shí)例只是修改了協(xié)程池的 Worker,本命令主要是完成從 Redis 隊(duì)列中獲取消息然后 push 到 jobQueue 中,jobQueue 中的數(shù)據(jù)會(huì)被 20 個(gè) Worker 實(shí)例中某一個(gè)搶占后并行執(zhí)行,本例的發(fā)送代碼邏輯就在 SmserWorker 類中:

applications/console/src/Libraries/SmserWorker.php

 */
class SmserWorker extends AbstractWorker implements WorkerInterface
{

    /**
     * 郵件發(fā)送器
     * @var Smser
     */
    public $smser;

    /**
     * 初始化事件
     */
    public function onInitialize()
    {
        parent::onInitialize(); // TODO: Change the autogenerated stub
        // 實(shí)例化一些需重用的對象
        $this->smser = new Smser();
    }

    /**
     * 處理
     * @param $data
     */
    public function handle($data)
    {
        // TODO: Implement handle() method.
        $data = unserialize($data);
        if (empty($data)) {
            return;
        }
        try {
            $result = $this->smser->send($data["phone"], $data["templateCode"], $data["templateParam"]);
            app()->log->info("SMS sent successfully:phone {phone} templateCode {templateCode} result {result}", array_merge($data, ["result" => json_encode($result, JSON_UNESCAPED_UNICODE)]));
        } catch (Throwable $e) {
            app()->log->error("SMS failed to send:phone {phone} templateCode {templateCode} error {error}", array_merge($data, ["error" => $e->getMessage()]));
        }
    }

}

由以上代碼可見,Worker 在初始化時(shí),新增了一個(gè) Smser 類的屬性,當(dāng) jobQueue 消息投遞過來時(shí)消息會(huì)傳遞到 handle 方法,在該方法中使用 Mailer 類的實(shí)例完成郵件發(fā)送任務(wù),所以我們要編寫了一個(gè) Smser 發(fā)送程序:

applications/console/src/Libraries/Smser.php

 */
class Smser
{

    /**
     * 配置信息
     */
    const SIGN_NAME = "***";

    /**
     * Smser constructor.
     */
    public function __construct()
    {
        // 開啟協(xié)程鉤子
        Coroutine::enableHook();
    }

    /**
     * 發(fā)送
     * @param $phone
     * @param $templateCode
     * @param $templateParam
     * @return array
     * @throws ClientException
     * @throws ServerException
     */
    public function send($phone, $templateCode, $templateParam)
    {
        $result = AlibabaCloud::rpc()
            ->product("Dysmsapi")
            // ->scheme("https") // https | http
            ->version("2017-05-25")
            ->action("SendSms")
            ->method("POST")
            ->options([
                "query" => [
                    "PhoneNumbers"  => $phone,
                    "SignName"      => static::SIGN_NAME,
                    "TemplateCode"  => $templateCode,
                    "TemplateParam" => json_encode($templateParam),
                ],
            ])
            ->request();
        return $result->toArray();
    }

}

以上就完成了全部的代碼邏輯,現(xiàn)在我們開始測試,先啟動(dòng)消費(fèi)者守護(hù)程序:

[root@localhost bin]# ./mix-console smser

將上文的生產(chǎn)者腳本命名為 push.php 然后在 CLI 中執(zhí)行 (開一個(gè)新終端):

[root@localhost bin]# php /tmp/push.php

消費(fèi)者守護(hù)程序結(jié)果:

[root@localhost bin]# ./mix-console smser
[info] 2019-05-24 12:03:32 <101014> [message] SMS sent successfully:phone *** templateCode SMS_*** result {"Message":"OK","RequestId":"4071D031-6D9E-4F70-9269-6C1979080858","BizId":"939807358670612546^0","Code":"OK"}
[info] 2019-05-24 12:03:32 <101014> [message] SMS sent successfully:phone *** templateCode SMS_*** result {"Message":"觸發(fā)分鐘級流控Permits:1","RequestId":"490B73D7-317E-4362-B2DD-5E2153A7B891","Code":"isv.BUSINESS_LIMIT_CONTROL"}
[info] 2019-05-24 12:03:32 <101014> [message] SMS sent successfully:phone *** templateCode SMS_*** result {"Message":"觸發(fā)分鐘級流控Permits:1","RequestId":"1FD22EDB-BAA4-4416-8FF9-242EDCF34359","Code":"isv.BUSINESS_LIMIT_CONTROL"}

命令行終端打印了發(fā)送成功的日志,發(fā)送完成。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/31565.html

相關(guān)文章

  • Mix PHP V2 實(shí)例協(xié)程池異步郵件發(fā)送守護(hù)程序

    摘要:消費(fèi)者開發(fā)使用本例時(shí),請確保你使用的編譯時(shí)開啟了本例我們采用的守護(hù)程序協(xié)程池來完成一個(gè)超高性能的郵件發(fā)送程序。 去年 Mix PHP V1 發(fā)布時(shí),我寫了一個(gè)多進(jìn)程的郵件發(fā)送實(shí)例: 使用 mixphp 打造多進(jìn)程異步郵件發(fā)送,今年 Mix PHP V2 發(fā)布,全面的協(xié)程支持讓我們可以使用一個(gè)進(jìn)程就可達(dá)到之前多個(gè)進(jìn)程都無法達(dá)到的更高 IO 性能,所以今天重寫一個(gè)協(xié)程池版本的郵件發(fā)送實(shí)例。...

    lauren_liuling 評論0 收藏0
  • Mix PHP V2 新特性:協(xié)程、定時(shí)器

    摘要:主函數(shù)查詢數(shù)據(jù)不手動(dòng)釋放的連接不會(huì)歸還連接池,會(huì)在析構(gòu)時(shí)丟棄執(zhí)行結(jié)果為,說明是并行執(zhí)行的。主函數(shù)查詢數(shù)據(jù)即便拋出了異常,仍然能執(zhí)行到,沒有導(dǎo)致內(nèi)的一直處于阻塞狀態(tài)。主函數(shù)一次性定時(shí)持續(xù)定時(shí)停止定時(shí) 協(xié)程 Mix PHP V2 基于 Swoole 4 的 PHP Stream Hook 協(xié)程技術(shù)開發(fā),協(xié)程使用方式與 Golang 幾乎一致,包括框架封裝的協(xié)程池、連接池、命令行處理都大量參...

    Nosee 評論0 收藏0
  • Mix PHP V2 生態(tài):讓 Guzzle 支持 Swoole 的 Hook 協(xié)程

    摘要:是一個(gè)非常流行的的客戶端,現(xiàn)在各大廠的也都開始基于開發(fā),因?yàn)橹恢С值膮f(xié)程,而默認(rèn)是使用擴(kuò)展的,所以開發(fā)了,能在不修改源碼的情況下讓協(xié)程化。 Guzzle 是一個(gè)非常流行的 PHP 的 HTTP 客戶端,現(xiàn)在各大廠的 SDK 也都開始基于 Guzzle 開發(fā),因?yàn)?Swoole 只支持 PHP Stream 的協(xié)程 Hook ,而 Guzzle 默認(rèn)是使用 cURL 擴(kuò)展的,所以 Mix...

    Flands 評論0 收藏0
  • swoole通用協(xié)程池的實(shí)現(xiàn)

    摘要:之前過行代碼實(shí)現(xiàn)通用協(xié)程池今天看了下相關(guān)文檔,用也實(shí)現(xiàn)了一個(gè),由于沒有的,所以實(shí)現(xiàn)的有點(diǎn)簡單,但是實(shí)用性還可以,通過工廠函數(shù)實(shí)現(xiàn)了通用性。官方的協(xié)程池是用只能用在。因?yàn)閰f(xié)程池代碼層耦合了實(shí)例化邏輯。 之前過golang40行代碼實(shí)現(xiàn)通用協(xié)程池 今天看了下swoole相關(guān)文檔,用PHP也實(shí)現(xiàn)了一個(gè),由于swoole沒有g(shù)olang的select,所以實(shí)現(xiàn)的有點(diǎn)簡單,但是實(shí)用性還可以,通過...

    wanghui 評論0 收藏0
  • Python協(xié)程(真才實(shí)學(xué),想學(xué)的進(jìn)來)

    摘要:所以與多線程相比,線程的數(shù)量越多,協(xié)程性能的優(yōu)勢越明顯。值得一提的是,在此過程中,只有一個(gè)線程在執(zhí)行,因此這與多線程的概念是不一樣的。 真正有知識的人的成長過程,就像麥穗的成長過程:麥穗空的時(shí)候,麥子長得很快,麥穗驕傲地高高昂起,但是,麥穗成熟飽滿時(shí),它們開始謙虛,垂下麥芒。 ——蒙田《蒙田隨筆全集》 上篇論述了關(guān)于python多線程是否是雞肋的問題,得到了一些網(wǎng)友的認(rèn)可,當(dāng)然也有...

    lykops 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<