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.