<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Volodymyr Dovbenko</title>
    <description>The latest articles on DEV Community by Volodymyr Dovbenko (@nostop8).</description>
    <link>https://dev.to/nostop8</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F511174%2F6cb3da46-1926-44a1-bb85-c30aaaa64b7c.jpg</url>
      <title>DEV Community: Volodymyr Dovbenko</title>
      <link>https://dev.to/nostop8</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nostop8"/>
    <language>en</language>
    <item>
      <title>Optimizing performance of PHP app that sends an external request</title>
      <dc:creator>Volodymyr Dovbenko</dc:creator>
      <pubDate>Mon, 24 May 2021 20:08:07 +0000</pubDate>
      <link>https://dev.to/nostop8/optimizing-performance-of-php-app-that-sends-an-external-request-291p</link>
      <guid>https://dev.to/nostop8/optimizing-performance-of-php-app-that-sends-an-external-request-291p</guid>
      <description>&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Intro&lt;/li&gt;
&lt;li&gt;Optimization Iteration #0&lt;/li&gt;
&lt;li&gt;Optimization Iteration #1&lt;/li&gt;
&lt;li&gt;Optimization Iteration #2&lt;/li&gt;
&lt;li&gt;Exposing more info&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Intro &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I’m running web services API written in PHP and one of the endpoints during every request pings another external web service, particularly Firebase Realtime DB which in its turn can deliver notification to the client through the Websockets API. Below is the image with a rough architecture of this process:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7uoell2p6fcw8fjo975g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7uoell2p6fcw8fjo975g.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
As you can see, any client app (browser) and any server app (some 3rd party) can send a request to the PHP endpoint, then PHP pings the Firebase endpoint and finally the Firebase notifies the client app (browser) about intercepted request.&lt;/p&gt;
&lt;h3&gt;
  
  
  Web service short story
&lt;/h3&gt;

&lt;p&gt;The web service I am running is not very complicated and we can divide the operations is does into the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Processing request&lt;/li&gt;
&lt;li&gt;Doing few SQL queries including insert queries&lt;/li&gt;
&lt;li&gt;Performing POST request to the external Firebase Realtime DB API&lt;/li&gt;
&lt;li&gt;Providing response to the client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After it was completed, I did load tests and profiling. It turned out that posting data to Firebase was taking around 1.6–2 seconds. Therefore number of requests that could be performed simultaneously to my own PHP endpoint was around 300–400 / 1 minute with the response time of more than 2 seconds (PHP app time + Firebase request time). This was a very poor result, so I started to look into improving the performance and particularly the request time to Firebase.&lt;/p&gt;
&lt;h3&gt;
  
  
  A bit of side talk
&lt;/h3&gt;

&lt;p&gt;You might know that any VM is generally limited by 2 main factors: CPU and Memory. When it comes to these limitations, a very important aspect is the time (a relevant time value to our VM capacity) that is required for our application to run in a specific environment. The faster our app runs or in other words the more optimized it is, the more simultaneous instances of our app can be executed. This is especially valid for the PHP scripts.&lt;/p&gt;
&lt;h2&gt;
  
  
  Optimization Iteration #0 &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;First of all, it turned out that the Firebase PHP SDK was making 2 requests all the time. First request to get the access token and second to actually post the data. I found out that there’s a way to use a “database secrets” which is a one-time generated token that you may use to access your Firebase DB. So I just dropped the SDK and used a direct request using CURL interface provided inside the PHP. The time to post data to Firebase decreased approximately by 1 second. So, now I could perform 700–800 requests per 1 minute and the response time was around 1–1.2 seconds. This is better than previous, but still not good enough.&lt;/p&gt;

&lt;p&gt;The reason why Firebase request was taking so long is because PHP waits for the response from the remote server due to its synchronous code execution nature. So all the subsequent code is blocked until the response is received. This piece of functionality with Firebase notifications is not very critical to my application and in case for any reason something goes wrong during the request to Firebase, there’s no need to perform some rollback and I do not actually need to know about it immediately etc. Thus, I decided to speed up the performance by omitting the part where PHP is waiting for the response from the remote server. The PHP should just send a request and does not care about what happens afterwards.&lt;/p&gt;
&lt;h2&gt;
  
  
  Optimization Iteration #1 &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To solve this task I used a simple solution where you can execute external CLI commands using PHP. And yes, cURL does has a CLI interface (tool).&lt;br&gt;
We may present the updated architecture on the diagram below:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv0c23k6b7e5oqzwnonwd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv0c23k6b7e5oqzwnonwd.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
The PHP code combined with the cURL command looks the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$cliCommand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;&amp;lt;&amp;lt;&amp;lt;CODE
curl -k -H "Content-Type: application/json" -d '{"hello": "world"}' -X POST https://&amp;lt;my-db&amp;gt;.firebaseio.com/&amp;lt;my-endpoint&amp;gt;.json?auth=&amp;lt;database_secret&amp;gt; &amp;gt;&amp;gt; /tmp/firebase.log 2&amp;gt;&amp;amp;1 &amp;amp;
CODE;&lt;/span&gt;
&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cliCommand&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This part &lt;code&gt;&amp;gt;&amp;gt; /tmp/firebase.log 2&amp;gt;&amp;amp;1 &amp;amp;&lt;/code&gt; allows to omit the response waiting (code blocking) and instead log it inside the firebase.log file in case I need to check for any possible error later which I implemented later using a cronjob task.&lt;/p&gt;

&lt;p&gt;This simple solution made the endpoint working almost instantly. The response time from the PHP script dropped from 1–1.2 seconds to 150–250ms and now I could perform around 1200–1300 simultaneous requests per 1 minute… Really? I was expecting a bit more. Something was definitely wrong here.&lt;br&gt;
When I checked the htop tool (CPU and memory monitoring tool), I found out that during the load test, curl tools literally eats all the CPU. 100% of CPU power was filled with CURL tasks. I’m not really sure why this little command line application is “so hungry” for the computation power. If you know, please drop a message in the comments below.&lt;/p&gt;
&lt;h2&gt;
  
  
  Optimization Iteration #2 &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Anyway, I started to search for some alternatives. Among CLI tools there’s nothing better than curl. So, I’ve decided to try another interface, particularly HTTP interface by experimenting with NodeJS (Express). Javascript is an asynchronous programming language and Node runs it very efficiently along with Express. I’ve created a small JS script using 2 extra libraries: Express and HTTP. It is basically an asynchronous proxy that listens for HTTP requests and forwards data to Firebase Realtime DB endpoint. Because we access the NodeJS script through the HTTP interface, instead of using exec() method on the PHP side, I had to switch to sockets, particularly to fsocketopen(), fwrite() and fclose(). Sockets allow to send ping requests from PHP without waiting for the response. You might want to ask why in the hell I need the NodeJS then? Well, if I use fsocketopen for sending request directly to the remote web server (Firebase) in a different network and region and for sending request to the local web server (NodeJS) that sits on the same machine — that’s 2 totally different things in terms of timing. Also I can run my local Express server without SSL, but Firebase can run using only HTTPS, thus fsocketopen would have to spend additional time to send an extra background request for the SSL handshake. Thus, yes, there’s a great benefit in using fsocketopen in order to simply open some another thread on some different local interface.&lt;br&gt;
Anyway, this is the new architecture diagram I came to:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffiio7xe57vava565841j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffiio7xe57vava565841j.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here are the performance optimization results. CPU load went down from 100% to 40–50% max. The memory was almost on the same level, roughly 50–60%. The response time was 150–250 ms. And finally the number of simultaneous requests I could execute to the endpoint skyrocketed up to 5500 requests per 1 minute.&lt;/p&gt;
&lt;h2&gt;
  
  
  Exposing more info &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Testing Environment
&lt;/h3&gt;

&lt;p&gt;Finally, revealing the environment I used for those tests. It is EC2 t2.micro instance (1 CPU and 1GB of memory). BTW, MySQL DB is on the separate VM instance, which saves the VM resources greatly. For load tests I was using Apache jMeter and a default thread properties, which are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;number of threads (users) equals to 10&lt;/li&gt;
&lt;li&gt;ramp-up period equals to 1 second.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Code snippets
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;PHP script that sends request to the NodeJS script using the fsocketopen&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'http://localhost:3000/prod'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// URL to the NodeJS script, could be also http://localhost:3000/dev&lt;/span&gt;
&lt;span class="nv"&gt;$urlParts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;parse_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$jsonData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{"hello": "world"}'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$fp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;fsockopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$urlParts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'host'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$urlParts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'port'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$errno&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$errstr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$fp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// good place to log your error&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"POST "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$urlParts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'path'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;" HTTP/1.1&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$out&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Host: localhost&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$out&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$out&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Content-Length: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$jsonData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$out&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Connection: Close&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$out&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$jsonData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$out&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;fclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// good place to log your error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;NodeJS script which forwards request asynchronously to Firebase&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;environments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;my-db-dev&amp;gt;.firebaseio.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;my-dev-token&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;prod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;my-db-prod&amp;gt;.firebaseio.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;my-prod-token&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;postFirebase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;envName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;environments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;envName&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;envName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; not found`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;environments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;envName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/&amp;lt;my-endpoint&amp;gt;.json?auth=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Length&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;postFirebase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Listen on port 3000&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Server started on port 3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary &lt;a&gt;&lt;/a&gt;&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To summarize, there’s always a room for improving and optimizing the code and its efficiency. I’ve managed to improve the time required to run the API endpoint from 2.2 seconds to 0.2 (11x times). As for the number of simultaneous requests, the improvement is more than 13x times (from 300–400 requests per minute and up to 5500). NodeJS performed much better than a CLI’s curl tool in terms of consuming CPU and memory resources. Therefore the pair of “fsocketopen() / NodeJS” works much better than “exec() / curl” if you want to initiate another thread from within the PHP to ping some external resource or web service.&lt;/p&gt;

&lt;p&gt;Thanks for reading. Please let me know if you have an idea why curl requires so much of CPU resources to send requests comparing to NodeJS? Also it is interesting if there’s any other good option instead of NodeJS to create a small proxy over the HTTP protocol to send requests asynchronously (e.g. Python?) and do you think it can perform better? Thanks ahead for your thoughts!&lt;/p&gt;

&lt;p&gt;P.S. This article was also posted on my another blog here: &lt;a href="https://nostop8.medium.com/optimizing-performance-of-php-app-that-sends-an-external-request-86e2958bfece" rel="noopener noreferrer"&gt;https://nostop8.medium.com/optimizing-performance-of-php-app-that-sends-an-external-request-86e2958bfece&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>firebase</category>
      <category>node</category>
      <category>performance</category>
    </item>
    <item>
      <title>Why would I use mock API while coding APP or another API?</title>
      <dc:creator>Volodymyr Dovbenko</dc:creator>
      <pubDate>Wed, 27 Jan 2021 19:29:11 +0000</pubDate>
      <link>https://dev.to/nostop8/why-would-i-use-mock-api-while-coding-app-or-another-api-3o5g</link>
      <guid>https://dev.to/nostop8/why-would-i-use-mock-api-while-coding-app-or-another-api-3o5g</guid>
      <description>&lt;p&gt;As a software engineer with an experience of many years, I had worked with a dummy API or fake API a lot of times. Luckily or now I think unfortunately I had this experience only in expected cases. For instance the client app and web services were developing simultaneously and while developing client apps we were using predefined mocks with the similar data to what was expected from the real web service in near future. Such mocks were served either by internal service methods, files or by the local mock servers.&lt;/p&gt;

&lt;p&gt;But recently we’ve got a project where most of the development process flows were totally not as expected. Our back-end app is supposed to communicate with a 3rd party through one more API, basically a Proxy API. This proxy API was developed by another SD company. BTW, below you may see the diagram of the server apps (APIs) and how they communicate:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Furqgmf1vswjtpsjzjez2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Furqgmf1vswjtpsjzjez2.png" alt="Communication flow diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why such a weird API communication flow? This another SD company was a trusted partner of the 3rd party company for a long period. So, this Proxy API was basically a security solution, another security layer between us and a 3rd party. And that’s where things got wrong. The Proxy API company was way too slow with web services development, moreover the 3rd party was changing the requirements over and over again, so even we could not catch up with everything on time. So imagine the situation where you have 3 server apps, with a direct flow of data between them for instance 1 -&amp;gt; 2 -&amp;gt; 3 -&amp;gt; 2 -&amp;gt; 1 or vise versa. For communication between app #1 and app #2 we were using REST API services and webhooks for instant notifications. As for the communication between app #2 and app #3 - we did not care :) Probably they were using some SOAP protocol because the app #3 is very ancient and obviously untrendy.&lt;/p&gt;

&lt;p&gt;Anyway, our API specifications were always changing and more than 50% of the web services were “in dev” status constantly. Obviously in order to continue development of our app without blockers we had to cover gaps of endpoints in a form of mock API or API stubs. We started as usual with a local approach by adding abstraction layers for communication with the 3rd party API, classes with dummy data, files, and local configs to switch between fake data or real endpoint. But switching between fake and real web service or vise versa using in-code configs all the time was not very handy, especially for UAT tests. What is more important, we had troubles with webhook notifications. We could do an end-to-end test properly only on the staging environments which is really frustrating.&lt;/p&gt;

&lt;p&gt;So, we started to search for some existing solutions to our problems with API mocking. Our main requirement was gradually switching from dummy API to real API or even backwards when needed using some UI. Another important requirement was to allow end-to-end tests when we are running 2 server apps on 2 different local environments in totally different locations. After going through lots of existing tools, we’ve found one that matched all requirements we had. That seems to be a very brand new API mock tool called &lt;a href="https://quickmocker.com" rel="noopener noreferrer"&gt;QuickMocker&lt;/a&gt; (&lt;a href="https://quickmocker.com" rel="noopener noreferrer"&gt;https://quickmocker.com&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The greatest benefit across all other similar mocking tools was a Local Forwarder feature that allowed us to perform end-to-end tests even from a local environment. We had some trouble setting it up, because your API webhooks which are used only for receiving requests from the server app will need the CORS and OPTIONS method to be enabled. Obviously it is not a usual case to have CORS and OPTIONS in place for the server to server communication, but that was a “small price” for enabling communication between 2 server applications that are located on 2 different “localhosts” without any public DNS or IP address.&lt;/p&gt;

&lt;p&gt;Another reason why we pick-up QuickMocker among all others was the Proxy and a nice UI to configure which endpoints should work as a proxy and which can be left as dummy API endpoints. Obviously it's not something unique and many other tools have this feature. But the nice thing is that all that we needed was in 1 single place!&lt;/p&gt;

&lt;p&gt;Finally, QuickMocker recently added support for OpenAPI import. Because we use Swagger to define our web services, the import function might become handy. Though, we still need to test it.&lt;/p&gt;

&lt;p&gt;Yeah, last but not least, QuickMocker has a pretty decent support for response templating with a lot of options to generate random and faker data. They support URL path patterns to match the request URL using Regular Expressions.&lt;/p&gt;

&lt;p&gt;I think that’s all the features that we’ve used and you might find more out there. If you haven’t used API mocking solutions yet, QuickMocker could be a good start for you. And if you had opportunities to create some mock API already, I think &lt;a href="https://quickmocker.com" rel="noopener noreferrer"&gt;QuickMocker&lt;/a&gt; could be an interesting alternative for you.&lt;/p&gt;

</description>
      <category>api</category>
      <category>developer</category>
      <category>webdev</category>
      <category>development</category>
    </item>
  </channel>
</rss>
