网络模型
网络模型 OSI(Open System Interconnect)
意为 开放网络互联,它是一种网络互联模型,也是一种规范。
网络模型分为七层,即用户发起请求到服务器接收(用户利用互联网发送信息给另一个用户),会经历七道工序:
层级 | 名称 | 说明 | 理解 |
---|---|---|---|
第七层 | 应用层 | 面向用户,使用户与计算机交互,交互的过程实质就是接口的调用,应用层提供了交互的接口,这一层常用的协议有:HTTP ,HTTPS ,FTP ,SMTP ,POP3 等,Nginx 就在这层,为七层负载均衡 | 我打开快递软件去寄信,此时快递软件就充当 应用层 的作用 |
第六层 | 表示层 | 提供数据格式编码及加密功能,确保 请求端 发送的数据能被 响应端 识别 | 寄信给外国友人需要将中文翻译为英文,此时翻译软件就充当 表示层 的作用 |
第五层 | 会话层 | 请求发送到响应端的过程中会存在会话,会话层就充当这一个过程的管理者作用,从 创建会话 到 维护会话 最后 销毁会话 | |
第四层 | 传输层 | 网络层提供了主机之间的逻辑通道,即通过寻址的方式,把数据包从一个主机发到另一个主机上。如果一个主机有多个进程同时在使用网络连接,那么数据包到达主机之后,如何区分它属于哪个进程呢?为了区分数据包所属的进程,就需要使用到 传输层。传输层提供了应用进程之间的端到端连接 | 如快递员取件收件,此时他就充当 传输层 的作用 |
第三层 | 网络层 | 网络通信必须要有本机IP和对方的IP,网络层也因此称为IP层,它提供了主机之间的逻辑通道,即通过寻址的方式,把数据包从一个主机发到另一个主机上,能提供IP分配的设备为路由器或交换机 | |
第二层 | 数据链路层 | 该层提供计算机MAC地址,通信的时候携带,为了确保请求投递正确,它会验证MAC地址,以确保请求响应的可靠性 | 如快递员派送前询问你家地址对不对,此时快递员就充当 数据链路层 的职责 |
第一层 | 物理层 | 物理介质,如网线、中继器等等设备 |
1 JMeter 测试单体和集群之间的差别
JMeter 用于测试网站性能,下载后点击 jmeter.bat
启动即可。
启动之后就可以添加测试计划进行测试,其中 添加线程组
中的
- 线程数:模拟用户个数
- Ramp-Up时间:请求的间隔时间
- 循环次数:每个用户发送请求的次数
添加取样器为HTTP请求,用于设置测试请求接口:
添加监听器,查看结果,常用添加聚合报告
,查看结果树
,用表格查看结果
2 Nginx 负载均衡
2.1 upstream
指令块
Nginx 使用 upstream
指令块用以配置上游服务器,然后在 location
指令块使用 proxy_pass
指令指向 upstream
指令块即可,当 upstream
指令块包含多台服务器时,此时就能起到集群的作用,如果 upstream
指令块只包含一台服务器,此时就仅仅起一个反向代理的作用。
# 配置上游服务器(集群)
upstream frontEndServers {
# 处于同一个内网中的其他服务器
server 192.168.1.105:8080;
# 本机的前端资源服务器
server localhost:8080;
}
# 总网关
server {
listen 80;
server_name www.foodie.com;
# 分发给真正处理前端资源的服务器,起到集群的效果
location / {
proxy_pass http://frontEndServers;
}
# 反向代理,分发给真正处理后端资源的服务器
location /foodie-api {
proxy_pass http://localhost:8088;
}
}
2.1.1 加权轮训
Nginx 默认的负载均衡策略是 轮训,如上一节的配置,将集群里的网页进行不同的修改,打开代理的网站每刷新一次,就会发现它会平均的、依次循环地访问每一台上游服务器,即每一个上游服务器的权重都是一致的,所以它的使用场景是每一个上游服务器的硬件性能水平一致。
当上游服务器之间的硬件性能水平不一致,就可以使用 加权轮训 进行自定义的分配功能,在 upstream
使用 weight
指令参数,声明权重即可,默认的数值为 1
。
如下配置,可以理解为当有三个请求发送过来,会有一个请求去到 192.168.1.105:8080
服务器中,两个请求去到 localhost:8080
服务器中,但注意响应的顺序是随机的,它只是起到一个权重百分比的作用。
# 配置上游服务器(集群)
upstream frontEndServers {
# 处于同一个内网中的其他服务器
server 192.168.1.105:8080 weight=1;
# 本机的前端资源服务器
server localhost:8080 weight=2;
}
2.1.2 max_conns
upstream
指令块的 max_conns
指令参数用于限制每台服务器的连接数,起到保护避免过载、限流的作用。
要注意的是当 Nginx 使用了多个 worker
进程时,该 max_conns
就会应用到每个 worker
进程中:
# 配置上游服务器(集群)
upstream frontEndServers {
# 处于同一个内网中的其他服务器
server 192.168.1.105:8080 max_conns=1;
# 本机的前端资源服务器
server localhost:8080 max_conns=2;
}
2.1.3 slow_start
upstream
指令块的 slow_start
指令参数用于让上游服务器缓慢的加入到集群中,如下配置的上游服务器在 60
秒内缓慢地由权重从 0
升到 6
,有些访问器在启动之后可能会有额外的配置,此时不想让很多流量申请进来,就可以使用这个功能。
该功能只有商业版能使用,而且只适合用于权重负载均衡,同时只适用于集群,不能用于单个服务器。
# 配置上游服务器(集群)
upstream frontEndServers {
# 处于同一个内网中的其他服务器
server 192.168.1.105:8080 weight=1;
# 本机的前端资源服务器
server localhost:8080 weight=6 slow_start=60s;
}
2.1.3 down
与 backup
upstream
指令块的 down
指令参数表示某个服务器不能使用
# 配置上游服务器(集群)
upstream frontEndServers {
# 处于同一个内网中的其他服务器
server 192.168.1.105:8080 down;
# 本机的前端资源服务器
server localhost:8080;
}
upstream
指令块的 backup
指令参数表示某个服务器为备用机,当其它所有服务器奔溃掉或者使用 down
参数指令停掉时,才会接收请求:
# 配置上游服务器(集群)
upstream frontEndServers {
# 处于同一个内网中的其他服务器
server 192.168.1.105:8080;
# 本机的前端资源服务器
server localhost:8080 bakup;
}
backup
指令参数只适合用于权重负载均衡
2.1.4 max_fails
和 fail_timeout
upstream
指令块的 max_fails
指令参数表示设置该服务器最大的失败次数,当该服务器在失败的重试时间内响应失败的次数到达该设置值,Nginx就会认为该服务器已经宕机,后续的请求就不发送到该服务器,默认值为 1
。
upstream
指令块的 fail_timeout
指令参数表示失败的重试时间,当该服务器在失败的重试时间内响应失败的次数到达该设置值,Nginx就会认为该服务器已经宕机,后续的请求就不发送到该服务器,当过了这个设置的失败重试时间,Nginx会继续尝试分发请求到该服务器中,并如此循环下去,默认为 10
。
# 配置上游服务器(集群)
upstream frontEndServers {
# 处于同一个内网中的其他服务器
server 192.168.1.105:8080;
# 本机的前端资源服务器
srver localhost:8080 max_fails=2 fail_timeout=2s;
}
2.1.5 keepalive
提高吞吐量
keepalive
指令用于设置长链接处理的数量,提高吞吐量;
proxy_http_version
设置长链接 http
版本为 1.1
;
proxy_set_header Connection
清除 connection header
信息;
# 配置上游服务器(集群)
upstream frontEndServers {
# 处于同一个内网中的其他服务器
server 192.168.1.105:8080;
# 设置长链接处理的数量
keepalive 32;
}
# 总网关
server {
listen 80;
server_name www.foodie.com;
# 反向代理,分发给真正处理前端资源的服务器,起到集群的效果
location / {
proxy_pass http://frontEndServers;
# 设置长链接 http 版本为 1.1
proxy_http_version 1.1;
# 清除 connection header 信息
proxy_set_header Connection "";
}
}
2.2 ip_hash
负载均衡
ip_hash
可以保证用户访问可以请求到上游服务中的固定的服务器,前提是用户 ip
没有发生更改。
它的运作原理是:
- 1、对用户
ip
进行hash运算求出对应的hash
值 - 2、对
hash
值进行上游服务器数量求模 - 3、得出的数值就依次对应上游服务器的序号
类似的数据库分表的逻辑操作也可以依据该 hash算法 的原理去操作。
upstream frontEndServers {
# 声明使用 ip_hash 负载均衡
ip_hash;
# 处于同一个内网中的其他服务器
server 192.168.1.105:8080;
# 本机的前端资源服务器
srver localhost:8080;
}
注意,后期上游服务器移除就要使用
down
指令参数,而不是直接删除注释,因为直接删除注释会导致 hash算法 的更改,导致所有用户的会话都会发生更改。
2.3 一致性 hash 算法
上述的哈希算法会有一个问题,就是服务器宕机或者增加服务器到集群都会导致哈希算法的改变,使得所有用户原来的会话都会丢失并且服务器内所涉及的缓存都会失效,从而用户发起的请求可能就因此变得耗时。
一致性 hash 算法可以尽量的避免这种情况,它的作用原理如图,实质上就是让服务器节点管理一个范围内的用户,当新增或删除服务器时,受影响的用户会顺时针地交给下一个服务器进行接管,从而避免大部分用户请求对应的服务器发生改变。
2.4 url_hash
负载均衡
与 ip_hash
负载均衡类似,不过 url_hash
负载均衡是根据 url
进行哈希运算
代码如下:
upstream frontEndServers {
# 声明使用 url_hash 负载均衡
hash $request_uri;
# 处于同一个内网中的其他服务器
server 192.168.1.105:8080;
# 本机的前端资源服务器
srver localhost:8080;
}
2.5 least_conn
负载均衡
使用 least_conn
就是根据上游服务器的连接数进行分配,哪台连接数最小就分配到哪台服务器进行响应请求。
upstream frontEndServers {
# 声明使用 least_conn 负载均衡
least_conn;
# 处于同一个内网中的其他服务器
server 192.168.1.105:8080;
# 本机的前端资源服务器
srver localhost:8080;
}
3 Nginx的反向代理缓存
Nginx 在反向代理静态文件服务器的时候,可以将服务器的静态资源缓存在 Nginx 端。其配置如下:
# proxy_cache_path 设置缓存存放目录
# keys_zone 设置共享内存名称以及占用空间大小,下例设置为 mycache 和5M
# max_size 设置缓存大小,下例设置为 1g
# inactive 设置缓存有效时间,超过该时间则被清理,下例设置为1分钟
# use_temp_path 临时目录,使用后会影响 nginx 性能,下例设置为关闭
proxy_cache_path /usr/local/nginx/upstream_cache keys_zone=mycache:5m max_size=1g inactive=1m use_temp_path=off
server {
listen 80;
server_name www.foodie.com;
# 反向代理,分发给真正处理前端资源的服务器,起到集群的效果
location / {
proxy_pass http://frontEndServers;
# 启动缓存,后面的数值要与 keys_zone 的命名一致
proxy_cache mycache;
# 针对200和304状态码缓存时间为8小时
proxy_cache_valid 200 304 8h;
}
}
3.1 proxy_pass
在 nginx 中配置 proxy_pass
代理转发时,需要特别注意 proxy_pass
和 location
的路径问题。下面逐个举例说明一下:
server {
listen 80;
server_name localhost;
location /api1/ {
proxy_pass http://localhost:8080;
}
# http://localhost/api1/xxx -> http://localhost:8080/api1/xxx
location /api2/ {
proxy_pass http://localhost:8080/;
}
# http://localhost/api2/xxx -> http://localhost:8080/xxx
location /api3 {
proxy_pass http://localhost:8080;
}
# http://localhost/api3/xxx -> http://localhost:8080/api3/xxx
location /api4 {
proxy_pass http://localhost:8080/;
}
# http://localhost/api4/xxx -> http://localhost:8080//xxx,请注意这里的双斜线,好好分析一下。
location /api5/ {
proxy_pass http://localhost:8080/haha;
}
# http://localhost/api5/xxx -> http://localhost:8080/hahaxxx,请注意这里的haha和xxx之间没有斜杠,分析一下原因。
location /api6/ {
proxy_pass http://localhost:8080/haha/;
}
# http://localhost/api6/xxx -> http://localhost:8080/haha/xxx
location /api7 {
proxy_pass http://localhost:8080/haha;
}
# http://localhost/api7/xxx -> http://localhost:8080/haha/xxx
location /api8 {
proxy_pass http://localhost:8080/haha/;
}
# http://localhost/api8/xxx -> http://localhost:8080/haha//xxx,请注意这里的双斜杠。
}
同时在使用 Nginx 代理转发的时候,后端 Java 使用 getRequestURL
方法取得的值是 Nginx 代理的 URL
的值,而不是取不到访问的 URL
,有时候需要拼接 Cookie 的 domain
属性会导致困扰,所以需要添加以下配置:
# 反向代理,分发给真正处理后端资源的服务器
location /foodie-api/ {
# 通过nginx代理后,后端使用getRequestURL取得的值是nginx代理的URL的值
# 取不到访问的URL,此时需要添加以下配置
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 代理转发
proxy_pass http://192.168.191.110:8088/;
}