摘要:會(huì)依據(jù)協(xié)議,將請求的數(shù)據(jù)等信息發(fā)送給解析器,接下來解析器會(huì)解析文件,初始化執(zhí)行環(huán)境,然后處理請求,再以規(guī)定的格式返回處理后的結(jié)果,退出進(jìn)程。它的特點(diǎn)是會(huì)在動(dòng)態(tài)分配處理進(jìn)程給請求,以達(dá)到提高效率的目的,大多數(shù)實(shí)現(xiàn)都會(huì)維護(hù)一個(gè)進(jìn)程池。
PHP作為世界上最好的編程語音,被廣泛的運(yùn)用到Web開發(fā)中。因?yàn)槠湔Z法和C類似,有著非常平緩的學(xué)習(xí)曲線,越來越多的人使用PHP進(jìn)行Web產(chǎn)品的快速開發(fā)。PHP世界里也涌現(xiàn)了很多開發(fā)框架,比如Laravel、ThinkPHP等,但不論何總框架,他們在處理Web請求時(shí)的模式都是一樣的,本文首先闡述PHP開發(fā)Web應(yīng)用的基本架構(gòu),然后分別分析Laravel和ThinkPHP在處理Web請求時(shí)的處理流程。
PHP開發(fā)Web應(yīng)用的基本架構(gòu)PHP開發(fā)Web應(yīng)用時(shí)所以的請求需要指向具體的入口文件。WebServer是一個(gè)內(nèi)容分發(fā)者,他接受用戶的請求后,如果是請求的是css、js等靜態(tài)文件,WebServer會(huì)找到這個(gè)文件,然后發(fā)送給瀏覽器;如果請求的是/index.php,根據(jù)配置文件,WebServer知道這個(gè)不是靜態(tài)文件,需要去找PHP解析器來處理,那么他會(huì)把這個(gè)請求簡單處理后交給PHP解析器。
WebServer會(huì)依據(jù)CGI協(xié)議,將請求的Url、數(shù)據(jù)、Http Header等信息發(fā)送給PHP解析器,接下來PHP解析器會(huì)解析php.ini文件,初始化執(zhí)行環(huán)境,然后處理請求,再以CGI規(guī)定的格式返回處理后的結(jié)果,退出進(jìn)程。web server再把結(jié)果返回給瀏覽器。整個(gè)處理過程如上圖所示。
FastCGI這里的PHP解析器就是實(shí)現(xiàn)了CGI協(xié)議的程序,每次請求到來時(shí)他會(huì)解析php.ini文件,初始化執(zhí)行環(huán)境,這就導(dǎo)致PHP解析器性能低下,于是就出現(xiàn)了CGI的改良升級版FastCGI。FastCGI是一種語言無關(guān)的協(xié)議,用來溝通程序(如PHP, Python, Java)和Web服務(wù)器(Apache2, Nginx), 理論上任何語言編寫的程序都可以通過FastCGI來提供Web服務(wù)。它的特點(diǎn)是會(huì)在動(dòng)態(tài)分配處理進(jìn)程給請求,以達(dá)到提高效率的目的,大多數(shù)FastCGI實(shí)現(xiàn)都會(huì)維護(hù)一個(gè)進(jìn)程池。FastCGI會(huì)先啟一個(gè)master進(jìn)程,解析配置文件,初始化執(zhí)行環(huán)境,然后再啟動(dòng)多個(gè)worker進(jìn)程。當(dāng)請求過來時(shí),master進(jìn)程會(huì)這個(gè)請求傳遞給一個(gè)worker進(jìn)程,然后立即接受下一個(gè)請求。而且當(dāng)worker進(jìn)程不夠用時(shí),master可以根據(jù)配置預(yù)先啟動(dòng)幾個(gè)worker進(jìn)程等待;當(dāng)然空閑worker進(jìn)程太多時(shí),也會(huì)自動(dòng)關(guān)閉,這樣就提高了性能,節(jié)約了系統(tǒng)資源。整個(gè)過程FastCGI扮演著對CGI進(jìn)程進(jìn)行管理的角色。
PHP-FPMPHP-FPM是一個(gè)專門針對PHP實(shí)現(xiàn)了FastCGI協(xié)議的程序,它實(shí)際上就是一個(gè)PHP FastCGI進(jìn)程管理器,負(fù)責(zé)管理一個(gè)進(jìn)程池,調(diào)用PHP解析器來處理來自Web服務(wù)器的請求。PHP-FPM能夠?qū)hp.ini文件的修改進(jìn)行平滑過度。
新建一個(gè)helloworld.php文件,寫入下列代碼
配置好WebServer和PHP-FPM等php運(yùn)行環(huán)境后,在瀏覽器中訪問該文件就可以直接得到輸出。
基于PHP的Web框架它主要的任務(wù)包括:基于某模式將PHP開發(fā)常用功能封裝實(shí)現(xiàn)使開發(fā)者快速開發(fā)的工具
代碼重用:定義包、類、函數(shù)的放置和加載規(guī)則,建議直接整合Composer及其AutoLoad特性。
請求的分發(fā)管理:這個(gè)就是路由,Rest風(fēng)的框架喜歡Rewrite,簡單的一點(diǎn)的框架主要通過參數(shù)來定位模塊和方法所在。
配置文件管理:加載和動(dòng)態(tài)加載配置數(shù)據(jù)
錯(cuò)誤和異常管理:異常捕捉、錯(cuò)誤日志記錄以及錯(cuò)誤碼規(guī)范。
Layout和模板引擎:如何規(guī)劃頁面布局、widget如何重用、ajax頁面如何結(jié)合、過期- session如何重定向;數(shù)據(jù)和模板怎么渲染成HTML,是否壓縮和設(shè)置過期頭。
數(shù)據(jù)庫:如何融入控制器;支持什么樣的driver;考慮主從分離的擴(kuò)展性;以及是否使用ORM
ThinkPHP3.2框架處理流程分析
TP的設(shè)計(jì)邏輯就是簡單粗暴,面對問題解決問題,所以他的處理流程是基于面向過程的思想,而沒有采用面向?qū)ο蟮囊蕾囎⑷?、控制反轉(zhuǎn)等思路。他的自動(dòng)加載、錯(cuò)誤處理通過php原生函數(shù)的回調(diào)來實(shí)現(xiàn)。TP處理每次請求要經(jīng)過四個(gè)步驟如下圖所示:
index.php是TP的入口文件,所有的請求都由該文件接管,它的工作也很簡單主要是引入ThinkPHP入口文件
5.3.0 !"); // 開啟調(diào)試模式 建議開發(fā)階段開啟 部署階段注釋或者設(shè)為false define("APP_DEBUG",False); // 定義應(yīng)用目錄 define("APP_PATH","./Application/"); // 引入ThinkPHP入口文件 require "./ThinkPHP/ThinkPHP.php";載入框架入口文件ThinkPHP.php
在ThinkPHP.php中主要記錄初始運(yùn)行時(shí)間和內(nèi)存開銷,然后完成系統(tǒng)常量判斷及定義,最后載入框架引導(dǎo)類(ThinkThink)并執(zhí)行Think::start方法進(jìn)行應(yīng)用初始化。
應(yīng)用初始化ThinkThink:start()應(yīng)用初始化首先設(shè)置錯(cuò)誤處理機(jī)制和自動(dòng)加載機(jī)制
static public function start() { // 注冊AUTOLOAD方法 spl_autoload_register("ThinkThink::autoload"); // 設(shè)定錯(cuò)誤和異常處理 register_shutdown_function("ThinkThink::fatalError"); set_error_handler("ThinkThink::appError"); set_exception_handler("ThinkThink::appException");
然后加載相關(guān)配置文件和運(yùn)行模式定義文件,最后調(diào)用ThinkApp類的run方法啟動(dòng)應(yīng)用
運(yùn)行應(yīng)用App::run()此后TP進(jìn)入請求處理管道,TP為管道中定義了14個(gè)事件,每個(gè)事件都可以綁定回調(diào)函數(shù),請求到達(dá)管道后依次觸發(fā)這些事件,事件觸發(fā)后就會(huì)調(diào)用綁定到事件的回調(diào)函數(shù),整個(gè)管道的生命周期由app_init開始,由app_end結(jié)束。具體實(shí)現(xiàn)上,TP將這些事件命名為標(biāo)簽(位),也可以稱之為鉤子,將回調(diào)函數(shù)命名為行為,當(dāng)應(yīng)用程序運(yùn)行到標(biāo)簽的時(shí)候,就會(huì)被攔截下來,統(tǒng)一執(zhí)行相關(guān)的行為。
Laravel框架處理流程分析統(tǒng)一入口
Laravel框架使用了統(tǒng)一入口,入口文件:/public/index.php
make("IlluminateContractsHttpKernel"); //運(yùn)行Kernel類的handle方法,主要?jiǎng)幼魇沁\(yùn)行middleware和啟動(dòng)URL相關(guān)的Contrller $response = $kernel->handle( $request = IlluminateHttpRequest::capture() ); //控制器返回結(jié)果之后的操作,暫時(shí)還沒看,以后補(bǔ)上 $response->send(); $kernel->terminate($request, $response);自動(dòng)加載文件
laravel的自動(dòng)加載,其實(shí)也就是Composer的自動(dòng)加載
Composer根據(jù)聲明的依賴關(guān)系,從相關(guān)庫的源下載代碼文件,并根據(jù)依賴關(guān)系在 Composer 目錄下生成供類自動(dòng)加載的 PHP 腳本,使用的時(shí)候,項(xiàng)目開始處引入 “/vendor/autoload.php” 文件,就可以直接實(shí)例化這些第三方類庫中的類了。
服務(wù)容器,也叫IoC容器,其實(shí)包含了依賴注入(DI)和控制反轉(zhuǎn)(IoC)兩部分,是Laravel的真正核心。其他的各種功能模塊比如 Route(路由)、Eloquent ORM(數(shù)據(jù)庫 ORM 組件)、Request and Response(請求和響應(yīng))等等等等,實(shí)際上都是與核心無關(guān)的類模塊提供的,這些類從注冊到實(shí)例化,最終被使用,其實(shí)都是 Laravel 的服務(wù)容器負(fù)責(zé)的。
啟動(dòng)Kernel代碼Kernel實(shí)例調(diào)用handle方法,意味著Laravel的核心和公用代碼已經(jīng)準(zhǔn)備完畢,此項(xiàng)目正式開始運(yùn)行
代碼清單/app/Http/Kernel.php
"AppHttpMiddlewareAuthenticate", "auth.basic" => "IlluminateAuthMiddlewareAuthenticateWithBasicAuth", "guest" => "AppHttpMiddlewareRedirectIfAuthenticated", "test" => "AppHttpMiddleware estMiddleWare", ]; }
可以看到,其實(shí)這個(gè)文件里面沒有handle方法,只有一些屬性定義,所以真正的handle方法,實(shí)在父類里面實(shí)現(xiàn)的
代碼清單…/Illuminate/Foundation/Http/Kernel.php
//這個(gè)很重要,是項(xiàng)目的一些啟動(dòng)引導(dǎo)項(xiàng),Kernel的重要步驟中,首先就是啟動(dòng)這些文件的bootstrap方法 protected $bootstrappers = [ //檢測環(huán)境變量文件是否正常 "IlluminateFoundationBootstrapDetectEnvironment", //取得配置文件,即把/config/下的所有配置文件讀取到容器(app()->make("config")可以查看所有配置信息) "IlluminateFoundationBootstrapLoadConfiguration", //綁定一個(gè)名字為log的實(shí)例到容器,怎么訪問??(app()->make("log")) "IlluminateFoundationBootstrapConfigureLogging", //設(shè)置異常抓取信息,這個(gè)還沒仔細(xì)看,但大概就是這個(gè)意思 "IlluminateFoundationBootstrapHandleExceptions", //把/config/app.php里面的aliases項(xiàng)利用PHP庫函數(shù)class_alias創(chuàng)建別名,從此,我們可以使用App::make("app")方式取得實(shí)例 "IlluminateFoundationBootstrapRegisterFacades", //把/config/app.php里面的providers項(xiàng),注冊到容器 "IlluminateFoundationBootstrapRegisterProviders", //運(yùn)行容器中注冊的所有的ServiceProvider中得boot方法 "IlluminateFoundationBootstrapBootProviders", ]; //真正的handle方法 public function handle($request) { try { //主要是這行,調(diào)度了需要運(yùn)行的方法 return $this->sendRequestThroughRouter($request); } catch (Exception $e) { $this->reportException($e); return $this->renderException($request, $e); } } protected function sendRequestThroughRouter($request) { $this->app->instance("request", $request); Facade::clearResolvedInstance("request"); //運(yùn)行上述$bootstrappers里面包含的文件的bootstrap方法,運(yùn)行的作用,上面已經(jīng)注釋 $this->bootstrap(); //這是在對URL進(jìn)行調(diào)度之前,也就是運(yùn)行Route之前,進(jìn)行的一些準(zhǔn)備工作 return (new Pipeline($this->app)) ->send($request) //需要運(yùn)行$this->middleware里包含的中間件 ->through($this->middleware) //運(yùn)行完上述中間件之后,調(diào)度dispatchToRouter方法,進(jìn)行Route的操作 ->then($this->dispatchToRouter()); } //前奏執(zhí)行完畢之后,進(jìn)行Route操作 protected function dispatchToRouter() { return function($request) { $this->app->instance("request", $request); //跳轉(zhuǎn)到Router類的dispatch方法 return $this->router->dispatch($request); }; }
下面就需要根據(jù)URL和/app/Http/routes.php文件,進(jìn)行Route操作
文件清單…/Illuminate/Routing/Router.php
public function dispatch(Request $request) { $this->currentRequest = $request; //在4.2版本里面,Route有一個(gè)篩選屬性;5.0之后的版本,被Middleware代替 $response = $this->callFilter("before", $request); if (is_null($response)) { //繼續(xù)調(diào)度 $response = $this->dispatchToRoute($request); } $response = $this->prepareResponse($request, $response); //在4.2版本里面,Route有一個(gè)篩選屬性;5.0之后的版本,被Middleware代替 $this->callFilter("after", $request, $response); return $response; } public function dispatchToRoute(Request $request) { $route = $this->findRoute($request); $request->setRouteResolver(function() use ($route) { return $route; }); $this->events->fire("router.matched", [$route, $request]); $response = $this->callRouteBefore($route, $request); if (is_null($response)) { // 只看這一行,還是調(diào)度文件 $response = $this->runRouteWithinStack( $route, $request ); } $response = $this->prepareResponse($request, $response); $this->callRouteAfter($route, $request, $response); return $response; } protected function runRouteWithinStack(Route $route, Request $request) { // 取得routes.php里面的Middleware節(jié)點(diǎn) $middleware = $this->gatherRouteMiddlewares($route); //這個(gè)有點(diǎn)眼熟 return (new Pipeline($this->container)) ->send($request) //執(zhí)行上述的中間件 ->through($middleware) ->then(function($request) use ($route) { //到Controller類了 return $this->prepareResponse( $request, //run控制器 $route->run($request) ); }); } public function run(Request $request) { $this->container = $this->container ?: new Container; try { if ( ! is_string($this->action["uses"])) return $this->runCallable($request); if ($this->customDispatcherIsBound()) //實(shí)際上是運(yùn)行了這行 return $this->runWithCustomDispatcher($request); //其實(shí)我是直接想運(yùn)行這行 return $this->runController($request); } catch (HttpResponseException $e) { return $e->getResponse(); } } //繼續(xù)調(diào)度,最終調(diào)度到.../Illuminate/Routing/ControllerDispatcher.php文件的dispatch方法 protected function runWithCustomDispatcher(Request $request) { list($class, $method) = explode("@", $this->action["uses"]); $dispatcher = $this->container->make("illuminate.route.dispatcher"); return $dispatcher->dispatch($this, $request, $class, $method); }
文件清單…/Illuminate/Routing/ControllerDispatcher.php
public function dispatch(Route $route, Request $request, $controller, $method) { $instance = $this->makeController($controller); $this->assignAfter($instance, $route, $request, $method); $response = $this->before($instance, $route, $request, $method); if (is_null($response)) { //還要調(diào)度 $response = $this->callWithinStack( $instance, $route, $request, $method ); } return $response; } protected function callWithinStack($instance, $route, $request, $method) { //又是Middleware......有沒有忘記,官方文檔里面Middleware可以加在控制器的構(gòu)造函數(shù)中?。]錯(cuò),這個(gè)Middleware就是在控制器里面申明的 $middleware = $this->getMiddleware($instance, $method); //又是這個(gè),眼熟吧 return (new Pipeline($this->container)) ->send($request) //再次運(yùn)行Middleware ->through($middleware) ->then(function($request) use ($instance, $route, $method) { 運(yùn)行控制器,返回結(jié)果 return $this->call($instance, $route, $method); }); }
終于到達(dá)控制器
轉(zhuǎn)自:http://www.eurekao.com/PHP-pr...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/22586.html
摘要:的運(yùn)維追蹤技巧總結(jié)曾幾何時(shí)我開始運(yùn)維公司的網(wǎng)站,經(jīng)過一段時(shí)間的摸爬滾打,也算是總結(jié)了不少在服務(wù)器下調(diào)試追蹤各種網(wǎng)站錯(cuò)誤的方法。 LNMP的運(yùn)維追蹤技巧總結(jié) 曾幾何時(shí)我開始運(yùn)維公司的LNMP網(wǎng)站,經(jīng)過一段時(shí)間的摸爬滾打,也算是總結(jié)了不少在LNMP服務(wù)器下調(diào)試追蹤各種網(wǎng)站錯(cuò)誤的方法。好記性不如爛筆頭,還是總結(jié)一下吧! 在開始我會(huì)梳理一下我所理解的一個(gè)web請求從發(fā)起到響應(yīng)的各個(gè)階段服務(wù)器和...
摘要:的運(yùn)維追蹤技巧總結(jié)曾幾何時(shí)我開始運(yùn)維公司的網(wǎng)站,經(jīng)過一段時(shí)間的摸爬滾打,也算是總結(jié)了不少在服務(wù)器下調(diào)試追蹤各種網(wǎng)站錯(cuò)誤的方法。 LNMP的運(yùn)維追蹤技巧總結(jié) 曾幾何時(shí)我開始運(yùn)維公司的LNMP網(wǎng)站,經(jīng)過一段時(shí)間的摸爬滾打,也算是總結(jié)了不少在LNMP服務(wù)器下調(diào)試追蹤各種網(wǎng)站錯(cuò)誤的方法。好記性不如爛筆頭,還是總結(jié)一下吧! 在開始我會(huì)梳理一下我所理解的一個(gè)web請求從發(fā)起到響應(yīng)的各個(gè)階段服務(wù)器和...
摘要:是與之間數(shù)據(jù)交換的一種協(xié)議。當(dāng)收到這個(gè)請求后,會(huì)啟動(dòng)對應(yīng)的程序,這里就是的解析器。接下來解析器會(huì)解析文件,初始化執(zhí)行環(huán)境,然后處理請求,再以規(guī)定規(guī)定的格式返回處理后的結(jié)果,退出進(jìn)程,再把結(jié)果返回給瀏覽器。 CGI:是 Web Server 與 Web Application 之間數(shù)據(jù)交換的一種協(xié)議。FastCGI:同 CGI,是一種通信協(xié)議,但比 CGI 在效率上做了一些優(yōu)化。PHP-...
摘要:是與之間數(shù)據(jù)交換的一種協(xié)議。當(dāng)收到這個(gè)請求后,會(huì)啟動(dòng)對應(yīng)的程序,這里就是的解析器。接下來解析器會(huì)解析文件,初始化執(zhí)行環(huán)境,然后處理請求,再以規(guī)定規(guī)定的格式返回處理后的結(jié)果,退出進(jìn)程,再把結(jié)果返回給瀏覽器。 CGI:是 Web Server 與 Web Application 之間數(shù)據(jù)交換的一種協(xié)議。FastCGI:同 CGI,是一種通信協(xié)議,但比 CGI 在效率上做了一些優(yōu)化。PHP-...
閱讀 4766·2021-10-25 09:48
閱讀 3315·2021-09-07 09:59
閱讀 2423·2021-09-06 15:01
閱讀 2848·2021-09-02 15:21
閱讀 2786·2019-08-30 14:14
閱讀 2299·2019-08-29 13:59
閱讀 2603·2019-08-29 11:02
閱讀 2614·2019-08-26 13:33