Deje su lenguaje de programación colgado en la puerta y venga a admirar la mejor biblioteca estándar que he conocido. Este es todo el código que realmente necesita... Elegir un lenguaje de programación para un proyecto no debería ser como declarar quién es tu equipo favorito. debería ser una cuestión de pragmatismo y de elegir la herramienta adecuada para el trabajo adecuado. En este post quiero mostrarte cuándo y por qué Go brilla como lenguaje. Específicamente, quiero mostrarles lo sólida que es su biblioteca estándar para la programación básica de Internet. Aún más específicamente... ¡vamos a escribir un proxy inverso! "Go tiene mucho a su favor, pero donde realmente se destaca es en estas tareas de plomería de red de nivel inferior, no hay mejor lenguaje". ¿Qué es un proxy inverso? Recibo un envío de solicitud de un cliente, envío esa solicitud a otro servidor, recibo una respuesta del servidor y la reenvío al cliente. La parte inversa de esto simplemente significa que el propio proxy determina dónde enviar el tráfico y cuándo Una gran forma elegante de decir un promotor de tráfico. (Simplemente hermoso 😍…) ¿Por qué es útil? Debido a que el concepto es tan simple, se puede aplicar para ayudar en muchos casos diferentes: equilibrio de carga, pruebas A/B, almacenamiento en caché, autenticación, etc. Al final de esta breve publicación, habrá aprendido a: Servir solicitudes HTTP Analizar el cuerpo de una solicitud Servir tráfico a otro servidor utilizando un proxy inverso Nuestro proyecto de proxy inverso Sumerjámonos en el proyecto real. Lo que vamos a hacer es tener un servidor web que: 1. Acepta solicitudes 2. Lee el cuerpo de una solicitud, específicamente el campo proxy_condition 3. Si el dominio proxy es igual a A, envíe el tráfico a la URL 1 4. Si el dominio proxy es igual a B, envíe el tráfico a la URL 2 5. Si el dominio proxy no es ninguno, envíe el tráfico a la URL predeterminada. requisitos previos para la programación con. Ir para crear servidores simples con. servidor http Configurando nuestro entorno Lo primero que queremos hacer es ingresar todas las variables de configuración requeridas en nuestro entorno para que podamos usarlas en nuestra aplicación y mantenerlas fuera del código fuente. Creo que el mejor enfoque es crear un archivo .env que contenga las variables de entorno deseadas. A continuación se muestra lo que tengo para este proyecto específico: PORT= A_CONDITION_URL= B_CONDITION_URL= DEFAULT_CONDITION_URL= export 1330 export "http://localhost:1331" export "http://localhost:1332" export "http://localhost:1333" Este es un hábito que aprendí de la aplicación 12 Factor Después de guardar su archivo que puede ejecutar: .env . source env para configurar, cargue la configuración en su entorno en cualquier momento. Sentando las bases de nuestro proyecto A continuación vamos a crear un archivo llamado que hace lo siguiente: main.go 1. Cuando se inicia registra el , , , y variables de entorno a la consola PORT A_CONDITION_URL B_CONDITION_URL DEFAULT_CONDITION_URL 2. Escuche las solicitudes en la ruta: / package main ( ) func getEnv(key, fallback string) string { value, := os.LookupEnv(key); ok { value } fallback } func getListenAddress() string { := getEnv( , ) + port } func logSetup() { := os.Getenv( ) b_condtion_url := os.Getenv( ) default_condtion_url := os.Getenv( ) log.Printf( , getListenAddress()) log.Printf( , a_condtion_url) log.Printf( , b_condtion_url) log.Printf( , default_condtion_url) } func handleRequestAndRedirect(res http.ResponseWriter, req *http.Request) { } func main() { logSetup() http.HandleFunc( , handleRequestAndRedirect) err := http.ListenAndServe(getListenAddress(), nil); err != nil { panic(err) } } import "bytes" "encoding/json" "io/ioutil" "log" "net/http" "net/http/httputil" "net/url" "os" "strings" // Get env var or default if ok return return // Get the port to listen on port "PORT" "1338" return ":" // Log the env variables required for a reverse proxy a_condtion_url "A_CONDITION_URL" "B_CONDITION_URL" "DEFAULT_CONDITION_URL" "Server will run on: %s\n" "Redirecting to A url: %s\n" "Redirecting to B url: %s\n" "Redirecting to Default url: %s\n" // Given a request send it to the appropriate url // We will get to this... // Log setup values // start server "/" if (💀Saquemos los esqueletos del armario para que podamos pasar a las cosas divertidas). Ahora deberías poder ejecutar Analizar el cuerpo de la solicitud Ahora que tenemos el esqueleto de nuestro proyecto juntos, queremos comenzar a crear la lógica que manejará el análisis del cuerpo de la solicitud. Empiece por actualizar para analizar el valor del cuerpo de la solicitud. handleRequestAndRedirect proxy_condition type requestPayloadStruct struct { ProxyCondition string } func requestBodyDecoder(request *http.Request) *json.Decoder { body, := ioutil.ReadAll(request.Body) err != nil { log.Printf( , err) panic(err) } request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) json.NewDecoder(ioutil.NopCloser(bytes.NewBuffer(body))) } func parseRequestBody(request *http.Request) requestPayloadStruct { := requestBodyDecoder(request) requestPayload requestPayloadStruct err := decoder.Decode(&requestPayload) err != nil { panic(err) } requestPayload } func handleRequestAndRedirect(res http.ResponseWriter, req *http.Request) { := parseRequestBody(req) } `json:"proxy_condition"` // Get a json decoder for a given requests body // Read body to buffer err if "Error reading body: %v" // Because go lang is a pain in the ass if you read the body then any susequent calls // are unable to read the body again.... return // Parse the requests body decoder var if return // Given a request send it to the appropriate url requestPayload // ... more to come (Análisis básico de un blob JSON a una estructura en Go). Usar para determinar a dónde enviamos el tráfico proxy_condition Ahora que tenemos el valor de la a partir de la solicitud, la usaremos para decidir hacia dónde dirigimos nuestro proxy inverso. proxy_condition Recuerda de antes que tenemos tres casos: Si es igual a luego enviamos tráfico a proxy_condition A A_CONDITION_URL Si es igual a luego enviamos tráfico a proxy_condition B B_CONDITION_URL De lo contrario, envíe el tráfico a DEFAULT_CONDITION_URL func logRequestPayload(requestionPayload requestPayloadStruct, proxyUrl string) { log.Printf( , requestionPayload.ProxyCondition, proxyUrl) } func getProxyUrl(proxyConditionRaw string) string { := strings.ToUpper(proxyConditionRaw) a_condtion_url := os.Getenv( ) b_condtion_url := os.Getenv( ) default_condtion_url := os.Getenv( ) proxyCondition == { a_condtion_url } proxyCondition == { b_condtion_url } default_condtion_url } func handleRequestAndRedirect(res http.ResponseWriter, req *http.Request) { := parseRequestBody(req) url := getProxyUrl(requestPayload.ProxyCondition) logRequestPayload(requestPayload, url) } // Log the typeform payload and redirect url "proxy_condition: %s, proxy_url: %s\n" // Get the url for a given proxy condition proxyCondition "A_CONDITION_URL" "B_CONDITION_URL" "DEFAULT_CONDITION_URL" if "A" return if "B" return return // Given a request send it to the appropriate url requestPayload // more still to come... Proxy inverso a esa URL ¡Finalmente estamos en el proxy inverso real! En tantos idiomas, un proxy inverso requeriría mucho pensamiento y una buena cantidad de código o al menos tener que importar una biblioteca sofisticada. Sin embargo, la biblioteca estándar de Golang hace que la creación de un proxy inverso sea tan simple que es casi increíble. A continuación se muestra esencialmente la única línea de código que necesita: httputil. . NewSingleHostReverseProxy( ) url ServeHTTP( , ) res req Tenga en cuenta que en el siguiente código agregamos un poco más para que pueda admitir completamente la redirección SSL (aunque no es necesario): func serveReverseProxy(target string, res http.ResponseWriter, req *http.Request) { url, := url.Parse(target) proxy := httputil.NewSingleHostReverseProxy(url) req.URL.Host = url.Host req.URL.Scheme = url.Scheme req.Header.Set( , req.Header.Get( )) req.Host = url.Host proxy.ServeHTTP(res, req) } func handleRequestAndRedirect(res http.ResponseWriter, req *http.Request) { := parseRequestBody(req) url := getProxyUrl(requestPayload.ProxyCondition) logRequestPayload(requestPayload, url) serveReverseProxy(url, res, req) } // Serve a reverse proxy for a given url // parse the url _ // create the reverse proxy // Update the headers to allow for SSL redirection "X-Forwarded-Host" "Host" // Note that ServeHttp is non blocking and uses a go routine under the hood // Given a request send it to the appropriate url requestPayload La única vez en el proyecto sentí que Go realmente se estaba quitando de mi camino. Ok, ahora que tenemos todo esto conectado, configure nuestra aplicación en el puerto y nuestros 3 servidores simples en puertos (todo en terminales separados): 1330 1331–1333 source .env && go install && $GOPATH/bin/reverse-proxy-demo http-server -p 1331 http-server -p 1332 http-server -p 1333 Con todo esto en funcionamiento, podemos comenzar a enviar solicitudes con un cuerpo json en otra terminal de la siguiente manera: curl --request GET \ --url http: --header \ --data //localhost:1330/ \ 'content-type: application/json' '{ "proxy_condition": "a" }' Si está buscando un gran cliente de solicitud HTTP, no puedo recomendar Insomnia lo suficiente. y listo, podemos comenzar a ver nuestro proxy inverso dirigiendo el tráfico a uno de nuestros 3 servidores en función de lo que configuramos en el ¡campo! proxy_condition (¡¡¡Está vivo!!!) Envolver Go tiene mucho a su favor, pero donde realmente se pavonea es en estas tareas de "plomería" de red de nivel inferior, no hay mejor lenguaje. Lo que hemos escrito aquí es simple, eficaz, confiable y muy listo para usar en producción. Para servicios simples, puedo verme buscando Go nuevamente en el futuro. 🧞 ¡Esto es de código abierto! puedes encontrarlo aquí en Github ❤️ Solo escribo sobre programación y trabajo remoto. Si me sigues en Twitter no te haré perder el tiempo.