Wednesday, October 13, 2021

Concurrency on Azure Functions (PHP) with custom handlers

PHP is not among supported languages in Azure Functions, but one can easily implement that with the help of Azure Functions custom handlers. There are several posts over the Internet guiding you how to set up a PHP Function with custom handlers, but one needs to pay attention to the concurrency here, or he/she may easily ends up with HTTP 500.121(Timeout).

 

Performance bottleneck

I created one PHP function app following the guidance from Internet, below is the host.json file. The HTTP trigger function is quite simple, it will only sleep 50 seconds once it is triggered before return. When I send 10 requests almost at the same time, the response time of each requests is increasing, until it reaches 230s(the maximum time IIS server on Azure App Service allowed) and returns HTTP 500.121(Timeout).

 

 

{
  "version": "2.0",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    }
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[2.*, 3.0.0)"
  },
  "customHandler": {
    "description": {
      "defaultExecutablePath": "php",
      "workingDirectory": "",
      "arguments": [
        "-S",
        "127.0.0.1:%FUNCTIONS_CUSTOMHANDLER_PORT%",
        "router.php"
      ],
      "enableForwardingHttpRequest": true
    }
  }
}

 

 

Requests response time:

PreciseTimeStamp

Cs_method

Cs_uri_stem

S_port

Sc_status

Sc_substatus

Sc_win32_status

Time_taken(ms)

2021-09-27 06:28:45.1512587

GET

/api/function1

443

200

0

0

50027

2021-09-27 06:28:45.1722946

GET

/api/function1

443

200

0

0

73530

2021-09-27 06:29:45.1965994

GET

/api/function1

443

200

0

0

123586

2021-09-27 06:30:45.2202146

GET

/api/function1

443

200

0

0

173613

2021-09-27 06:31:45.2311004

GET

/api/function1

443

200

0

0

223630

2021-09-27 06:31:45.2390925

GET

/api/function1

443

500

121

0

230002

2021-09-27 06:32:45.3702086

GET

/api/function1

443

500

121

0

230001

2021-09-27 06:33:45.4016691

GET

/api/function1

443

500

121

0

230003

2021-09-27 06:34:45.4178058

GET

/api/function1

443

500

121

0

230005

 

Root Cause

If you get back and check my host.json again, you will notice that I am launching a PHP built-in web server with the help of Azure Functions custom handlers. The web server runs only one single-threaded process, so PHP applications will stall if a request is blocked.

 

 

php -S 127.0.0.1:%FUNCTIONS_CUSTOMHANDLER_PORT% router.php

 

 

One doc you may reference: 

https://www.php.net/manual/en/features.commandline.webserver.php

 

What I can do

  • If one insists on using Azure Functions in his/her design, then he/she may leverage environmental variable ‘PHP_CLI_SERVER_WORKERS’ and set up a large number to achieve concurrency. This feature is only provided by PHP7.4 on linux. But PHP7.4 on linux is not available on current linux plan, one needs to run his/her function app in a custom linux container which has built that has PHP installed. Please note, even though it can achieve certain concurrency with multiple PHP processes, but it still not recommended to use in production environment due to limited performance.

Attach the Dockerfile below to your reference.

 

 

# To enable ssh & remote debugging on app service change the base image to the one below
FROM mcr.microsoft.com/azure-functions/dotnet:3.0-appservice 
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
    AzureFunctionsJobHost__Logging__Console__IsEnabled=true

#COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"]

RUN apt -y install lsb-release apt-transport-https ca-certificates 
RUN wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
RUN echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | tee /etc/apt/sources.list.d/php.list
RUN apt update && apt install php7.4 -y


COPY . /home/site/wwwroot

 

 

  • The best option is to switch to PHP web app instead of Azure Functions with custom handler. In that way, the PHP module launched by Apache/Nginx server could handle the API requests concurrently.

 

Posted at https://sl.advdat.com/3ayhrlY