GO+Nginx实现服务器的高并发+负载均衡(超级简单,开箱即用)

前言

很久都没出GO方面的文章了,自己都很久没玩了,很多东西都忘了,这次出个文章,使用Go+Nginx搭建一个可以实现高并发并且对请求做出限制功能的服务器。

一、Go

这里先搭建Go部分的环境,这个代码我自己div了一下Logger打印日志,可以自己随心所欲搭配打印,如果不需要自己div,图方便的可以使用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()
}

二、Nginx

要使用 Nginx 进行负载均衡,并在当前端口的并发达到一定数量时进行切换,可以结合 Nginx 的 upstream 模块和相关配置实现。以下是一般的步骤:

  1. 安装和配置 Nginx:首先,确保已经安装了 Nginx,并进行基本的配置。具体的安装和配置步骤会因操作系统而异。安装完成后,可以编辑 Nginx 的配置文件,通常是位于 nginx-1.25.1/conf/nginx.conf

  2. 定义后端服务器:在 Nginx 配置文件中,使用 upstream 指令定义后端服务器组,并指定各个服务器的地址和端口号。

    示例:

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

    在上述示例中,我们定义了一个名为 backend 的后端服务器组,其中包含多个后端服务器的地址和端口号。

  3. 配置负载均衡规则:在 Nginx 的虚拟主机配置中,使用 proxy_pass 指令将请求转发到后端服务器组,并配置负载均衡规则。

    示例:

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

    在上述示例中,我们定义了一个虚拟主机,它监听端口号为 80,server_name127.0.0.1。所有的请求将通过 proxy_pass 指令转发到名为 backend 的后端服务器组。

  4. 配置负载均衡策略:根据您的需求,可以配置不同的负载均衡策略。Nginx 提供了多种负载均衡算法,如轮询(默认)、IP 哈希、最少连接等。可以根据实际情况选择适合的负载均衡策略。(因为我们这里要测试,我就选择了最少连接算法来实现)

    示例:

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

    在上述示例中,我们使用了最少连接算法来实现负载均衡策略,该策略将请求发送到当前连接数最少的后端服务器。

  5. 配置并发阈值:要根据当前端口的并发数量进行切换,您可以使用 Nginx 的 limit_conn 模块来设置并发连接数的阈值,并根据阈值进行切换。

    示例:

    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;
     }
    }
    

    在上述示例中,我们使用 limit_conn 模块设置每个 IP 地址的并发连接数限制为 100。当达到这个限制时,Nginx 将会返回503错误。

  6. 重新加载配置和启动 Nginx:完成配置后,保存文件并使用命令重新加载 Nginx 配置。命令通常是 nginx -s reload。这将重新加载配置文件,使更改生效。

nginx-1.25.1/conf/nginx.conf总的配置代码(原来那个http记得注释掉):

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;
    }
  }
}

上面的 server 127.0.0.1:8080; server 127.0.0.1:8081;后续可以换成自己的节点地址,这里只是演示所以都用本地的端口来模拟多节点。

三、测试

两种测试代码,自己根据语言选择

Go并发请求测试

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()
}

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)

Go服务器的运行结果:
在这里插入图片描述
很直观的看到了,采用了我们之前的最少连接算法来实现负载均衡策略。

四、总结

Go部分的代码,我没怎么详细讲,大概意思大家应该能看懂,里面我还使用了令牌桶来对请求当前api的连接数做了限制,可以在Nginx的ip限制下再做一次限制,可以简单达到下面的两个功能

  1. 保护服务器资源:通过限制请求数,可以防止服务器过载。当同时接收到大量的请求时,服务器的资源(如 CPU、内存和网络带宽)可能会耗尽,导致性能下降甚至崩溃。通过限制请求数,可以确保服务器能够合理地处理请求,避免资源耗尽。

  2. 提高系统稳定性:通过限制请求数,可以防止某些恶意用户或恶意程序通过发送大量请求来攻击服务器。这种攻击通常被称为拒绝服务攻击(DDoS),通过限制请求数,可以减轻对服务器的过载攻击,提高系统的稳定性和可靠性。

在这里插入图片描述

借鉴

ChatGPT

猜你喜欢

转载自blog.csdn.net/qq_41866988/article/details/132102181