Swoole學習二Hyperf框架啟動流程分析

2020-09-04 大致的北漂日子

  • Hyperf簡介

這次我們學習下基於Swoole的PHP框架的使用,swoole的框架很多,目前主流的有Swoft(swoft.org)、Hyperf(hyperf.io)、EasySwoole(easyswoole.com)。

這裡我們以Hyperf為例進行學習,因為大體上這些框架是差不多的,其實就是它有啥我趕緊也弄一個,如果要應用在生產環境上,還需要根據自己的需求進行選擇,具體可以看下項目的維護進度、社區的活躍程度等,Hyperf是Swoole官方推薦的框架,開發人員也是swoole的核心人員。

無論是Hyperf還是Laravel、Yii等等,學習一個框架,我們首先要學習人家的官方文檔,至少要瀏覽一遍知道他的基本組成、功能點。

Hyperf提供的功能除了之前php-fpm運行模式下的框架功能如路由、事件、中間件、請求、響應、資料庫之外,加入了如註解、微服務組件、全協程支持等基於swoole的功能。

本次我們基於2.0.3版本進行學習,不同的版本之前可能略有差異。

我們可以按照文檔中的步驟進行安裝即可,本機或者docker中,這次我們只看一下Hyperf的啟動流程。

  • 啟動流程分析

在運行之前我們需要看一下官方文檔,了解兩個東西

  1. Hyperf的ConfigProvider思想 https://hyperf.wiki/2.0//zh-cn/di , 其他傳統框架如Laravel也有Container的概念

執行入口文件

./bin/hyperf.php start

hyperf.php

// Self-called anonymous function that creates its own scope and keep the global namespace clean.(function () { Hyperf\Di\ClassLoader::init();// 類加載、註解掃描等 /** @var \Psr\Container\ContainerInterface $container */ $container = require BASE_PATH . &39;;// 初始化容器 // 初始化consoleApplication $application = $container->get(\Hyperf\Contract\ApplicationInterface::class); // 啟動 $application->run();})();

在執行前會進行配置的掃描、依賴的處理、註解的掃描等等

通過ClassLoader::init(); 這裡面一堆東西,什麼類加載、註冊、掃描註解、類map等等大家可以慢慢看。

進入到container.php

// (new DefinitionSourceFactory(true))() 這個語法注意,對象當方法調用,會觸發__invoke方法$container = new Container((new DefinitionSourceFactory(true))());if (! $container instanceof \Psr\Container\ContainerInterface) { throw new RuntimeException(&39;);}return ApplicationContext::setContainer($container);

那麼上面DefinitionSourceFactory幹的事就是通過上面提到的ConfigProvider來解決依賴的問題,Hyperf的每個組件都需要有ConfigProvider文件,ConfigProvider在啟動的時候會進行處理,其中的denpendencies就是處理Inerface與對應的Definition。

那麼我們從vendor/hyperf/framework/src/ConfigProvider.php 可以看到ApplicationInterface的實現是ApplicationFactory


  • 初始化命令行程序:symfony/console/application ,這是公共組件,Laravel也用的這個
  • 解析輸入的command: 如 hyperf.php start 得到 vendor/hyperf/server/src/Command/StartServer ,繼承自Symfony/console/Command
  • 並執行StartServer->execute
    • 檢查環境 checkEnvironment
    • 獲取 config/autoload/server.php 配置
    • 解析配置
    • 根據server裡面的type去初始化server
    • startServer.php
    • 調用vendor/hyperf/server/src/Server->init
    • 後面的事情就是初始化server了,type可以是SERVER_HTTP, SERVER_WEBSOCKET等
    • 設置on 回調處理如onWorkerStart, onRequest等等
    • 這裡會掃描路由,包括routes.php及使用註解的路由,組裝成url->handleClass的映射,如 api/v1/login -> /UserController->login 類似這樣的
    • 調用server->start 啟動服務, 這裡後面就進入到swoole的執行流程了
  • 以Http服務為例子,當有請求進來時,會觸發onRequest事件
  • 也就是vendor/hyperf/http-server/src/Server->onRequest
    • 這個流程與其他框架就差不多了
    • 獲取請求參數,從路由解析出controller->method
    • middleware處理
    • 最後到達controller進行處理
    • 返回response

部分代碼:

startServer.php

protected function execute(InputInterface $input, OutputInterface $output) { $this->checkEnvironment($output); $serverFactory = $this->container->get(ServerFactory::class) ->setEventDispatcher($this->container->get(EventDispatcherInterface::class)) ->setLogger($this->container->get(StdoutLoggerInterface::class)); // 獲取配置 config/autoload/server.php $serverConfig = $this->container->get(ConfigInterface::class)->get(&39;, []); if (! $serverConfig) { throw new InvalidArgumentException(&39;); } // 初始化配置 $serverFactory->configure($serverConfig); Runtime::enableCoroutine(true, swoole_hook_flags()); // server->start() $serverFactory->start(); return 0; }

Server.php

protected function initServers(ServerConfig $config) { $servers = $this->sortServers($config->getServers()); // config/autoload/server.php 可以配置多個server foreach ($servers as $server) { $name = $server->getName(); $type = $server->getType(); $host = $server->getHost(); $port = $server->getPort(); $sockType = $server->getSockType(); $callbacks = $server->getCallbacks(); if (! $this->server instanceof SwooleServer) { // 根據類型初始化server 如HTTPServer WebSocketServer $this->server = $this->makeServer($type, $host, $port, $config->getMode(), $sockType); $callbacks = array_replace($this->defaultCallbacks(), $config->getCallbacks(), $callbacks); // 設置回調 $this->registerSwooleEvents($this->server, $callbacks, $name); $this->server->set(array_replace($config->getSettings(), $server->getSettings())); ServerManager::add($name, [$type, current($this->server->ports)]); if (class_exists(BeforeMainServerStart::class)) { // Trigger BeforeMainServerStart event, this event only trigger once before main server start. $this->eventDispatcher->dispatch(new BeforeMainServerStart($this->server, $config->toArray())); } } else { /** @var bool|\Swoole\Server\Port $slaveServer */ $slaveServer = $this->server->addlistener($host, $port, $sockType); if (! $slaveServer) { throw new \RuntimeException(&34;); } $server->getSettings() && $slaveServer->set(array_replace($config->getSettings(), $server->getSettings())); $this->registerSwooleEvents($slaveServer, $callbacks, $name); ServerManager::add($name, [$type, $slaveServer]); } // Trigger beforeStart event. if (isset($callbacks[SwooleEvent::ON_BEFORE_START])) { [$class, $method] = $callbacks[SwooleEvent::ON_BEFORE_START]; if ($this->container->has($class)) { $this->container->get($class)->{$method}(); } } if (class_exists(BeforeServerStart::class)) { // Trigger BeforeServerStart event. $this->eventDispatcher->dispatch(new BeforeServerStart($name)); } } }

  • 啟動流程圖


  • 請求處理


其中框架中的公共組件可以看下:

  1. 路由處理:fastRoute
    1. https://github.com/nikic/FastRoute
  2. Console及Command處理:
    1. https://github.com/symfony/symfony/tree/master/src/Symfony/Component/Console

相關焦點

  • swoole學習七基於hyperf的微服務初探(二)
    binary二進位?, 二進位明顯效率更高,但是json更適合人來看如何註冊、發現服務?consul:consul agent -dev 啟動服務提供者、調用者這裡使用了json進行序列化,可見json的數據還是很大的,但是目前官方不支持protocol的格式通過數據可以看到server其實還是通過路由匹配處理客戶端的請求放在最後這裡我們只是實現了一個簡單的rpc調用+註冊中心的例子,通過這個demo大家可以再深入的去研究微服務的流程
  • swoole學習五協程初探
    我們學習的版本是Swoole版本:4.5.2來個Demo理解下協程<?PHP_EOL;});執行發現並不是順序執行的,第一個等待兩秒,但是程序直接執行了第二個,這就是協程帶來的變化,非阻塞gaoz@nobodyMBP hyperf-skeleton % php swoole_demo/coroutine/coroutine.php 2:3:1:再來看普通的阻塞執行
  • swoole學習六hyperf註解的使用
    如何使用的先來看一下hyperf中的簡單使用<?this->request->input(&39;, &39;); $method = $this->request->getMethod(); return [ &39; => $method, &39; => &34;, ]; }}我們啟動服務之後直接訪問
  • 基於hyperf構建微服務-註冊中心Consul(二)使用
    這次我們主要學習三個方面:consul的HTTP apihyperf的服務註冊邏輯hyperf的服務發現邏輯中是如何使用的先來看服務註冊服務啟動時會根據配置信息及註解取獲取需要註冊的service信息服務提供者引用啟動的時候註冊到consul這裡需要提一嘴的是,hyperf中使用了大量的事件大家需要看一下
  • swoole學習四Hyperf的Aop基本應用
    function login(RequestInterface $request, ResponseInterface $response) { $userName = $request->input(&39;); return $this->loginService->login($userName); }啟動服務之後
  • swoole學習九hyperf使用Apollo配置中心
    Portal: 提供給client做負載均衡用的中間部分是Apollo自身保證高可用的架構,包括Eureka註冊中心、metaService代理服務使用步驟下載Apollo包導入相關sql修改demo.sh資料庫配置啟動服務
  • Swoole學習八Hyperf做一個Excel異步導出服務
    前提準備安裝crontab組件 composer require hyperf/crontab利用註解聲明一個計劃任務,需要配置屬性name任務名稱、rule也就是執行時間、callback也就是業務執行邏輯,關於註解我們之前已經學習過了在crontab的callback中編寫業務邏輯就可以了
  • php通信服務框架選擇swoole還是workerman?
    目前php通信服務框架最流行的有swoole與workerman倆個框架,swoole是有C語言開發的php擴展類,而workerman是純PHP開發框架,可能swoole比workerman出名,在百度、騰訊公司都有在使用,使用頻率也比較高,那麼我們來看下php通信服務框架選擇swoole還是
  • PHP與Swoole是如何異步通訊的
    本套課程適合已入門的PHP開發者,將帶領大家從swoole基礎到實戰(進程,協程),全面學習swoole。課程目錄1 Swoole介紹及應用場景2 Swoole安裝及自動上傳配置3 Swoole學習—TCP服務端4 Swoole學習—TCP客戶同步端5 Swoole學習—同步異步端6 Swoole學習—tcp和udp
  • swoole學習三hyperf實現簡單聊天應用
    Swoole提供了普通php-fpm模式下無法提供的websocket服務,hyperf在此基礎上進行了封裝,支持原生websocket協議、支持socket.io協議。hyperf完成了對socket.io服務端的封裝支持,對比原生的websocket服務,我們不需要自己去處理分組、私聊、廣播等邏輯了,直接使用即可。
  • Hyperf 發布 Nats 組件及 v1.1.5 版本 | PHP 微服務雲原生協程框架
    更新內容本周更新主要為新增 hyperf/nats 組件,Nats 是一個開源、輕量級、高性能的雲原生消息系統,本組件使 Hyperf 與 Nats 進行交互提供了可能性。
  • 學習swoole,swoole進程結構必須得掌握
    ,子進程和父進程一樣,擁有自己的內存空間和上下文環境二、Master進程:主進程Manger進程:管理進程Worker進程:工作進程Task進程:異步任務工作進程1、Master進程第一層,Master進程,這個是swoole的主進程,這個進程是用於處理swoole
  • Hyperf 發布 v1.1.13 版本,企業級的 PHP 微服務雲原生協程框架
    直接訪問 官網 hyperf.io 或 文檔 hyperf.wiki 查看更新內容。新增 #1137 constants 組件增加國際化支持。 #1165 Hyperf\HttpServer\Contract\RequestInterface 新增 route 方法。 #1195 註解 Cacheable 和 CachePut 增加最大超時時間偏移量配置。
  • swoole學習七基於hyperf的微服務初探(一)
    目前php這邊的生態不是太完善,因為微服務不是拆開就完事了,它需要一系列的組成部分,各司其職,從基於swoole的框架來看,目前基本上都提供了微服務相關組件,這是我們應該去了解的,也有一些公司使用php+go的組合去重構業務。
  • Swoole難上手?從EasySwoole開始
    名副其實,作者為了讓開發者更便捷的使用Swoole 而封裝的開發框架,地址在下方EasySwoole https://www.easyswoole.com/使用EasySwoole你會發現有很多很難理解的概念及用法
  • Hyperf 發布 v2.0.24 版本,企業級的 PHP 微服務雲原生協程框架
    composer update "hyperf/*" -o直接訪問 官網 hyperf.io 或 文檔 hyperf.wiki 查看更新內容修復 #2978 修復當沒有引用 hyperf/contract 時,hyperf/snowflake 組件會無法正常使用的問題。
  • Zend-Expressive-Swoole 0.2.2 支持 Swoole 4 協程
    ,完美解決了從前的笨重低性能的印象,基於 Zend Expressive 完美解耦的設計,搭建一個高性能的符合自己心意的框架完全不在話下了!通過 Zend Expressive 搭建一個我喜歡的框架只需執行 composer create-project zendframework/zend-expressive-skeleton folder_name 通過 Composer 就可以進入組件安裝的流程了,folder_name 改成你想要存放的文件夾名字
  • Hyperf 發布 WebSocket 組件及多個組件 v1.0.3 更新 | 企業級的...
    hyperf/database hyperf/di hyperf/framework hyperf/http-server hyperf/json-rpc hyperf/model-cache hyperf/websocket-client hyperf/websocket-serverAdded #48 增加 WebSocket 協程客戶端及服務端 #51 增加了 enableCache
  • swoole之進程結構
    在作業系統中可以運行多個進程的,對於一個進程來說,它可以創建自己的子進程,那麼當我們在一個進程中創建出若干個子進程的時候那麼可以看到如圖,子進程和父進程一樣,擁有自己的內存空間和上下文環境二、Swoole進程結構Swoole的高效不僅僅於底層使用c編寫,他的進程結構模型也使其可以高效的處理業務,我們想要深入學習,並且在實際的場景當中使用必須了解,下面我們先看一下結構圖
  • Hyperf 發布 v1.1.0 版本,企業級的 PHP 微服務雲原生協程框架
    來獲取由常量 SWOOLE_HOOK_FLAGS 所定義的 Runtime Hook 等級,您可以在 bin/hyperf.php 通過 !AMQP Consumer 增加 nack 的返回類型,當消費邏輯返回 Hyperf\Amqp\Result::NACK 時抽象消費者會以 basic_nack 方法來響應消息; #654 增加所有 Swoole Event 的默認回調和對應的 Hyperf 事件;變更 #437 Hyperf\Testing\Client 在遇到異常時不再直接拋出異常而是交給 ExceptionHandler 流程處理