Quand j'ai écrit Article précédent, la réponse de l'environnement sans serveur que j'ai construit moi-même était lente. Il y avait une sensation de peau. J'imaginais que ce serait plus lent qu'un serveur général car le conteneur a été démarré en utilisant Docker en interne. Mais dans quelle mesure la vitesse de réponse se détériore-t-elle? Il n'y a pas beaucoup d'articles comparatifs, j'ai donc décidé de le mesurer.
Bref aperçu du domaine technique de l'architecture sans serveur
Selon l'article
Est écrit. Cela signifie que dans un environnement sans serveur similaire, on peut prévoir que le temps de réponse sera à peu près le même ou un peu plus lent.
Nous utilisons un environnement sans serveur qui s'exécute sur site appelé Iron Functions. À ce sujet, j'ai écrit un article d'introduction dans le passé, alors jetez un œil là-bas.
En gros, c'est un produit pratique qui vous permet d'introduire facilement un environnement sans serveur tel qu'AWS Lambda.
Cette fois, le langage utilisé pour le benchmark est Go, Node.js et Python. Dans chaque langue, écrivez un code qui fonctionne presque de la même manière. Voyons quelle différence il y a lors de leur exécution sur Serverless et sur des serveurs HTTP intégrés pour chaque langue (natif).
Go Serverless
package main
import (
"encoding/json"
"fmt"
"os"
)
type Person struct {
Name string
}
func main() {
p := &Person{Name: "World"}
json.NewDecoder(os.Stdin).Decode(p)
fmt.Printf("Hello %v!", p.Name)
}
Go Native
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type Person struct {
Name string
}
func handler(w http.ResponseWriter, r *http.Request) {
p := &Person{Name: "World"}
json.NewDecoder(r.Body).Decode(p)
fmt.Fprintf(w, "Hello %v!", p.Name)
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":2000", nil)
}
Node.js Serverless
name = "World";
fs = require('fs');
try {
obj = JSON.parse(fs.readFileSync('/dev/stdin').toString())
if (obj.name != "") {
name = obj.name
}
} catch(e) {}
console.log("Hello", name, "from Node!");
Node.js Native
const http = require('http');
name = "World";
http.createServer(
(req, res) => {
var body = "";
req.on(
"data",
(chunk) => { body+=chunk; }
);
req.on(
"end",
() => {
obj = JSON.parse(body);
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello ' + obj.name + " from Node Native!");
}
);
}
).listen(6000);
Python Serverless
import sys
sys.path.append("packages")
import os
import json
name = "World"
if not os.isatty(sys.stdin.fileno()):
obj = json.loads(sys.stdin.read())
if obj["name"] != "":
name = obj["name"]
print "Hello", name, "!!!"
Python Native
from http.server import BaseHTTPRequestHandler,HTTPServer
from json import loads
from io import TextIOWrapper
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
content_length = int(self.headers.get('content-length'))
text = TextIOWrapper(self.rfile).read(content_length)
self.send_response(200)
self.send_header('Content-type','text/plain')
self.end_headers()
obj = loads(text)
self.wfile.write("Hello {name} !! Welcome to Native Python World!!".format(name=obj["name"]).encode("utf-8"))
PORT = 1000
server = HTTPServer(("127.0.0.1", PORT), Handler)
print("serving at port", PORT)
server.serve_forever()
Le serveur de chaque benchmark s'exécute sur la même machine. Ubuntu 16.04 sur une machine virtuelle avec 1 cœur et 2 Go de mémoire. Le serveur qui place la charge et le serveur qui reçoit la charge sont les mêmes et utilisent Apache Bench. Préparez les json et
johnny.json
{
"name":"Johnny"
}
La charge est appliquée en lançant le post-traitement avec Apache Bench. Le nombre de requêtes est de 100 et le nombre de parallèles est de 5. (Le nombre de demandes est petit sera décrit plus tard) À ce moment, le temps jusqu'à ce que la réponse soit renvoyée du serveur (temps de réponse) est mesuré.
#XXXX/XXXX complète le cas échéant
ab -n 100 -c 5 -p johnny.json -T "Content-Type: application/json" http://localhost:XXXXX/XXXXX
Response Time | min[ms] | mean[ms] | std[ms] | median[ms] | max[ms] | Ratio natif(mean) |
---|---|---|---|---|---|---|
Go Serverless | 3951 | 6579 | 1010 | 6512 | 8692 | 1644.75 |
Go Native | 0 | 4 | 5 | 2 | 37 | - |
Node Serverless | 5335 | 14917 | 3147 | 15594 | 20542 | 621.54 |
Node Native | 5 | 24 | 45 | 12 | 235 | - |
Python Serverless | 5036 | 13455 | 4875 | 14214 | 29971 | 840.94 |
Python Native | 6 | 16 | 4 | 16 | 26 | - |
** Veuillez noter que l'axe vertical de la figure ci-dessous est une échelle logarithmique (la relation d'amplitude est exprimée en logarithme pour une meilleure compréhension) **
Comme vous pouvez le voir dans le tableau, avec ** Go, l'environnement sans serveur est plus de 1600 fois plus lent que l'environnement natif. Vous pouvez voir que les autres Node.js sont 600 fois plus lents et Python 800 fois plus lent. ** ** Si vous comparez les résultats de Python et Node.js, vous pouvez trouver étrange que Python soit plus rapide. Lorsque j'ai essayé un test de suivi dans l'environnement natif, Python était parfois plus rapide lorsque le nombre de requêtes était petit et le nombre de parallèles était petit. Lorsque le nombre de requêtes est supérieur ou égal à 10000, Node.js peut traiter de manière plus stable et le traitement se termine plus rapidement que Python. De plus, l'implémentation native de Python entraînait parfois une erreur et la requête ne pouvait pas être traitée normalement. Probablement, Go, qui a une différence de vitesse avec ce nombre de requêtes, est réglé de manière anormale. Ici, je voudrais le comparer avec le "AWS Lambda a un temps de traitement de 250 ms à 8 000 ms" mentionné ci-dessus. Le résultat de cette référence était un résultat avec peu d'inconfort personnellement. Quand j'ai fait une demande à Iron Functions par moi-même avec Curl, je me sentais "lent", et si je fais un Docker pour chaque demande, je pense qu'il n'y a aucune aide pour cela. En revanche, j'avais l'impression que les 250ms d'AWS Lambda sont très rapides.
En regardant, AWS Lambda semble avoir deux types de méthodes de démarrage, démarrage à froid et démarrage à chaud. Le premier n'est pas différent du sans serveur intuitif, et l'image est qu '"un conteneur est créé pour chaque requête", et le second semble être "un conteneur une fois créé est réutilisé". De cette manière, l'implémentation Lambda est plus rapide car elle risque de ne pas créer de conteneur. Il paraît que. Je pense que c'est la raison pour laquelle nous pouvons répondre en 250 ms au plus vite. D'un autre côté, Iron Functions n'implémente probablement que le démarrage à froid, donc je pense que ce n'est pas si rapide. Cependant, lorsque Go est exécuté sur Serverless dans un environnement créé par moi-même, je pense que Max d'environ 8600ms est une bonne vitesse de traitement. Bien sûr, il y a une différence dans le nombre de clients traités, mais la vitesse de création / élimination des conteneurs n'est-elle pas réellement la même? J'ai pensé.
Vous trouverez ci-dessous un lien vers la liste de prix AWS Lambda.
Tarification AWS Lambda https://aws.amazon.com/jp/lambda/pricing/
Le plan tarifaire semble être facturé en fonction du temps d'utilisation * de l'utilisation de la mémoire, et il semble que vous ne puissiez pas sélectionner le processeur. Alors, comment le CPU est-il alloué? Il a été écrit dans Aide.
Et un autre article, l'article suivant
L'histoire de l'introduction en production du Framework Serverless
C'était énoncé. J'ai fait quelques tests d'environnement sans serveur, mais l'utilisation du processeur de la machine hôte augmente considérablement. Par conséquent, je me demandais comment le côté hôte (Amazon) pourrait payer, mais il semble que le prix unitaire horaire soit élevé du côté Amazon. De plus, sans serveur est essentiellement conçu pour que la réponse ne puisse être renvoyée qu'après le démarrage et la suppression du conteneur, je me demande donc si le processeur est réglé sur un bon processeur pour une réponse à haute vitesse. J'ai pensé.
Normalement, le nombre de points de repère est de 10 000, ou je pense que certains modèles sont exécutés. Cependant, dans cette expérience, le nombre de requêtes est limité à environ 100. Il y a deux raisons. La première est qu'elle est «lente». Lors de l'exécution avec Serverless, cela prend environ 4000 ms au plus rapide. Par conséquent, nous n'avons pas évalué les demandes à grande échelle parce que ce n'était pas réaliste. Le second est parce qu'il est "instable". Iron Functions a un comportement instable. Par conséquent, même si la demande est environ 100 fois, elle peut échouer environ 10 fois. Par conséquent, si vous augmentez le nombre de parallèles ou le nombre de requêtes, il y a une forte probabilité que le traitement ne soit pas possible. Cela semble également dépendre du cycle de vie du conteneur Doquer de Iron Functions, et c'était un produit qui avait expiré ou pas même si la même demande était envoyée, et il était difficile d'obtenir une valeur précise. Par conséquent, les données décrites dans cet article suivent la valeur de temps de traitement elle-même. Je pense plutôt qu'il est plus exact de reconnaître qu'il y a un ordre de temps de traitement par rapport à Native. De plus, le fait que la machine qui applique la charge et la machine qui reçoit la charge soient les mêmes peut être un point de repère légèrement inexact. C'est simplement que je n'ai pas préparé deux environnements, mais si vous regardez la différence de vitesse entre l'implémentation native et l'implémentation sans serveur, cela peut ne pas poser de problème si vous ne gardez que l'état du serveur. .. Je pense.
Cette référence a pris un temps très long. L'affichage a été considérablement retardé car il a fallu 3 à 4 heures pour demander au maximum 600 fois. Et la lenteur de Serverless est devenue un résultat vraiment frappant. Utilisons-le. J'ai pensé, mais je devrais arrêter ça pendant un moment ... D'un autre côté, AWS Lambda est excellent ... Et la vitesse du serveur http de Go est incroyable. Je n'ai jamais pensé que même un si petit banc serait rapide. De plus, Iron Functions est douloureux car il n'a presque pas de savoir-faire japonais. En fait, il existe un proxy inverse appelé fnlb, et une méthode de clustering par celui-ci est également officiellement préparée. Ensuite, ce sera plus facile à mettre à l'échelle. Bien que je pense que, l'opération en elle-même est trop lente, il peut donc être essentiel de régler davantage ou d'améliorer le goulot d'étranglement. Iron Functions lui-même est écrit en Go en premier lieu, donc ça ne devrait pas être aussi lent, mais ... je me demande si c'est autour du conteneur docker ... Hmm. Il y a un long chemin à parcourir sans serveur.
Recommended Posts