HTTP(s) Client Performance von PHP, Go und Java

Möchte man einen Backend Service oder ein Programm schreiben das Inhalte von einer Webseite abruft oder mit einer Webschnittstelle kommuniziert ist häufig die Performance ein wichtiges Kriterium. Den hier werden meist nicht ein, zwei oder drei HTTP(s) Aufrufe abgearbeitet sondern viele hundert oder tausend. So multiplizieren sich einige hundertstel Millisekunden Unterschied bei der Request Abwicklung schnell zu Sekunden oder Minuten.

Um diese Unterschiede zu ermitteln führe ich im folgenden einen Performance Test durch bei dem über eine HTTPS Verbindung eine Webseite n-Mal aufgerufen wird und der entsprechende Client diese auf der Standardausgabe ausgibt.

In den Ring steigen PHP 5.6.11, PHP 7.0.0beta1, Go1.4.2 und Java 1.8.0_25. Die Clients laufen auf einem MacBook Pro mit 2.8 GHz Intel Core i5 aus der Serie von Mitte 2014. Ich habe darauf geachtet das zur Laufzeit mehr als die notwendigen Ressourcen wie zum Beispiel Arbeitsspeicher zur Verfügung stehen. Das Ziel ist ein Webcluster mit genügend Ressourcen und Netzwerkanbindung. Engpass wäre hier eher Das MacBook Pro welches an ein gewöhnliches DSL 16000 angeschlossen ist.

Die HTTP(s) Clients sind eine Eigenentwicklung. Ich habe dabei darauf geachtet nach Möglichkeit die Standart Extensions bzw. Bibliotheken ohne weitergehende Konfiguration zu benutzen sowie den Code möglichst einfach zu halten.

PHP

<?php

class Client
{
    private $_argc;
    private $_argv;

    public function __construct($argc, $argv)
    {
        $this->_argc = $argc;
        $this->_argv = $argv;
    }

    public function request()
    {
        if ($this->_argc == 3)
        {
            for($i = $this->_argv[2]; $i >= 0; $i--)
            {
                echo file_get_contents($this->_argv[1]);
            }
        }
    }
}

$client = new Client($argc, $argv);
$client->request();

Go

package main

import "fmt"
import "os"
import "strconv"
import "net/http"
import "io/ioutil"

func request(uri string) {
    resp, err := http.Get(uri)
    if err != nil {}
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    fmt.Print(string(body))
}

func main() {
    var args = os.Args
    if (len(args) == 3) {
        var counter, err = strconv.ParseInt(args[2], 10, 64)
        if err == nil {
            for i := counter; i > 0; i-- {
                request(args[1])
            }
        }
    }
}

Java

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
 
import javax.net.ssl.HttpsURLConnection;
 
public class Client {
 
	private final String USER_AGENT = "Mozilla/5.0";
 
	public static void main(String[] args) throws Exception {
 		if (args.length == 2) {
 			for (int i = Integer.parseInt(args[1]); i > 0; i--) {
				URL obj = new URL(args[0]);
				HttpURLConnection con = (HttpURLConnection) obj.openConnection();
				con.setRequestMethod("GET");
				int responseCode = con.getResponseCode();
				BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
				String inputLine;
				StringBuffer response = new StringBuffer();
				while ((inputLine = in.readLine()) != null) {
					response.append(inputLine);
				}
				in.close();
				System.out.println(response.toString());
			}
		}
	}
 
}

Da bei PHP kein signifikanter Zeitunterschied zwischen einem durchgeführten Request mit der Standart Funktion file_get_contents und der Curl Extension zu messen war, führe ich letztere hier nicht auf.

Mit jedem Client werden 5 Messungen mit je 10, 50 und 100 Requests durchgeführt. Die Zeit wird mittels time Programm von der Kommandozeile ermittelt. Weiterführende Werte sind rechnerisch ermittelt.

Meine Erwartungshaltung ist aufgrund der Designs der Sprachen das sich Go auf Platz eins befindet gefolgt von Java und PHP 7 und PHP 5.6 die Schlussplätze belegen. Den Fokus will ich deshalb auf die Differenz legen.

Vor den Messergebnissen noch je ein Beispiel Aufruf für jede Sprache

time php client.php https://www.pcspezialist.de 10 >> /dev/null
time go run client.go https://www.pcspezialist.de 10 >> /dev/null
time java Client https://www.pcspezialist.de 10 >> /dev/null

Es ergeben sich die folgenden Ergebnisse für die durchgeführten Tests

Legende

Sprache – Die verwendete Programmiersprache
M1-M5 – Benötigte Zeit in Sekunden für diese Messreihe
MW – Mittelwert für diese Messreihe in Sekunden
AzsPS – Abstand zur schnellsten Programmiersprache (in Sekunden)
AzsPP – Abstand zur schnellsten Programmiersprache (in Prozent)
FzvM – Faktor zur vorherigen Messung (der selben Programmiersprache)

Die Genauigkeit beträgt vier Nach-Komma-Stellen.

10 Requests

[table]
Sprache,M1,M2,M3,M4,M5,MW,AzsPS,AzsPP,FzvM
Go,0.987,0.828,0.814,0.811,1.021,0.8922,0,0,-
Java,1.423,1.315,1.366,1.231,1.268,1.3206,0.4284,+48%,-
PHP 7,2.500,2.485,2.447,2.461,2.465,2.4716,1.5794,+177%,-
PHP 5.6,2.481,2.489,2.472,2.469,2.481,2.4784,1.5826,+177%,-
[/table]

50 Requests

[table]
Sprache,M1,M2,M3,M4,M5,MW,AzsPS,AzsPP,FzvM
Go,1.967,1.895,1.906,1.900,1.898,1.9132,0,0,2.1442
Java,4.688,4.310,4.184,4.132,4.162,4.2952,2.3820,+124%,3.2524
PHP 7,11.433,11.328,11.353,11.416,11.366,11.3792,9.4660,+494%,4.6039
PHP 5.6,11.791,12.004,12.245,11.461,11.345,11.7692,9.8560,+515%,4.7487
[/table]

100 Requests

[table]
Sprache,M1,M2,M3,M4,M5,MW,AzsPS,AzsPP,FzvM
Go,3.326,3.373,3.303,3.276,3.235,3.3026,0,0,1.7263
Java,7.743,8.103,7.569,8.233,7.559,7.8414,4.5388,+137%,1.7418
PHP 7,24.314,24.067,23.890,24.089,25.023,24.2766,20.974,+635%,2.1334
PHP 5.6,22.430,24.232,24.204,23.965,23.944,23.755,20.4529,+619%,2.0184
[/table]

Daraus ergeben sich für Go 0.0381 Sekunden/Request, für Java 0.0841 Sekunden/Request, für PHP 7 0.2382 Sekunden/Request und für PHP 5.6 0.2375 Sekunden/Request.

Mit diesen Werten lässt sich nun für vergleichbare Systeme eine ungefähre Zeiteinschätzung berechnen wie lange man für n Requests braucht.

Leave a Reply

Your email address will not be published. Required fields are marked *