Nginx补全计划1

前言

nginx是服务器的代理,我们习惯称之为“反向代理”。什么是正向代理,什么是反向代理呢?

服务端——客户端模型的概念可以说是深入人心了,而在这两个的中间,我们就可以进行一些中间件的添加,来保证整个服务更加可靠高效。

  • 我们习惯把代理客户端的服务器作为正向代理,常见的有VPN技术,即用户与目标服务器之间的网络可能是不可达的,但是有某些服务器是可以到达目标服务器的,于是,一种很自然的想法就是去让我们的客户端与代理服务器进行连接,中间的代理服务器把请求转发给目标服务器,这样中继一下,就可以实现通信。
  • 而反向代理是中间服务器作为目标服务器的代理,换言之,我们只知道中间代理服务器的地址,而不知道目标服务器的地址。

这样做的一个好处是,当请求到达中间服务器时,中间服务器就可以做负载均衡,即把请求比较均匀的分配给集群的服务器;还有一个好处是,可以隐藏目标服务器的地址。

反向代理

环境配置

安装

我们可以很方便的使用apt来进行安装,无非就那几步

  • 首先是更新apt

    1
    
    sudo apt update
    
  • 然后是安装nginx

    1
    
    sudo apt install nginx
    
  • 然后是启动nginx服务

    1
    
    sudo systemctl start nginx
    
  • 如果你想要设置为开机自启动

    1
    
    sudo systemctl enable nginx
    

启动和测试

安装好后,我们看一下nginx是否启动了

1
sudo systemctl status nginx

如果看到activate的字样,说明启动成功了

image-20250715203259894

启动好后,我们来打开浏览器,输入

1
localhost

试一试,可以看到对应的欢迎界面

image-20250715203410993

小测试

我始终认为,上手实战要远比在哪什么都不动要好,我们先不关注nginx的那些高级配置,我们就先来关注一下nginx的基本的功能,也就是反向代理。

实现http反向代理

众所周知啊,即然http实现的是反向代理的功能,那么,当我访问localhost的时候,nginx应该会自动帮我实现请求转发。下面我们来实现一下这个功能。

首先是启动我们的后端程序,这个后端程序很简单,就返回一句Hello, World就行,为了方便起见,我这里直接使用net/http来进行演示。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import (
	"fmt"
	"net/http"
)

func index(w http.ResponseWriter, req *http.Request) {
	w.Write([]byte("<H1> Hello, World </H1>"));
	fmt.Println("访问index!")
}

func main() {
	http.HandleFunc("/", index)
	http.ListenAndServe(":5000", nil);
	
}

这段代码的意思是,当我访问localhost:5000这个地址的时候,会输出一句话,如下图所示

image-20250715205553285

好了,现在,我们就可以改写nginx转发的配置,目的就是,当直接访问nginx的地址时,会直接出现Hello, World这行字。

改写nginx配置

我们需要修改的文件在

1
/etc/nginx/sites-available/default

就是这个配置文件。把对应的location的部分给修改了。

修改为以下内容

1
2
3
4
5
6
		# 自己的配置
		location / {
			proxy_pass http://localhost:5000;
			proxy_set_header Host $host;
          	proxy_set_header X-Real-IP $remote_addr;
		}

然后可以使用-t参数来测试以下修改是否生效。

1
sudo nginx -t

如果是

1
2
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

说明没有问题。

然后重启nginx

验证

此时,我们可以直接去访问nginx服务器对应的网址,也就是localhost,看看是否转发成功

image-20250715211846936

我们此时发现,转发确实成功了!

负载均衡测试

使用nginx的一个好处就是可以使用负载均衡,常见的负载均衡有

  • 轮询:可以理解为遍历每个服务器,每一次把一个请求发送给服务器,然后转着圈循环着去发送。
  • 加权:权重越大,收到请求的概率也就越高
  • IP哈希:同一IP的请求需要某个机器负责
  • url哈希:同一url请求某个机器负责

对于轮询算法来说,其最大的优势就是简单,但这是建立在所有服务器性能一样的基础上的;

加权则可以表示为权限越大的服务器性能越好,其收到请求的概率也就越大;

IP哈希则可以很好的将一个IP地址固定在某个机器上,这样做的好处就是在使用session这类鉴权系统时可以做到集中session

我们来看看怎么实现一个加权负载均衡

nginx实现负载均衡

我们先把负载均衡的配置给搞清楚,这里我们就用单机来模拟一下多台机器同时在线的情况,即不同的端口就表示不同的服务在运行,也就是说,我们得改写一下之前写的那个后端程序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import (
	"fmt"
	"net/http"
	"os"
)

var port string

func index(w http.ResponseWriter, req *http.Request) {
	msg := fmt.Sprintf("<h1>Hello from port %s</h1>", port)
	w.Write([]byte(msg))
	fmt.Printf("收到请求,响应端口:%s\n", port)
}

func main() {
	// 默认端口
	port = "5000"

	// 如果传入了命令行参数,则使用它作为端口
	if len(os.Args) > 1 {
		port = os.Args[1]
	}

	http.HandleFunc("/", index)

	addr := ":" + port
	fmt.Printf("服务正在端口 %s 上运行...\n", port)
	err := http.ListenAndServe(addr, nil)
	if err != nil {
		fmt.Println("启动失败:", err)
	}
}

这样,我们就可以从命令行输入多个参数进行启动。我们将启动过程可以封装为一个脚本,这样就可以通过脚本启动。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/bash

echo "开始编译main.go"
go build main.go
echo "编译完毕"


echo "开始启动..."
./main $1 &
./main $2 &
./main $3 &


echo "启动完成!"

这样,我们的多个服务就可以成功启动了,当我们进行访问的时候,就可以从浏览器的页面中快速的看到是访问了哪个服务器。

当然,我们还得对nginx做一些负载均衡的配置。

轮询

首先是负载均衡的关键字upstream

1
2
3
4
5
upstream testServer {
    server localhost:5000;
    server loalhost:5001;
    server localhost:5002;
}

加权轮询

更高级的,我们可以把权重值加载后面,从而形成加权轮询

1
2
3
4
5
upstream testServer {
    server localhost:5000 weight=1;
    server loalhost:5001 weight=3;
    server localhost:5002;
}

我们要把这段upstream的内容放在nginx.confhttp{}内,然后,把原来的server地址就可以替换为

1
2
3
4
5
		location / {
			proxy_pass http://testServer;
			proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
		}

然后重启nginx

1
sudo systemctl restart nginx

一切准备完毕,现在我们来看看是否真的可以做到负载均衡!

测试

预期效果:当我们访问localhost的时候,应该可以看到

1
2
3
Hello from port 5000
Hello from port 5001
Hello from port 5002

这样的结果。

我们来试一下。

第一次测试:

image-20250716101042080

刷新一下看看

image-20250716101107189

再刷新一下看看

image-20250716101124182

由于我们实现的负载均衡是轮询,即每次请求一定会按照循序把所有请求分发给不同的服务器。更直观的,我们用Jmeter来测试一下。

Jmeter测试轮询

jmeter中,创建一个测试工程,其目录结构如下

image-20250716102517014

然后在线程组/HTTP请求下,填写我们需要测试的url

image-20250716102628138

然后就可以直接开始测试,我设置的是一组10个线程,所以单次运行,一次会创建10个请求。

按照预期,每个请求都是均匀出现的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
~/go4test ❯ 收到请求,响应端口:5000
收到请求,响应端口:5001
收到请求,响应端口:5001
收到请求,响应端口:5002
收到请求,响应端口:5000
收到请求,响应端口:5001
收到请求,响应端口:5002
收到请求,响应端口:5000
收到请求,响应端口:5001
收到请求,响应端口:5002

符合预期!

Jmeter测试加权轮询

按照我们的预期,权限越大,被访问的几率也就越大。根据配置,应该是5001 > 5000 > 5002出现的次数

1
2
3
4
5
upstream testServer {
    server localhost:5000 weight=1;
    server loalhost:5001 weight=3;
    server localhost:5002;
}

我们来测试一下试试!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
~/go4test ❯ 收到请求,响应端口:5001
收到请求,响应端口:5001
收到请求,响应端口:5001
收到请求,响应端口:5001
收到请求,响应端口:5001
收到请求,响应端口:5001
收到请求,响应端口:5002
收到请求,响应端口:5001
收到请求,响应端口:5001
收到请求,响应端口:5000

可以看到,5001的出现频率远大于其它两个。

总结

这次我们配置了nginx的环境,同时也实现了一个反向代理服务器的功能,最后也演示了一下nginx的负载均衡功能,包括轮询负载均衡、加权负载均衡等。

Licensed under CC BY-NC-SA 4.0
花有重开日,人无再少年
使用 Hugo 构建
主题 StackJimmy 设计