Java架构师成长直通车:LVS+Nginx实现高可用集群

LVS+Nginx实现高可用集群

主从复制高可用Redis集群

Redis缓存雪崩,穿透

常见问题

LVS+Nginx实现高可用集群

Nginx(engine x)是一个高性能的 HTTP 和反向代理web 服务器,同时也提供 IMAP/POP3/SMTP服务。

  • 反向代理
  • 通过配置文件实现集群和负载均衡
  • 静态资源虚拟化

常见服务器

  • MS IIS                        asp.net
  • Weblogic、Jboss       传统行业 ERP/物流/金融/电信
  • Tomcat、Jetty            J2EE
  • Apache、Nginx           静态服务、反向代理
  • Netty                           高性能服务器编程

Java架构师成长直通车:LVS+Nginx实现高可用集群Java架构师成长直通车:LVS+Nginx实现高可用集群正在上传…重新上传取消Java架构师成长直通车:LVS+Nginx实现高可用集群

来源:Netcraft

安装 Nginx(CentOS)

Nginx HTTP服务知识大全

Nginx 核心知识

# 安装依赖 yum install -y gcc-c++ # 安装 gcc 环境 yum install -y pcre pcre-devel # 安装 PRCE 库,用于解析正则表达式 yum install -y zlib zlib-devel yum install -y openssl openssl-devel # https协议 wget http://nginx.org/download/nginx-1.16.1.tar.gz # 下载稳定版 tar -zxvf nginx-1.16.1.tar.gz # 解压 mkdir -p /var/temp/nginx # 创建临时目录 cd nginx-1.16.1/ ./configure --prefix=/usr/local/nginx --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_gzip_static_module --http-client-body-temp-path=/var/temp/nginx/client --http-proxy-temp-path=/var/temp/nginx/proxy --http-fastcgi-temp-path=/var/temp/nginx/fastcgi --http-uwsgi-temp-path=/var/temp/nginx/uwsgi --http-scgi-temp-path=/var/temp/nginx/scgi --with-http_ssl_module make && make install cd /usr/local/nginx/sbin/ # conf 等目录均位于/usr/local/nginx/中 ./nginx # 启动 ./nginx -s stop # 直接停止 ./nginx -s quit # 优雅停止 ./nginx -s reload # 重新加载 ./nginx -t # 检测配置文件 ./nginx -v # 查看 Nginx 版本 ./nginx -V # 查看版本及编译信息

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

# 安装依赖

yum install -y gcc-c++ # 安装 gcc 环境

yum install -y pcre pcre-devel # 安装 PRCE 库,用于解析正则表达式

yum install -y zlib zlib-devel

yum install -y openssl openssl-devel  # https协议

wget http://nginx.org/download/nginx-1.16.1.tar.gz # 下载稳定版

tar -zxvf nginx-1.16.1.tar.gz # 解压

mkdir -p /var/temp/nginx # 创建临时目录

cd nginx-1.16.1/

./configure --prefix=/usr/local/nginx --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_gzip_static_module --http-client-body-temp-path=/var/temp/nginx/client --http-proxy-temp-path=/var/temp/nginx/proxy --http-fastcgi-temp-path=/var/temp/nginx/fastcgi --http-uwsgi-temp-path=/var/temp/nginx/uwsgi --http-scgi-temp-path=/var/temp/nginx/scgi --with-http_ssl_module

make && make install

cd /usr/local/nginx/sbin/ # conf 等目录均位于/usr/local/nginx/中

./nginx           # 启动

./nginx -s stop   # 直接停止

./nginx -s quit   # 优雅停止

./nginx -s reload # 重新加载

./nginx -t        # 检测配置文件

./nginx -v        # 查看 Nginx 版本

./nginx -V        # 查看版本及编译信息

 

命令 解释
–prefix 指定nginx安装目录
–pid-path 指向nginx的pid
–lock-path 锁定安装文件,防止被恶意篡改或误操作
–error-log 错误日志
–http-log-path http日志
–with-http_gzip_static_module 启用gzip模块,在线实时压缩输出数据流
–http-client-body-temp-path 设定客户端请求的临时目录
–http-proxy-temp-path 设定http代理临时目录
–http-fastcgi-temp-path 设定fastcgi临时目录
–http-uwsgi-temp-path 设定uwsgi临时目录
–http-scgi-temp-path 设定scgi临时目录

 

Nginx 的进程模型

  • master 进程:主进程
  • worker 进程:工作进程

 

ps -ef|grep nginx # 查看 Nginx 进程 # 修改 worker 进程数 vi conf/nginx.conf worker_processes 2;

1

2

3

4

ps -ef|grep nginx # 查看 Nginx 进程

# 修改 worker 进程数

vi conf/nginx.conf

worker_processes  2;

Java架构师成长直通车:LVS+Nginx实现高可用集群Java架构师成长直通车:LVS+Nginx实现高可用集群正在上传…重新上传取消Java架构师成长直通车:LVS+Nginx实现高可用集群

Nginx 事件处理

 

# vi conf/nginx.conf # 设置工作模式 events { # 默认使用 epoll,因此可省略 use epoll; # 每个 worker 允许连接客户端最大连接数,可根据实际情况进行修改 worker_connections 10240; }

1

2

3

4

5

6

7

8

# vi conf/nginx.conf

# 设置工作模式

events {

    # 默认使用 epoll,因此可省略

    use epoll;

    # 每个 worker 允许连接客户端最大连接数,可根据实际情况进行修改

    worker_connections  10240;

}

使用了多路径复用器,在出现了阻塞时一个 worker 可以处理多个客户端请求

nginx.conf 配置结构

main 全局配置 events 配置工作模式及连接数 http http 模块相关配置 |_server 虚拟主机配置,可以有多个 |_location 路由规则,表达式 |_upstream 集群,内网服务器

1

2

3

4

5

6

main 全局配置

events 配置工作模式及连接数

http http 模块相关配置

|_server 虚拟主机配置,可以有多个

    |_location 路由规则,表达式

    |_upstream 集群,内网服务器

BIO:同步阻塞

NIO:同步非阻塞

AIO:异步非阻塞

nginx.conf 核心配置文件

 

user root; # worker进程的用户,默认为 nobody worker_processes 1; # 工作进程数,通常同 CPU 数量或 N-1 # error_log日志级别 debug|info|notice|warn|error|crit|alert|emerg,级别从左到右越来越大 pid logs/nginx.pid; # Nginx 进程 pid # http 指令块,对 http 网络传输的一些指令配置 http { } include mime.types; # include 引入外部配置,提高可读性,避免单个配置文件过大 # 日志格式中main为格式名称,供access_log等调用,日志中各项请见下表 # sendfile 使用高效文件传输,提升传输性能;启用后才能使用tcp_nopush,是指当数据表累积一定大小后才发送,提高了效率 sendfile on; tcp_nopush on; # keepalive_timeout设置客户端与服务端请求的超时时间,保证客户端多次请求的时候不会重复建立新的连接,节约资源损耗 keepalive_timeout 65;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

user  root; # worker进程的用户,默认为 nobody

worker_processes  1; # 工作进程数,通常同 CPU 数量或 N-1

# error_log日志级别 debug|info|notice|warn|error|crit|alert|emerg,级别从左到右越来越大

pid        logs/nginx.pid; # Nginx 进程 pid

# http 指令块,对 http 网络传输的一些指令配置

http {

}

 

include       mime.types; # include 引入外部配置,提高可读性,避免单个配置文件过大

# 日志格式中main为格式名称,供access_log等调用,日志中各项请见下表

# sendfile 使用高效文件传输,提升传输性能;启用后才能使用tcp_nopush,是指当数据表累积一定大小后才发送,提高了效率

sendfile        on;

tcp_nopush     on;

# keepalive_timeout设置客户端与服务端请求的超时时间,保证客户端多次请求的时候不会重复建立新的连接,节约资源损耗

keepalive_timeout  65;

 

参数名 参数意义
$remote_addr 客户端ip
$remote_user 远程客户端用户名,一般为:’-’
$time_local 时间和时区
$request 请求的url以及method
$status 响应状态码
$body_bytes_send 响应客户端内容字节数
$http_referer 记录用户从哪个链接跳转过来的
$http_user_agent 用户所使用的代理,一般来说都是浏览器
$http_x_forwarded_for 通过代理服务器来记录客户端的ip

 

# 日志切割脚本 # vi cut_my_log.sh #!/bin/bash LOG_PATH="/var/log/nginx/" RECORD_TIME=$(date -d "yesterday" +%Y-%m-%d) PID=/var/run/nginx/nginx.pid mv ${LOG_PATH}/access.log ${LOG_PATH}/access.${RECORD_TIME}.log mv ${LOG_PATH}/error.log ${LOG_PATH}/error.${RECORD_TIME}.log #向Nginx主进程发送信号,用于重新打开日志文件 kill -USR1 `cat $PID` # 手动切割 chmod u+x cut_my_log.sh ./cut_my_log.sh # 自动切割可通过定时任务,如未安装yum install -y crontabs crontab -e * * */1 * * /usr/local/nginx/sbin/cut_my_log.sh # crontab -l 查看 service crond restart # 重启

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

# 日志切割脚本

# vi cut_my_log.sh

 

#!/bin/bash

LOG_PATH="/var/log/nginx/"

RECORD_TIME=$(date -d "yesterday" +%Y-%m-%d)

PID=/var/run/nginx/nginx.pid

mv ${LOG_PATH}/access.log ${LOG_PATH}/access.${RECORD_TIME}.log

mv ${LOG_PATH}/error.log ${LOG_PATH}/error.${RECORD_TIME}.log

#向Nginx主进程发送信号,用于重新打开日志文件

kill -USR1 `cat $PID`

 

# 手动切割

chmod u+x cut_my_log.sh

./cut_my_log.sh

# 自动切割可通过定时任务,如未安装yum install -y crontabs

crontab -e

* * */1 * * /usr/local/nginx/sbin/cut_my_log.sh

# crontab -l 查看

service crond restart # 重启

静态文件两种配置方式(其中 xxx 为服务器中/home 下的静态文件目录名):

server { .. location /xxx{ root /home; } location /static{ alias /home/xxx; } }

1

2

3

4

5

6

7

8

9

10

server {

..

        location /xxx{

                root /home;

        }

 

        location /static{

                alias /home/xxx;

        }

}

gzip 压缩配置示例

#开启 gzip 压缩功能以提高传输效率、节约带宽 gzip on; #限制最小压缩,小于1字节文件不进行压缩 gzip_min_length 1; #压缩比,级别为1-9,级别越高相应CPU 占用也越多 gzip_comp_level 3; #压缩文件类型 gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/json;

1

2

3

4

5

6

7

8

    #开启 gzip 压缩功能以提高传输效率、节约带宽

    gzip  on;

    #限制最小压缩,小于1字节文件不进行压缩

    gzip_min_length 1;

    #压缩比,级别为1-9,级别越高相应CPU 占用也越多

    gzip_comp_level 3;

    #压缩文件类型

    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png  application/json;

location的匹配规则

空格:默认匹配、普通匹配 location / { root /home; } =:精确匹配 location = /xxx.png { root /home; } ~*:匹配正则表达式,不区分大小写 location ~* \.(GIF|jpg|png|jpeg) { root /home; } ~:匹配正则表达式,区分大小写 location ~ \.(GIF|jpg|png|jpeg) { root /home; } ^~:以某个字符路径开头 location ^~ /xxx/img { root /home; }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

空格:默认匹配、普通匹配

location / {

    root /home;

}

=:精确匹配

location = /xxx.png {

    root /home;

}

~*:匹配正则表达式,不区分大小写

location ~* \.(GIF|jpg|png|jpeg) {

    root /home;

}

~:匹配正则表达式,区分大小写

location ~ \.(GIF|jpg|png|jpeg) {

    root /home;

}

^~:以某个字符路径开头

location ^~ /xxx/img {

    root /home;

}

Hosts 文件管理工具(兼容多平台):SwitchHosts,通过它可避免手动去编辑系统 hosts 文件

Nginx 的跨域

CORS(Cross-Origin Resource Sharing)

解决跨域的方案Jsonp, SpingBoot Cors, Nginx

# SpringBoot CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("http://www.xxx.com"); # 设置是否发送 cookie 信息 config.addAllowCredentials(true); # 设置允许请求的方式 config.addAllowedMethod("*"); #设置允许的 header config.addAllowedHeader("*"); # Nginx server{ ... # 允许跨域请求的域,*代表所有 add_header 'Access-Control-Allow-Origin' *; # 允许带上 cookie 请求 add_header 'Access-Control-Allow-Credentials' 'true'; # 允许请求的方法,比如 GET/POST/PUT/DELETE add_header 'Access-Control-Allow-Methods' *; # 允许请求的 header add_header 'Access-Control-Allow-Headers' *; ... }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

# SpringBoot

CorsConfiguration config = new CorsConfiguration();

config.addAllowedOrigin("http://www.xxx.com");

# 设置是否发送 cookie 信息

config.addAllowCredentials(true);

# 设置允许请求的方式

config.addAllowedMethod("*");

#设置允许的 header

config.addAllowedHeader("*");

 

 

# Nginx

server{

...

    # 允许跨域请求的域,*代表所有

    add_header 'Access-Control-Allow-Origin' *;

    # 允许带上 cookie 请求

    add_header 'Access-Control-Allow-Credentials' 'true';

    # 允许请求的方法,比如 GET/POST/PUT/DELETE

    add_header 'Access-Control-Allow-Methods' *;

    # 允许请求的 header

    add_header 'Access-Control-Allow-Headers' *;

...

}

Nginx静态资源防盗链

server{ .. # 对源站点验证 valid_referers *.xxx.com; # 非法引入会进入下方判断 if($invalid_referer) { return 404; } ... }

1

2

3

4

5

6

7

8

9

10

server{

..

    # 对源站点验证

    valid_referers *.xxx.com;

    # 非法引入会进入下方判断

    if($invalid_referer) {

        return 404;

    }

...

}

Java架构师成长直通车:LVS+Nginx实现高可用集群Java架构师成长直通车:LVS+Nginx实现高可用集群正在上传…重新上传取消Java架构师成长直通车:LVS+Nginx实现高可用集群

负载均衡

四层负载均衡

  • F5硬负载均衡(基于硬件)
  • LVS四层负载均衡
  • HAProxy 四层负载均衡
  • Nginx 四层负载均衡

七层负载均衡

  • Nginx 七层负载均衡
  • HAProxy 七层负载均衡
  •  Apache 七层负载均衡

DNS地域负载均衡

层级 名称 说明
第七层 应用层 与用户行为交互
第六层 表示层 定义数据格式以及数据加密
第五层 会话层 创建、管理以及销毁会话
第四层 传输层 创建、管理请求端到响应端(端到端)的连接
第三层 网络层 请求端的IP地址
第二层 数据链路层 提供介质访问与链路管理
第一层 物理层 传输介质,物理媒介

集群配置

本地集群可使用多台虚拟机,或直接在单台Linux机器上使用 Docker

docker run -d -it --rm tomcat:8.0

1

docker run -d -it --rm tomcat:8.0

我们连续启动了3个Docker

#配置上游服务器 upstream xxx { server 172.17.0.2:8080; server 172.17.0.3:8080; server 172.17.0.4:8080; } server { listen: 80; server_name www.xxx.com; location / { proxy_pass http://xxx; } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

#配置上游服务器

upstream xxx {

server 172.17.0.2:8080;

server 172.17.0.3:8080;

server 172.17.0.4:8080;

}

 

server {

listen: 80;

server_name www.xxx.com;

location / {

proxy_pass http://xxx;

}

}

Jmeter

下载链接:https://jmeter.apache.org/download_jmeter.cgi

下载后 Windows 双击 jmeter.bat,macOS双击 jmeter 执行文件即可打开可视化窗口

1、测试计划>线程组:配置线程(用户)数和循环次数

2、线程组>采样器>HTTP 请求:配置请求的域名、端口等

3、测试计划>监听器:添加查看结果树聚合报告用表格查看结果

着重对比单机和集群的异常率(聚合报告),以了解所能负载的用户数,一般超过20%即超出了异常极限

负载均衡 – 轮询、权重

负载均衡默认使用轮询,平均逐一分配给集群内的服务器;可通过配置来设置加权轮询

# 加权轮询配置(数值越小,分配的流量越少) upstream xxx { server 172.17.0.2:8080 weight=1; server 172.17.0.3:8080 weight=3; server 172.17.0.4:8080 weight=5; }

1

2

3

4

5

6

# 加权轮询配置(数值越小,分配的流量越少)

upstream xxx {

server 172.17.0.2:8080 weight=1;

server 172.17.0.3:8080 weight=3;

server 172.17.0.4:8080 weight=5;

}

 

upstream指令参数

更多内容参见:官方文档,其中各参数的配置方法同上述的 weight,以下设置均位于 upstream 中

  • max_conns
    限制一台服务器的最大连接数,在有多个 worker 进程时因共享内存实际会超出
    默认为0,不做任何限制
    通过将 worker 设置为1,使用 Jmeter 可进行测试(线程组Ramp-Up 时间设置为0)
    示例:server 172.17.0.2:8080 max_conns=2;
  • slow_start
    让服务器缓慢加入集群,配合 weight 使用,当前仅适用于商业版
    示例:server 172.17.0.4:8080 weight=5 slow_start=60s;
  • down
    标识服务器为不可用
    示例:server 172.17.0.2:8080 down;
  • backup
    标识服务器为备用服务器,在主服务器都挂掉时启用
    示例:server 172.17.0.2:8080 backup;
  • max_fails
    fail_timeout所设置时间内失败尝试次数,默认值为1,若达到失败尝试上限次数,则判定为宕机
  • fail_timeout
    配合 max_fails 使用,默认为10秒,对判定为宕机的服务在达到 fail_timeout 的时长后会重新尝试连接
    示例:server 172.17.0.2:8080 backup max_fails=2 fail_timout=10s;

keepalive吞吐量,用于设置长连接处理的数量

示例:keepalive 32;

对于 http,还应进行如下设置(location 内):

proxy_http_version 1.1; # 设置长连接 http 版本为1.1 proxy_set_header Connection ""; # 清除 connection header 信息

1

2

proxy_http_version 1.1; # 设置长连接 http 版本为1.1

proxy_set_header Connection ""; # 清除 connection header 信息

 

负载均衡 – ip_hash

hash 算法:用户ip哈希对服务端节点数取模获取下标:

hash(ip) % node_counts = index

数据库分表哈希算法同理,如将 ip 替换为 pid

配置:

upstream xxx { ip_hash; server ... }

1

2

3

4

upstream xxx {

    ip_hash;

    server ...

}

nginx/src/http/modules/ngx_http_upstream_ip_hash_module.c

... iphp->addrlen = 3; ... for (i = 0; i < (ngx_uint_t) iphp->addrlen; i++) { hash = (hash * 113 + iphp->addr[i]) % 6271; } ...

1

2

3

4

5

6

7

         ...

         iphp->addrlen = 3;

         ...

         for (i = 0; i < (ngx_uint_t) iphp->addrlen; i++) {

             hash = (hash * 113 + iphp->addr[i]) % 6271;

         }

         ...

根据以上 Nginx 的源码文件可以分析出 ip_hash实际上取的是 IP 的前3段,因此通过内网(如hash( 192 168 1 ))将访问同一台主机。

注意:使用 ip_hash 如果有服务器出现故障不能直接移除,而是应将其标记为 down

一致性哈希算法

ip_hash 在有节点宕机时节点数就会发生变化,自然所有下标也即访问的主机也会发生变化 。这样用户会丢失原有的 session,缓存无效,基于这一缺点引入了一致性哈希算法。

Java架构师成长直通车:LVS+Nginx实现高可用集群Java架构师成长直通车:LVS+Nginx实现高可用集群正在上传…重新上传取消Java架构师成长直通车:LVS+Nginx实现高可用集群

一致性哈希算法在0到232-1之间,根据用户和服务器节点的哈希值按顺时针就近原则决定用户所访问的服务器节点,这样不论是增加服务器节点还是减少服务器节点,都只有少数用户受到影响,并且依然保持相同的原则。

负载均衡 – url_hash,least_conn

url_hash 是根据请求 url 进行哈希,然后与节点数取模得出下标

hash(url) % node_counts = index

upstream xxx { hash $request_uri; server ... }

1

2

3

4

upstream xxx {

hash $request_uri;

server ...

}

url_hash 在 url 发生变化时(如请求链接后多一个/)请求的服务器就会发生变化

least_conn 是最小连接数,实际含义是将请求发送到 连接数/权重 值最小的服务器上,以避免有些服务器节点出现闲置的状况:

upstream xxx { least_conn; server ... }

1

2

3

4

upstream xxx {

least_conn;

server ...

}

 

缓存

  • 静态资源缓存浏览器
    expires 指令

    location /static{ alias /xxx/yyy; #expires 10s; # 缓存10s后过期 expires @22h30m; # 指定缓存过期时间 # expires -1h; # 缓存1小时前失效,即不缓存 # expires epoch; # 过期时间为1 January,1970,00:00:01 GMT,同样不进行缓存 # expires off; # 不设置,会使用浏览器默认值 # expires max; # 过期时间设置为31 December 2037 23:59:59 GMT,“Cache-Control”的值为10年 }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    location /static{

    alias /xxx/yyy;

    #expires 10s;    # 缓存10s后过期

            expires @22h30m; # 指定缓存过期时间

            # expires -1h; # 缓存1小时前失效,即不缓存

            # expires epoch; # 过期时间为1 January,1970,00:00:01 GMT,同样不进行缓存

            # expires off; # 不设置,会使用浏览器默认值

            # expires max; # 过期时间设置为31 December 2037 23:59:59 GMT,“Cache-Control”的值为10年

    }

  • 上游服务器资源缓存到 Nginx端

    # 设置缓存保存的目录 # keys_zone 设置共享内存及占用的空间大小,mycache为自定义名称,在下方需使用 # max_size 设置缓存总大小 # inactive 缓存超出所指定的时间进行清理 proxy_cache_path /usr/local/nginx/upstream_cache keys_zone=mycache:5m max_size=1g inactive=30m use_temp_path=off; server { ... # 开启并使用缓存,mycache为上方所指定的名称 proxy_cache mycache; # 针对指定状态码的缓存过期时间 proxy_cache_valid 200 304 8h; ... }

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    # 设置缓存保存的目录

    # keys_zone 设置共享内存及占用的空间大小,mycache为自定义名称,在下方需使用

    # max_size 设置缓存总大小

    # inactive 缓存超出所指定的时间进行清理

    proxy_cache_path /usr/local/nginx/upstream_cache keys_zone=mycache:5m max_size=1g inactive=30m use_temp_path=off;

     

    server {

    ...

    # 开启并使用缓存,mycache为上方所指定的名称

    proxy_cache mycache;

    # 针对指定状态码的缓存过期时间

    proxy_cache_valid 200 304 8h;

    ...

    }

Nginx配置SSL(HTTPS)

首先要确定 Nginx有没安装了ssl 模块

nginx -V # 如未安装需进入Nginx 源码目录重新编译,在原命令基础上添加--with-http_ssl_module再进行编译安装 ./configure --prefix=/usr/local/nginx ... --with-http_ssl_module

1

2

3

4

nginx -V

# 如未安装需进入Nginx 源码目录重新编译,在原命令基础上添加--with-http_ssl_module再进行编译安装

 

./configure --prefix=/usr/local/nginx ... --with-http_ssl_module

配置示例

server { listen 443; ... # 开启ssl ssl on; # 配置ssl证书 ssl_certificate xxx.crt; ssl_certificate_key xxx.key; # ssl会话缓存 ssl_session_cache shared:SSL:1m; # ssl会话超时时间 ssl_session_timeout 5m; # 配置加密套件,写法遵循 openssl 标准 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; ssl_prefer_server_ciphers on; }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

server {

listen 443;

...

 

# 开启ssl

ssl on;

# 配置ssl证书

ssl_certificate xxx.crt;

ssl_certificate_key xxx.key;

 

# ssl会话缓存

ssl_session_cache shared:SSL:1m;

# ssl会话超时时间

ssl_session_timeout 5m;

 

# 配置加密套件,写法遵循 openssl 标准

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;

ssl_prefer_server_ciphers  on;

}

 

动静分离

  • 分布式
  • 前后端解耦
  • 静态归 Nginx
  • 接口服务化

静态数据:css/js/html/images/audios/videos/…

动态数据:得到的响应可能会和上一次不同

实现方式

  • CDN:将静态资源放到第三方CDN 平台
  • Nginx:将静态资源放到 Nginx 服务器或上游集群服务器

动静分离的问题

  • 跨域
    • SpringBoot
    • Nginx
    • Jsonp
  • 分布式会话
    • 分布式缓存中间件Redis

Nginx高可用HA

主、备 Nginx

Keepalived:

  • 解决单点故障
  • 组件免费
  • 可以实现高可用HA机制
  • 基于VRRP协议(Virtual Router Redundancy Protocol)
    • 解决内网单机故障的路由协议
    • 构建有多个路由器 MASTER BACKUP
    • 虚拟 IP – VIP(Virtual IP Address)

Keepalived安装

下载地址:keepalived.org

以当前版本2.0.19为例:

tar -zxvf keepalived-2.0.19.tar.gz cd keepalived-2.0.19/ ./configure --prefix=/usr/local/keepalived --sysconf=/etc make && make install

1

2

3

4

tar -zxvf keepalived-2.0.19.tar.gz

cd keepalived-2.0.19/

./configure --prefix=/usr/local/keepalived --sysconf=/etc

make && make install

主要配置

# vi /etc/keepalived/keepalived.conf lobal_defs { # 路由id: 当前安装Keepalived 节点主机的标识符,要求全局唯一 router_id LVS_151 } # 计算机节点 vrrp_instance VI_1 { # 表示状态,MASTER/BACKUP state MASTER # 当前实例绑定的网卡,根据实际状况配置 interface eth0 # 保证主备节点一致即可 virtual_router_id 51 # 优先级/权重,优先级高的在主节点故障时优先成为主机点 priority 100 # 主备之间同步检查的时间间隔,默认1s advert_int 1 # 认证授权的密码,防止非法节点的进入 authentication { auth_type PASS auth_pass 1111 } # 虚拟IP地址,可以有多个 virtual_ipaddress { 192.168.1.161 } }

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

# vi /etc/keepalived/keepalived.conf

lobal_defs {

   # 路由id: 当前安装Keepalived 节点主机的标识符,要求全局唯一

   router_id LVS_151

}

 

# 计算机节点

vrrp_instance VI_1 {

    # 表示状态,MASTER/BACKUP

    state MASTER

    # 当前实例绑定的网卡,根据实际状况配置

    interface eth0

    # 保证主备节点一致即可

    virtual_router_id 51

    # 优先级/权重,优先级高的在主节点故障时优先成为主机点

    priority 100

    # 主备之间同步检查的时间间隔,默认1s

    advert_int 1

    # 认证授权的密码,防止非法节点的进入

    authentication {

        auth_type PASS

        auth_pass 1111

    }

    # 虚拟IP地址,可以有多个

    virtual_ipaddress {

        192.168.1.161

    }

}

启动和关闭服务

cd /usr/local/keepalived/sbin/ ./keepalived # 此时停止服务需使用 ps -ef|grep keepalived 获取进程号然后执行 kill -9 [pid] # 注册为系统服务,需进入源码目录拷贝一些文件 cd keepalived-2.0.19/keepalived/etc/ cp init.d/keepalived /etc/init.d/ cp sysconfig/keepalived /etc/sysconfig/ systemctl daemon-reload # 刷新服务 # 此时即可以系统服务操作 keepalived 了 systemctl start keepalived.service

1

2

3

4

5

6

7

8

9

10

cd /usr/local/keepalived/sbin/

./keepalived

# 此时停止服务需使用 ps -ef|grep keepalived 获取进程号然后执行 kill -9 [pid]

# 注册为系统服务,需进入源码目录拷贝一些文件

cd keepalived-2.0.19/keepalived/etc/

cp init.d/keepalived /etc/init.d/

cp sysconfig/keepalived /etc/sysconfig/

systemctl daemon-reload # 刷新服务

# 此时即可以系统服务操作 keepalived 了

systemctl start keepalived.service

本地测试可通过绑定hosts 文件指向虚拟 IP 地址,备服参照主服配置,state 设置为BACKUP、权重设置低于主服务器即可

编写检测脚本/etc/keepalived/check_nginx_alive_or_not.sh,在 Nginx 中断时自动启动服务:

#!/bin/bash A=`ps -C nginx --no-header |wc -l` # 判断Nginx是否宕机,如果宕机了,尝试重启 if [ $A -eq 0 ];then /usr/local/nginx/sbin/nginx # 等待一会再次检查Nginx,如果没有启动成功,则停止Keepalived,使其启动备用机 sleep 3 if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then killall keepalived fi fi

1

2

3

4

5

6

7

8

9

10

11

12

#!/bin/bash

 

A=`ps -C nginx --no-header |wc -l`

# 判断Nginx是否宕机,如果宕机了,尝试重启

if [ $A -eq 0 ];then

        /usr/local/nginx/sbin/nginx

        # 等待一会再次检查Nginx,如果没有启动成功,则停止Keepalived,使其启动备用机

        sleep 3

        if [ `ps  -C nginx --no-header |wc -l` -eq 0 ];then

                killall keepalived

        fi

fi

添加配置

chmod +x check_nginx_alive_or_not.sh # vi /etc/keepalived/keepalived.conf vrrp_script check_nginx_alive { script "/etc/keepalived/check_nginx_alive_or_not.sh" interval 2 # 每隔两秒运行上一行脚本 # weight 10 # 如果脚本运行成功,则升级权重+10 weight -10 # 如果脚本运行失败,则降低权重-10 } vrrp_instance VI_1 { ... track_script { check_nginx_alive # 追踪 Nginx 脚本 } ... }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

chmod +x check_nginx_alive_or_not.sh

# vi /etc/keepalived/keepalived.conf

 

vrrp_script check_nginx_alive {

        script "/etc/keepalived/check_nginx_alive_or_not.sh"

        interval 2 # 每隔两秒运行上一行脚本

        # weight 10 # 如果脚本运行成功,则升级权重+10

        weight -10 # 如果脚本运行失败,则降低权重-10

}

 

vrrp_instance VI_1 {

...

    track_script {

        check_nginx_alive # 追踪 Nginx 脚本

    }

...

}

 

Keepalived 双主热备

以上使用的Keepalived 双机主备一个主要的缺点是在MASTER 运行正常的情况下,从服务器将保持闲置的状态,因此让我们来了解一下双主热备,即互为主备:

通过设置多个虚拟 IP 并使用 DNS 轮询的方式实现(阿里、腾讯等平台的域名解析中均可将A记录如 www 解析到不同域名并设置权重)

配置类似前面,将原有的配置拷贝一份,比如在备用服务器上再使用另一个虚拟 IP 配置其为主服务器:

vrrp_instance VI_2 { state MASTER interface eth0 virtual_router_id 52 priority 100 ... virtual_ipaddress { 192.168.1.162 } }

1

2

3

4

5

6

7

8

9

10

vrrp_instance VI_2 {

    state MASTER

    interface eth0

    virtual_router_id 52

    priority 100

    ...

    virtual_ipaddress {

        192.168.1.162

    }

}

相应地原主服务器也需要再进行一份备用服务器的配置

Java架构师成长直通车:LVS+Nginx实现高可用集群Java架构师成长直通车:LVS+Nginx实现高可用集群正在上传…重新上传取消Java架构师成长直通车:LVS+Nginx实现高可用集群

LVS负载均衡

  • Linux Virtual Server
  • 章文嵩博士主导的开源负载均衡项目
  • LVS(ipvs)已被集成到 Linux 内核中
  • 负载均衡调度器(四层)

官方网站:http://linux-vs.org/

为什么要使用LVS+Nginx?

  • LVS基于四层,工作效率高
  • 单个 Nginx 承受压力有限,需要集群
  • LVS 充当 Nginx集群的调度者
  • Nginx 接受请求来回,LVS 可以只接受不响应

LVS 的三种模式

  • NAT
    请求通过 LVS,响应通过 LVS
  • TUN
    请求(上行)经过 LVS,响应(下行)不经过 LVS,每个节点必须要有网卡,节点对公网暴露
  • DR(Direct Routing)
    请求经过 LVS,响应通过路由,推荐使用Java架构师成长直通车:LVS+Nginx实现高可用集群Java架构师成长直通车:LVS+Nginx实现高可用集群正在上传…重新上传取消Java架构师成长直通车:LVS+Nginx实现高可用集群

LVS DR模式搭建

服务器示例如图所示

VIP:虚拟 IP

RIP:真实服务器 IP

# LVS及Nginx服务器上关闭网络配置管理器,避免本地服务器的网络接口冲突 systemctl stop NetworkManager systemctl disable NetworkManager # 虚拟IP配置(LVS) cd /etc/sysconfig/network-scripts/ # vi ifcfg-eth0:1(可拷贝ifcfg-eth0进行修改,根据不同机器eth0可能会存在变化,只需保留如下几行) BOOTPROTO="static" DEVICE="eth0:1" ONBOOT="yes" IPADDR=192.168.1.150 NETMASK=255.255.255.0 # 重启网络让配置生效 service network restart # 安装集群管理工具(LVS服务器) yum install -y ipvsadm # 查看集群信息 ipvsadm -Ln # 虚拟 IP 配置(Nginx) cd /etc/sysconfig/network-scripts/ cp ifcfg-lo ifcfg-lo:1 # vi ifcfg-lo:1,仅修改如下行,其余保留不变 DEVICE=lo:1 IPADDR=192.168.1.150 NETMASK=255.255.255.255 # 重启网络 ifup lo # 或 systemctl restart network

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

# LVS及Nginx服务器上关闭网络配置管理器,避免本地服务器的网络接口冲突

systemctl stop NetworkManager

systemctl disable NetworkManager

 

# 虚拟IP配置(LVS)

cd /etc/sysconfig/network-scripts/

# vi ifcfg-eth0:1(可拷贝ifcfg-eth0进行修改,根据不同机器eth0可能会存在变化,只需保留如下几行)

BOOTPROTO="static"

DEVICE="eth0:1"

ONBOOT="yes"

IPADDR=192.168.1.150

NETMASK=255.255.255.0

# 重启网络让配置生效

service network restart

 

# 安装集群管理工具(LVS服务器)

yum install -y ipvsadm

# 查看集群信息

ipvsadm -Ln

 

# 虚拟 IP 配置(Nginx)

cd /etc/sysconfig/network-scripts/

cp ifcfg-lo ifcfg-lo:1

# vi ifcfg-lo:1,仅修改如下行,其余保留不变

DEVICE=lo:1

IPADDR=192.168.1.150

NETMASK=255.255.255.255

# 重启网络

ifup lo # 或 systemctl restart network

注:云服务器需购买负载均衡或虚拟 IP 才可实现配置, Nginx 服务器配置对应本地回环(lo)的原因是其处理响应

LVS-DR模式的其它配置

arp-ignore:ARP 响应级别(处理请求)

  • 0:只要本机配置了 ip,就能响应请求
  • 1:请求的目标地址到达对应的网络接口,才会响应请求

推荐配置为1

arp-announce:ARP通告行为(返回响应)

  • 0:本地上任何网络接口都向外通告,所有的网卡都能接收到通告
  • 1:尽可能避免本网卡与不匹配的目标进行通告
  • 2:只在本网卡通告

推荐配置为2

# vi /etc/sysctl.conf(Nginx服务器上) net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.default.arp_ignore = 1 net.ipv4.conf.lo.arp_ignore = 1 net.ipv4.conf.all.arp_announce = 2 net.ipv4.conf.default.arp_announce = 2 net.ipv4.conf.lo.arp_announce = 2 # 刷新配置 sysctl -p # 增加网关,用于接收数据报文,将到达本地的请求交给 lo 处理 route add -host 192.168.0.150 dev lo:1 # 若无命令执行yum install net-tools -y安装 route -n # 查看 route delete # 删除 # 设置开机配置,避免重启后配置失效 echo "route add -host 192.168.0.150 dev lo:1" >> /etc/rc.local

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

# vi /etc/sysctl.conf(Nginx服务器上)

net.ipv4.conf.all.arp_ignore = 1

net.ipv4.conf.default.arp_ignore = 1

net.ipv4.conf.lo.arp_ignore = 1

net.ipv4.conf.all.arp_announce = 2

net.ipv4.conf.default.arp_announce = 2

net.ipv4.conf.lo.arp_announce = 2

 

# 刷新配置

sysctl -p

 

# 增加网关,用于接收数据报文,将到达本地的请求交给 lo 处理

route add -host 192.168.0.150 dev lo:1 # 若无命令执行yum install net-tools -y安装

route -n # 查看

route delete # 删除

# 设置开机配置,避免重启后配置失效

echo "route add -host 192.168.0.150 dev lo:1" >> /etc/rc.local

LVS配置

ipvsadm -A -t 192.168.1.150:80 -s rr # 编辑:ipvsadm -E -t 192.168.1.150:80 -s rr -p 5 ipvsadm -a -t 192.168.1.150:80 -r 192.168.1.171:80 -g ipvsadm -a -t 192.168.1.150:80 -r 192.168.1.172:80 -g ipvsadm -S # 保存规则避免重启失效 # 相关参数说明请参见 ipvsadm -h # 查看 ipvsadm -Ln # 查看持久化连接: ipvsadm -Ln --persistent-conn # 查看请求过期时间及源IP和目标IP: ipvsadm -Lnc # 查看状态: ipvsadm -Ln --status # 设置tcp tcpfin udp 的过期时间(通常保持默认,以下设置用于测试) ipvsadm --set 1 1 1 # 查看过期时间 ipvsadm -Ln --timeout

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

ipvsadm -A -t 192.168.1.150:80 -s rr

# 编辑:ipvsadm -E  -t 192.168.1.150:80 -s rr -p 5

ipvsadm -a -t 192.168.1.150:80 -r 192.168.1.171:80 -g

ipvsadm -a -t 192.168.1.150:80 -r 192.168.1.172:80 -g

ipvsadm -S # 保存规则避免重启失效

# 相关参数说明请参见 ipvsadm -h

# 查看 ipvsadm -Ln

# 查看持久化连接:  ipvsadm -Ln --persistent-conn

# 查看请求过期时间及源IP和目标IP: ipvsadm -Lnc

# 查看状态: ipvsadm -Ln --status

 

# 设置tcp tcpfin udp 的过期时间(通常保持默认,以下设置用于测试)

ipvsadm --set 1 1 1

# 查看过期时间

ipvsadm -Ln --timeout

 

-A:添加集群
-t:tcp协议
ip地址:设定集群的访问ip,也就是LVS的虚拟ip
-s:设置负载均衡的算法,rr表示轮询
-p:设置连接持久化的时间,默认300秒
-a:添加真实服务器
-r:真实服务器的ip地址
-g:设定DR模式

Keepalived+LVS高可用

在主备 LVS 服务器上安装 Keepalived,方法参见上方Keepalived安装

global_defs { ... } vrrp_instance VI_1 { ... } # 配置集群地址访问的 IP+端口,端口和 Nginx 保持一致 virtual_server 192.168.1.150 80 { # 健康检查时间,单位:秒 delay_loop 6 # 配置负载均衡算法,默认是轮询 lb_algo rr # 设置LVS的模式:NAT|TUN|DR lb_kind DR # 设置会话持久化时间 persistence_timeout 10 # 协议 protocol TCP # 负载均衡真实服务器,即Nginx节点 IP real_server 192.168.1.171 80 { # 轮询的默认权重配比 weight 1 # 设置健康检查 TCP_CHECK { # 检查的端口 connect_port 80 # 超时时间 connect_timeout 2 # 重试次数(s) nb_get_retry 5 # 间隔时间(s) delay_before_retry 3 } } real_server 192.168.1.172 80 { weight 1 ... } } # 清除此前规则 ipvsadm -C systemctl restart keepalived

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

35

36

37

38

39

40

41

42

43

44

45

46

global_defs {

   ...

}

 

vrrp_instance VI_1 {

    ...

}

 

# 配置集群地址访问的 IP+端口,端口和 Nginx 保持一致

virtual_server 192.168.1.150 80 {

    # 健康检查时间,单位:秒

    delay_loop 6

    # 配置负载均衡算法,默认是轮询

    lb_algo rr

    # 设置LVS的模式:NAT|TUN|DR

    lb_kind DR

    # 设置会话持久化时间

    persistence_timeout 10

    # 协议

    protocol TCP

    # 负载均衡真实服务器,即Nginx节点 IP

    real_server 192.168.1.171 80 {

        # 轮询的默认权重配比

        weight 1

        # 设置健康检查

        TCP_CHECK {

            # 检查的端口

            connect_port 80

            # 超时时间

            connect_timeout 2

            # 重试次数(s)

            nb_get_retry 5

            # 间隔时间(s)

            delay_before_retry 3

        }

    }

 

    real_server 192.168.1.172 80 {

        weight 1

        ...

    }

}

 

# 清除此前规则

ipvsadm -C

systemctl restart keepalived

以上即实现了高可用的 LVS+Keepalived 方案

LVS 负载均衡算法

静态算法

根据 LVS 自有固定算法分发用户请求

1、轮询(Round Robin – rr):平均分配请求(同 Nginx的轮询)

2、加权轮询(Weight Round Robin – wrr): 按照权重比例分配用户请求,权重越高,分配请求越多(同 Nginx 的权重)

3、源地址散列(Source Hash – sh):同 IP 用户由相同 RS 处理(同 Nginx 的 ip_hash)

4、目标地址散列(Destination Hash – dh):根据不同 url 请求不同的 RS(同 Nginx的 url_hash)

动态算法

根据流量或服务器压力不同分配用户请求

1、最小连接数(Least Connections – lc):将请求分配给连接最小的服务器

2、 加权最小连接数(Weight Least Connections – wlc):用数值表示服务器处理性能,将请求分发到性能好且空闲的服务器

3、 最短期望延迟(Shortest Expected Delay – sed):是一种特殊的 wlc 算法,将请求交给运算结果最小的服务器,设服务器 A、B、C的权重为1、2、3,计算方式如下:

  • A: (1+1)/1=2
  • B: (1+2)/2=3/2
  • C: (1+3)/3=4/3

4、 最少队列调试(Never Queue = nq):如有 RS 的连接数等于0,直接将请求分配过去,无需排队等待运算

注:RS(Real Server)

LVS 最常使用的负载均衡算法为 wlc 或 wrr

官方文档

 

常见问题

1、nginx: [error] open() “/var/run/nginx/nginx.pid” failed (2: No such file or directory)

mkdir /var/run/nginx /usr/local/nginx/sbin/nginx -s reload # 未启动则直接执行/usr/local/nginx/sbin/nginx

1

2

mkdir /var/run/nginx

/usr/local/nginx/sbin/nginx -s reload # 未启动则直接执行/usr/local/nginx/sbin/nginx

2、nginx: [error] invalid PID number “” in “/var/run/nginx/nginx.pid”

/usr/local/nginx sbin/nginx -c conf/nginx.conf # 指定配置文件 sbin/nginx -s reload

1

2

3

/usr/local/nginx

sbin/nginx -c conf/nginx.conf # 指定配置文件

sbin/nginx -s reload

3、默认 Docker 容器中没有 vi 等编辑器,可进行安装,也可在宿主机执行如下命令,然后根据返回的路径编辑容器中的文件

docker inspect --format='{{.GraphDriver.Data.MergedDir}}' 容器 ID 或名称

1

docker inspect --format='{{.GraphDriver.Data.MergedDir}}' 容器 ID 或名称

4、*** WARNING – this build will not support IPVS with IPv6. Please install libnl/libnl-3 dev libraries to support IPv6 with IPVS.

yum -y install libnl libnl-devel

1

yum -y install libnl libnl-devel