GO+Nginx logra una alta concurrencia + balanceo de carga de servidores (súper simple, listo para usar)

prefacio

Ha pasado mucho tiempo desde que publiqué un artículo sobre GO. No lo he jugado durante mucho tiempo y olvidé muchas cosas. Esta vez publiqué un artículo, usando Go+Nginx para construir un servidor que pueda lograr una alta concurrencia. y limitar las solicitudes.

1. Ir

Primero construyamos el entorno de la parte Go. Div el registrador para imprimir el registro por mí mismo. Puede imprimirlo como desee. Si no necesita su propio div, puede usarlo convenientemente.gin.Default()

package main

import (
	"fmt"
	"github.com/fatih/color"
	"github.com/gin-gonic/gin"
	"golang.org/x/time/rate"
	"log"
	"net/http"
	"sync"
	"time"
)

func Logger(addr string) gin.HandlerFunc {
    
    
	return func(context *gin.Context) {
    
    
		t := time.Now()
		// 继续处理请求
		context.Next()
		// 记录响应时间
		latency := time.Since(t).Milliseconds()
		ginLog := "[GIN-LOG] NowTime--> %s | Method--> %s | Path--> %s | SpendTime--> %dms | Ip--> %s | Port--> %s"
		info := fmt.Sprintf(
			ginLog,
			time.Now().Format("2006/01/02 15:04:05"),
			context.Request.Method,
			context.Request.URL.Path,
			latency,
			context.ClientIP(),
			addr)
		color.Green(info)
	}
}

// responseData 返回信息
func responseData(context *gin.Context, code int, message string, status string, result interface{
    
    }) {
    
    
	context.JSON(http.StatusOK, gin.H{
    
    "code": code, "message": message, "success": status, "result": result})
}

func route1Handler(addr string) http.Handler {
    
    
	// 创建令牌桶,每秒产生100个令牌,桶的容量为100
	bucket := rate.NewLimiter(100, 100)
	// 处理 route1 的逻辑
	e := gin.New()
	e.Use(gin.Recovery(), Logger(addr))
	e.GET("/test", func(context *gin.Context) {
    
    
		if !bucket.Allow() {
    
    
			responseData(context, 300, "过载", "fail", "")
			return
		}
		name := context.Query("name")
		if name == "" {
    
    
			responseData(context, 200, "成功返回数据", "success", "")
			return
		}
	})
	return e
}

func startServer(wg *sync.WaitGroup, port int) {
    
    
	defer wg.Done()

	addr := fmt.Sprintf(":%d", port)
	server := &http.Server{
    
    
		Addr:         addr,
		Handler:      route1Handler(addr),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}
	err := server.ListenAndServe()
	if err != nil && err != http.ErrServerClosed {
    
    
		log.Fatal(err)
	}
}

func main() {
    
    
	// 端口数量(节点数量)
	concurrency := 2 

	var wg sync.WaitGroup
	wg.Add(concurrency)

	// 启动端口(模拟多个节点操作)
	for i := 0; i < concurrency; i++ {
    
    
		go startServer(&wg, 8080+i)
	}

	// 等待所有并发请求完成
	wg.Wait()
}

2. Nginx

Para usar Nginx para equilibrar la carga y cambiar cuando la simultaneidad del puerto actual alcanza un número determinado, puede combinar el módulo ascendente de Nginx y las configuraciones relacionadas. Estos son los pasos generales:

  1. Instale y configure Nginx: Primero, asegúrese de que se haya instalado Nginx y realice la configuración básica. Los pasos específicos de instalación y configuración varían según el sistema operativo. Una vez completada la instalación, puede editar el archivo de configuración de Nginx, que normalmente se encuentra en nginx-1.25.1/conf/nginx.conf.

  2. Defina el servidor back-end: en el archivo de configuración de Nginx, use upstreamel comando para definir el grupo de servidores back-end y especifique la dirección y el número de puerto de cada servidor.

    Ejemplo:

    upstream backend {
      server 127.0.0.1:8080;
      server 127.0.0.1:8081;
      # 添加更多的后端服务器...
    }
    

    En el ejemplo anterior, definimos un backendgrupo de servidores backend denominado , que contiene las direcciones y los números de puerto de varios servidores backend.

  3. Configure las reglas de equilibrio de carga: en la configuración del host virtual de Nginx, use proxy_passel comando para reenviar la solicitud al grupo de servidores back-end y configure las reglas de equilibrio de carga.

    Ejemplo:

    server {
      listen 80;
      server_name 127.0.0.1;
    
     location / {
       proxy_pass http://backend;
     }
    
     location /test {
       proxy_pass http://backend/test;
     }
    }
    

    En el ejemplo anterior, definimos un host virtual que escucha en el puerto número 80, server_name127.0.0.1. Todas las solicitudes se proxy_passreenviarán al backendgrupo de servidores back-end nombrado por la directiva.

  4. Configurar estrategia de balanceo de carga: Según tus necesidades, puedes configurar diferentes estrategias de balanceo de carga. Nginx proporciona una variedad de algoritmos de equilibrio de carga, como round robin (predeterminado), hash de IP, conexión mínima, etc. Puede elegir una estrategia de equilibrio de carga adecuada según la situación real. (Debido a que queremos probar aquí, elegí el algoritmo de conexión mínima para lograr)

    Ejemplo:

    upstream backend {
      least_conn;  # 使用最少连接算法负载均衡策略
      server 127.0.0.1:8080;
      server 127.0.0.1:8081;
      # 添加更多的后端服务器...
    }
    

    En el ejemplo anterior, usamos el algoritmo de menor cantidad de conexiones para implementar la estrategia de equilibrio de carga, que envía la solicitud al servidor backend con la menor cantidad de conexiones actuales.

  5. Configure el umbral concurrente: para cambiar según el número actual de conexiones simultáneas, puede usar el limit_connmódulo Nginx para establecer el umbral del número de conexiones simultáneas y cambiar según el umbral.

    Ejemplo:

    server {
     listen 80;
     server_name 127.0.0.1;
    
     location / {
       limit_conn conn_limit_per_ip 100;
       proxy_pass http://backend;
     }
    
     location /test {
       limit_conn conn_limit_per_ip 100;
       proxy_pass http://backend/test;
     }
    }
    

    En el ejemplo anterior, usamos limit_connel módulo para establecer un límite de 100 conexiones simultáneas por dirección IP. Cuando se alcanza este límite, Nginx devolverá un error 503.

  6. Vuelva a cargar la configuración e inicie Nginx: después de completar la configuración, guarde el archivo y use el comando para volver a cargar la configuración de Nginx. El comando suele ser nginx -s reload. Esto recargará el archivo de configuración, haciendo efectivos los cambios.

nginx-1.25.1/conf/nginx.confEl código de configuración total (recuerde comentar el http original):

http {
    
    
  limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:20m;

  upstream backend {
    
    
    least_conn;
    server 127.0.0.1:8080;
    server 127.0.0.1:8081;
  }

  server {
    
    
    listen 80;
    server_name 127.0.0.1;

    location / {
    
    
      limit_conn conn_limit_per_ip 100;
      proxy_pass http://backend;
    }

    location /test {
    
    
      limit_conn conn_limit_per_ip 100;
      proxy_pass http://backend/test;
    }
  }
}

El server 127.0.0.1:8080; server 127.0.0.1:8081;seguimiento anterior se puede reemplazar con su propia dirección de nodo. Esto es solo una demostración, por lo que el puerto local se usa para simular múltiples nodos.

3. prueba

Dos códigos de prueba, elige según el idioma

Ir a la prueba de solicitud simultánea

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"
)

func sendRequest(url string, wg *sync.WaitGroup) {
    
    
	defer wg.Done()

	response, err := http.Get(url)
	if err != nil {
    
    
		fmt.Printf("Error connecting to %s: %s\n", url, err)
		return
	}
	defer response.Body.Close()

	//处理响应
	responseBody, err := ioutil.ReadAll(response.Body)
	if err != nil {
    
    
		fmt.Printf("Error reading response body from %s: %s\n", url, err)
		return
	}
	fmt.Printf("Response from %s: %s\n", url, string(responseBody))

	//fmt.Printf("Response from %s: %s\n", url, response.Status)
}

func main() {
    
    
	// 设置并发请求数
	concurrency := 300
	// 设置要请求的URL
	url := "http://127.0.0.1/test"

	var wg sync.WaitGroup
	wg.Add(concurrency)

	// 发送并发请求
	for i := 0; i < concurrency; i++ {
    
    
		go sendRequest(url, &wg)
	}

	wg.Wait()
}

Prueba de solicitud concurrente de Python

import requests
from concurrent.futures import ThreadPoolExecutor

def send_request(url):
    try:
        response = requests.get(url)
        print(response.text)
        # print(f"Response from {url}: {response.status_code}")
    except requests.exceptions.RequestException as e:
        print(f"Error connecting to {
      
      url}: {
      
      e}")

# 设置并发请求数
concurrency = 500
# 设置要请求的URL
url = "http://127.0.0.1/test"

# 创建线程池执行器
executor = ThreadPoolExecutor(max_workers=concurrency)

# 发送并发请求
for _ in range(concurrency):
    executor.submit(send_request, url)

Los resultados de ejecución del servidor Go:
inserte la descripción de la imagen aquí
es muy intuitivo ver que nuestro algoritmo de conexión mínima anterior se usa para implementar la estrategia de equilibrio de carga.

Cuatro Resumen

No entré en detalles sobre la parte Go del código, pero todos deberían poder entenderlo. También usé un depósito de tokens para limitar la cantidad de conexiones que solicitan la API actual. Puede limitarlo nuevamente bajo la ip de Nginx límite. , puede lograr fácilmente las siguientes dos funciones

  1. Proteja los recursos del servidor: al limitar la cantidad de solicitudes, puede evitar la sobrecarga del servidor. Cuando se recibe una gran cantidad de solicitudes al mismo tiempo, los recursos del servidor (como la CPU, la memoria y el ancho de banda de la red) pueden agotarse, lo que resulta en un bajo rendimiento o incluso fallas. Al limitar la cantidad de solicitudes, puede asegurarse de que el servidor pueda manejar las solicitudes de manera razonable y evitar el agotamiento de los recursos.

  2. Mejorar la estabilidad del sistema: al limitar la cantidad de solicitudes, se puede evitar que algunos usuarios o programas maliciosos ataquen el servidor enviando una gran cantidad de solicitudes. Este tipo de ataque generalmente se denomina ataque de denegación de servicio (DDoS) Al limitar la cantidad de solicitudes, puede reducir el ataque de sobrecarga en el servidor y mejorar la estabilidad y confiabilidad del sistema.

inserte la descripción de la imagen aquí

aprende de

ChatGPT

Supongo que te gusta

Origin blog.csdn.net/qq_41866988/article/details/132102181
Recomendado
Clasificación