因为这几天一直忙着搭建组内一个用于前端人员接口联调的测试环境,好多天没写文章了,昨天基本把测试环境做好了。现在简单讲一下这件事情的背景,以及解决方案。现在我们组内只有一套测试环境,用于测试人员测试使用。但是接口开发完毕,前端开发人员进行接口联调时,经常遇到这种尴尬的场景:比如A需求后台是由后台人员A开发的,同时还有B需求是由后台人员B开发的,每个需求前端也有相应的同学在开发,两个需求同时进行接口联调,如果仅仅使用现有的开发环境,只能临时将两个需求的代码合到一起,然后发到测试环境,有时候还会发生冲突,非常的不方便。以此为出发点,就想做一个测试环境,每个人都有自己的环境,相互之间互不影响。
起初希望每个人都有一台独立的物理机,然后每个人在相应的物理机上起服务。单独部署一台Nginx反向代理服务器,通过Nginx对各台物理机做反向代理,将前端请求转发到相应的机器上。但是由于组内资源不够,最终采取了一个降级方案,三人共用一台物理机,在一台物理机上建三个用户,每个人有自己的工作路径,每个人服务的端口不同,用于区分同一台机器上不同开发人员的服务。通过Nginx可以实现前端接口到后台网关服务的映射,但是还存在另一个问题,网关服务都是调用的Dubbo服务,怎么区分不同开发人员的Dubbo服务,最终的解决方案是在启动dubbo服务的时候,给一个version字段,每个人的dubbo服务通过version区分。最终实现如下图:
实际操作过程中,还有一系列其他问题,比如在我们后台网关之前,还存在一套鉴权网关,主要做接口权限鉴定、json归一化处理(统一json格式回给前端),所以相应的Nginx要完成一个功能,将后台服务返回的数据json转成前端需要的格式。另外,我们的后台服务报文是是通过Sha1加密的,所以Nginx也要完成加密功能(最终通过openresty,使用lua脚本实现上述功能)。关于openresty及lua脚本,本篇文章暂不介绍,只介绍Nginx的简单使用。
1. 概述
1.1 什么是Nginx?
Nginx(engine x)是一个异步框架的 Web服务器,也可以用作反向代理,负载平衡器和HTTP缓存。
1.2 什么是反向代理?
讲反向代理之前,先讲一个概念,什么是正向代理(Forward proxy)。如果没有特殊说明,我们所说的代理就是指正向代理。正向代理过程,隐藏了真实请求的客户端,服务端不知道真正的客户端是谁,客户端的请求都是被代理服务器代替来请求的。比如某些用来上网的工具(访问Google等站点)就是一个正向代理,正常浏览器访问http://google.com肯定是无法访问的,如果存在一个国外的服务器,当你访问google时,替你向google发了一次请求,并且把结果传给你,这时候你在浏览器浏览器的表象就是你可以访问google了,但是谷歌服务器并不知道真正访问的客户端是你的浏览器,也就是说正向代理是代理客户端,对服务器而言隐藏了真实的客户端。
反向代理(Reverse Proxy)方式是指以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。看概念好像跟正向代理差不多,但其实可以看到最直观的一点区别,正向代理你浏览器访问的额是目标地址,而反向代理浏览器访问的是代理服务器。举个例子,当我们访问http://baidu.com时,我们都知道百度有成千上万台服务器在工作,所以真正响应你请求的服务器是未知的,但其实我们也不关心到底哪台服务器为我们工作了,我们只需要知道反向代理服务器,就能获取服务。浏览器并不知道真正访问的服务端是哪个,也就是说反向代理是代理服务器,对于浏览器而言隐藏了真实的服务端。
2. Nginx的安装和使用
本文所使用的服务器操作系统是Centos,Nginx的安装方式可以有两种,一种是源码包安装、另一种是yum安装。yum安装比较简单,不容易出错。源码包安装是先将Nginx的源码下载下来,在自己的系统里编译生成可执行文件,然后执行,因为是在自己的系统上编译的,在性能上也许更好,这里投个懒,就不使用源码包这种方式了,有兴趣的同学可以去了解一下。
2.1 安装
yum -y install nginx
-y表示安装过程中自动键入Y继续安装,安装成功后Nginx目录如下:
- /usr/sbin:Nginx指令路径,文件夹下有一个nginx文件,可以通过nginx启动、暂停、重启Nginx服务
- /etc/nginx:Nnginx配置文件地址,默认配置文件为nginx.conf,并且默认配置文件中还定义了一个配置文件夹,路径为/etc/nginx/con.d,该文件夹下所有的.conf文件都是Nginx的配置文件
- /var/log/nginx:Nginx默认配置文件定义的默认log地址,Nginx运行后,会生成两个日志文件,access.log访问日志及error.log错误日志
2.2 使用
nginx -s stop 快速关闭Nginx,可能不保存相关信息,并迅速终止web服务。
nginx -s quit 平稳关闭Nginx,保存相关信息,有安排的结束web服务。
nginx -s reload 因改变了Nginx相关配置,需要重新加载配置而重载。
nginx -s reopen 重新打开日志文件。
nginx -c filename 为 Nginx 指定一个配置文件,来代替缺省的。
nginx -t 不运行,而仅仅测试配置文件。nginx 将检查配置文件的语法的正确性,并尝试打开配置文件中所引用到的文件。
nginx -v 显示 nginx 的版本。
nginx -V 显示 nginx 的版本,编译器版本和配置参数。
3. Nginx应用
3.1 http反向代理配置
server {
listen 80;
server_name localhost;
client_max_body_size 1024M;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host:$server_port;
}
}
保存配置文件后重启Nginx,当访问localhost的时候,就相当于访问localhost:8080了
3.2 负载均衡配置
负载均衡是Nginx常用的一个功能,意思是讲请求分摊到多个服务单元上进行执行,例如Web服务器、FTP服务器等,从而共同完成工作任务。简单而言就是当有2台或以上服务器时,根据规则随机的将请求分发到指定的服务器上处理,负载均衡配置一般都需要同时配置反向代理,通过反向代理跳转到负载均衡。而Nginx目前支持自带3种负载均衡策略,还有2种常用的第三方策略。
3.2.1 RR
这是Nginx的默认策略,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
upstream test {
server localhost:8080;
server localhost:8081;
}
server {
listen 80;
server_name localhost;
client_max_body_size 1024M;
location / {
proxy_pass http://test;
proxy_set_header Host $host:$server_port;
}
}
假如upstream中其中一个服务8080挂掉了,访问http://localhost 的时候,也不会有问题,会默认跳转到http://localhost:8081,。因为Nginx会自动判断服务器的状态,如果服务器处于不能访问,就不会跳转到这台服务器,所以也避免了一台服务器挂了影响使用的情况,这就是Nginx的默认策略RR。
3.2.2 权重
指定每个服务的轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
upstream test {
server localhost:8080 weight=9;
server localhost:8081 weight=1;
}
server {
listen 80;
server_name localhost;
client_max_body_size 1024M;
location / {
proxy_pass http://test;
proxy_set_header Host $host:$server_port;
}
}
假如有10次请求,很有可能8081服务被访问1次,8080被访问10次。
3.2.3 ip_hash
上面两种方式都有一个问题,那就是下一个请求来的时候请求可能分发到另外一个服务器,当程序不是无状态的时候(比如服务端采用session保存数据),这时候就有一个很大的很问题。比如把登录信息保存到了session中,那么跳转到另外一台服务器的时候就需要重新登录了,所以很多时候我们需要一个客户只访问一个服务器,那么就需要用ip_hash了,ip_hash的每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
upstream test {
ip_hash;
server localhost:8080;
server localhost:8081;
}
server {
listen 80;
server_name localhost;
client_max_body_size 1024M;
location / {
proxy_pass http://test;
proxy_set_header Host $host:$server_port;
}
}
3.2.4 fair
这是一个第三方策略,按后端服务器的响应时间来分配请求,响应时间短的优先分配。
upstream test {
fair;
server localhost:8080;
server localhost:8081;
}
server {
listen 80;
server_name localhost;
client_max_body_size 1024M;
location / {
proxy_pass http://test;
proxy_set_header Host $host:$server_port;
}
}
3.2.5 url_hash
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,这种策略比较适合后端服务器存在缓存的情况,在upstream中加入hash语句即可。
upstream test {
hash $request_uri;
hash_method crc32;
server localhost:8080;
server localhost:8081;
}
server {
listen 80;
server_name localhost;
client_max_body_size 1024M;
location / {
proxy_pass http://test;
proxy_set_header Host $host:$server_port;
}
}
3.3 https反向代理配置
一些对安全性要求比较高的站点,可能会使用 HTTPS(一种使用 ssl 通信标准的安全 HTTP 协议),https反向代理配置与http反向代理配置基本上是相似的,但需要知道几点:
- https的固定端口号是 443,不同于http的80端口
- SSL标准需要引入安全证书,所以在Nginx配置文件中需要指定证书和它对应的key
server {
#监听443端口。443为知名端口号,主要用于HTTPS协议
listen 443 ssl;
server_name localhost;
#ssl证书文件位置(常见证书文件格式为:crt/pem)
ssl_certificate cert.pem;
#ssl证书key位置
ssl_certificate_key cert.key;
#ssl配置参数(选择性配置)
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
#数字签名,此处使用MD5
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://localhost:8080;
}
}
3.4 静态站点配置
有时候,我们需要配置静态站点(只包含html 文件和一堆静态资源)。比如所有的静态资源都放在了/app/dist目录下,我们只需要在Nginx配置文件中指定首页以及这个站点的host即可。
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
gzip on;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/javascript image/jpeg image/gif image/png;
gzip_vary on;
server {
listen 80;
server_name localhost;
location / {
root /app/dist;
index index.html;
#转发任何请求到 index.html
}
}
}
3.5 兼容http与https配置
当我们升级http服务为https服务时,为了兼顾一些访问者还是通过http访问的方式,我们通常采用两种方式。
- http可以访问,https也可以访问
- http 访问时,重定向到https
3.5.1 兼容方式
server {
listen 80;
listen 443 ssl;
server_name xxxx.com;
charset urf-8;
#ssl on; #保持关闭,或者注释
ssl_certificate /etc/nginx/conf.d/xxxx.crt;
ssl_certificate_key /etc/nginx/conf.d/xxxx.key;
ssl_session_timeout 10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
root /usr/share/nginx/html;
}
3.5.2 重定向方式
server {
listen 80;
server_name www.xxx.com;
#告诉浏览器有效期内只准用 https 访问
add_header Strict-Transport-Security max-age=15768000;
#永久重定向到 https 站点
return 301 https://$server_name$request_uri;
}
3.6 网站动静分离配置
动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路。
http {
upstream test{
server localhost:8080;
server localhost:8081;
}
server {
listen 80;
server_name localhost;
location / {
root /app/dist;
index index.html;
}
# 所有静态请求都由nginx处理
location ~ \.(gif|jpg|jpeg|png|bmp|swf|css|js)$ {
root root /app/dist;
}
# 所有动态请求都转发给tomcat处理
location /api/ {
proxy_pass http://test;
}
#error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /app/dist;
}
}
}
这样我们就可以吧HTML以及图片和css以及js放到/app/dist目录下,而tomcat只负责处理后台处理,例如当我们后缀为gif的时候,Nginx默认会从/app/dist获取到当前请求的动态图文件返回。这里的静态文件跟Nginx是同一台服务器,我们也可以在另外一台服务器,并通过proxy_pass属性指定。
3.7 跨域解决方案
web开发领域中,经常采用前后端分离模式。这种模式下,前端和后端分别是独立的web应用程序,比如后端是Java 程序,前端是React或Vue应用。各自独立的web app在互相访问时,势必存在跨域问题。解决跨域问题一般有两种思路:
- CORS:在后端服务器设置HTTP响应头,把你需要运行访问的域名加入加入Access-Control-Allow-Origin中。
- jsonp:把后端根据请求,构造json数据,并返回,前端用jsonp跨域。
Nginx针对CORS提供了一种跨域解决方案,比如前端后端存在跨域时,前端和后端如果使用http进行交互,请求会被拒绝。这时候可以通过设置后台服务响应头方式解决。
server {
listen 80;
server_name ***.***.com;
location /api/ {
add_header 'Access-Control-Allow-Origin' '$http_origin';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'POST, GET, OPTIONS, DELETE, PUT';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
#跨域OPTIONS请求,set response header后直接204返回
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://api_server;
}
}