راه‌اندازی استک نرم‌افزاری MySql/Asp.Net Core/PHP/Redis توسط داکر (۲)

در تکمیل مستند قبلی در این قسمت می خوایم یک سرور PHP بالا بیاریم که Request رو از کاربر بگیره و به سرور ASP.NET Core مون Request بزنه و اطلاعات رو دریافت کنه! در مرحله بعدی Cache رو بهش اضافه می کنیم.در فایل docker-compose.yml خطوط زیر رو پاک میکنیم و می خواهیم از سرور PHP بهش Request بزنیم.ports: - &quot5000:80&quotهم چنین باید کد های زیر را اضافه کنیم:php: build: context: ./php/ depends_on: - aspnetcore ports: - 10:80 volumes: - ./php/src:/var/www/html/حالا باید image عه php:7.4-apache رو به Dockerfile معرفی کنیم .یک فایل htaccess. درون پوشه php ایجاد میکنیم که وظیفش اینه که همه ی Request هایی که به این فولدر (و ساب فولدرها ) میاد رو بده به index.php !محتوای فایل htaccess. به صورت زیر خواهد بود:RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.+)$ index.php [QSA,L]برای اینکه فایل htaccess مون بتونه کار کنه و apache این اجازه رو بده ، توی Dockerfile موندستور RUN a2enmod rewrite رو اضافه میکنیم. در نهایت محتوای php/Dockerfile به صورت زیر خواهد بود :FROM php:7.4-apache RUN a2enmod rewriteفایل php/src/CurlHttp.php یک فایل php هست که می تونه به سرورمون Request های GET و POST بزنه . محتوای این فایل به صورت زیر خواهد بود:<?php class CurlHttp { private $base; public function __construct($base) { $this->base = $base; } public function request($type, $url, $params = []) { switch (strtoupper($type)) { case &quotGET&quot: return $this->getReq($url, $params); break; case &quotPOST&quot: return $this->postReq($url, $params); break; default: throw new Exception(&quotUndefined Request Type&quot); } } private function getReq($url, $params = []) { $ch = curl_init($this->generateUrl($url)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, 0); $response = curl_exec($ch); curl_close($ch); return $response; } private function postReq($url, $params = []) { $ch = curl_init($this->generateUrl($url)); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params)); curl_setopt( $ch, CURLOPT_HTTPHEADER, array('Content- Type:application/json')); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, 0); $response = curl_exec($ch); curl_close($ch); return $response; } private function generateUrl($url) { return $this->base.&quot/&quot.$url; } }فایل دیگری که ایجاد کردیم Router.php هست که برای مدیریت کردن Routing استفاده میشه و محتوای آن به صورت زیر خواهد بود:<?php class Router { private $request; private $supportedMethods = [ &quotGET&quot, &quotPOST&quot ]; private $routes; public function __construct($request) { $this->request = $request; $this->bootstrapRoutes(); } public function get($url, $callback) { $this->routes['GET'][$url] = $callback; } public function post($url, $callback) { $this->routes['POST'][$url] = $callback; } public function resolve($url, $method) { header('Content-Type: application/json'); if (!isset($this->routes[strtoupper($method)])) { echo json_encode(&quotUnsupported Http Method&quot); return; } $callback = $this->routes[strtoupper($method)][$url] ?? null; if (!$callback) { echo json_encode(&quotUnsupported Route&quot); return; } echo call_user_func_array($callback, [$this->request]); } private function bootstrapRoutes() { foreach ($this->supportedMethods as $method) { $this->routes[$method] = []; } } }` فایل دیگری که در گذشته نیز به آن اشاره کردیم index.php هست که Request ها رو دریافت می کنه و پارامترهایی که با json به آن ارسال شده رو می گیره و یک Router می سازه . بعد چک می کنیم که Request زده شده به notes از نوع GET است یا POST . اگر از نوع GET بود یک instance از CurlHttp میسازیم base address مون هم aspnetcore قرار میدیم و در نهایت به aspnetcore مون یک Request از نوع GET می زنیم. اگر Request زده شده به notes از نوع POST باشه هم مانند GET عمل می کنیم با این تفاوت که PostData هارو ست میکنیم و بعد به aspnetcore مون Request از نوع POST می زنیم. توجه داریم که در این جا با استفاده از نام Container می تونیم Request مون رو ارسال کنیم و لزومی به داشتن IP نیست! در نهایت محتویات فایل index.php به صورت زیر خواهد بود:<?php include &quotCurlHttp.php&quot include &quotRouter.php&quot define(&quotASP_CONTAINER&quot, &quotaspnetcore&quot); $url = $_SERVER['REQUEST_URI']; $request = json_decode(file_get_contents('php://input'), true); $router = new Router($request); $router->get('/notes', function ($request) { $curl = new CurlHttp(ASP_CONTAINER); return $curl->request('GET', 'notes'); }); $router->post('/notes', function ($request) { $curl = new CurlHttp(ASP_CONTAINER); $postData = ['title' => $request['title'],'text'=> $request['text'], ]; return $curl->request('POST', 'notes', $postData); }); $router->resolve($url, $_SERVER['REQUEST_METHOD']);تو این بخش می خوایم Cache رو اضافه کنیم. برای استفاده از افزونه Redis عه php یعنی predis به Composer نیاز داریم . command عه composer install فایل package.json که در کنار فایل php قرار داره رو می خونه و package های مورد نیاز رو نصب میکنه. نکته قابل توجه اینه که پارامتر depends-on برابر php قرار گرفته چون اول باید Container عه php بالا بیاد و بعد دستور composer install اجرا بشه! از image عه Redis هم استفاده می کنیم و یک Container براش می سازیم. بنابراین محتوای فایل docker-compose.yml به صورت زیر خواهد بود:php: container_name: amadev-php build: context: ./php/ depends_on: - aspnetcore - redis ports: - 10:80 volumes: - ./php/src:/var/www/html/ composer: container_name: amadev-composer image: composer:1.9 command: [&quotcomposer&quot, &quotinstall&quot] depends_on: - php volumes: - ./php/src:/app redis: container_name: amadev-redis image: redis:latestو اما فایل index.php:اول predis رو لود کردیم و یک instance از Predis\Client می گیریم . پارامتر host رو برابر redis قرار دادیم چون همونطور که گفته شد با داشتن نام container میتونیم بهش دسترسی داشته باشیم و قبل تر container مون رو با این نام ایجاد کرده بودیم.حالا برای شبیه سازی caching یک متغیر redisKey تعریف کردیم و یک مقدار پیش فرضی رو درونش قرار دادیم. json_decode($redis->get($redisKey))اگر خروجی کد بالا Null باشه یعنی redisKey ست نشده و اون دیتا رو تو cache مون نداریمش (یا تا به حال ست نشده یا اینکه expire شده ) بنابراین به مدت 30 ثانیه دیتا رو cache میکنیم.در نهایت محتوای فایل index.php به صورت زیر خواهد بود:<?php include &quotCurlHttp.php&quot include &quotRouter.php&quot include &quotvendor/predis/predis/autoload.php&quot define(&quotASP_CONTAINER&quot, &quotaspnetcore&quot); $url = $_SERVER['REQUEST_URI']; $request = json_decode(file_get_contents('php://input'), true); $router = new Router($request); $router->get('/notes', function ($request) { $curl = new CurlHttp(ASP_CONTAINER); $redis = new \Predis\Client([ 'scheme' => 'tcp', 'host' => 'redis', 'port' => '6379', ]); $redisKey = 'notes-key'; $result = [ 'from_cache' => true, 'data' => json_decode($redis->get($redisKey)), ]; if (!$result['data']) { $data = $curl->request('GET', 'notes'); $result = [ 'from_cache' => false, 'data' => json_decode($data), ]; $redis->set($redisKey, $data); $redis->expire($redisKey, 30); } return json_encode($result); }); $router->post('/notes', function ($request) { $curl = new CurlHttp(ASP_CONTAINER); $postData = [ 'title' => $request['title'], 'text' => $request['text'], ]; return $curl->request('POST', 'notes', $postData); }); $router->resolve($url, $_SERVER['REQUEST_METHOD']);`