http - the - ¿Por qué ApacheBench aplasta mi servidor Hello World go?
golang step by step (1)
package main
import (
"io"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello world!/n")
}
func main() {
http.HandleFunc("/", hello)
http.ListenAndServe(":8000", nil)
}
Tengo un par de servidores HTTP increíblemente básicos, y todos ellos presentan este problema.
$ ab -c 1000 -n 10000 http://127.0.0.1:8000/
This is ApacheBench, Version 2.3 <$Revision: 1604373 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
apr_socket_recv: Connection refused (61)
Total of 5112 requests completed
Con un valor de concurrencia menor, las cosas aún se caen. Para mí, el problema parece aparecer alrededor de la marca 5k-6k generalmente:
$ ab -c 10 -n 10000 http://127.0.0.1:8000/
This is ApacheBench, Version 2.3 <$Revision: 1604373 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
apr_socket_recv: Operation timed out (60)
Total of 6277 requests completed
Y, de hecho, puede eliminar la concurrencia por completo y el problema aún (a veces) ocurre:
$ ab -c 1 -n 10000 http://127.0.0.1:8000/
This is ApacheBench, Version 2.3 <$Revision: 1604373 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
apr_socket_recv: Operation timed out (60)
Total of 6278 requests completed
No puedo evitar preguntarme si estoy alcanzando algún tipo de límite del sistema operativo en alguna parte. ¿Cómo lo diría? ¿Y cómo mitigaría?
En resumen, te estás quedando sin puertos.
El rango de puerto efímero predeterminado en osx es 49152-65535, que es solo 16,383 puertos.
Como cada solicitud
ab
es
http/1.0
(sin keepalive en sus primeros ejemplos), cada nueva solicitud toma otro puerto.
A medida que se utiliza cada puerto, se pone en una cola donde espera el tcp "Duración máxima del segmento", que está configurado para ser 15 segundos en osx.
Entonces, si usa más de 16,383 puertos en 15 segundos, el sistema operativo lo estrangulará en conexiones adicionales.
Dependiendo de qué proceso se quede sin puertos primero, obtendrá errores de conexión del servidor o se bloquea desde
ab
.
Puede mitigar esto usando un generador de carga compatible con
http/1.1
como
wrk
, o usando la opción keepalive (
-k
) para
ab
, de modo que las conexiones se reutilicen según la configuración de concurrencia de la herramienta.
Ahora, el código del servidor que está comparando hace tan poco, que el generador de carga está sujeto a impuestos tanto como el servidor en sí, con el sistema operativo local y la pila de red haciendo una buena contribución. Si desea comparar un servidor http, es mejor hacer un trabajo significativo de varios clientes que no se ejecutan en la misma máquina.