Nginx
1 Nginx入门
1.1 Nginx概述
Nginx是一款高性能的http服务器/反向代理服务器以及邮件服务器,cpu、内存资源消耗小,并发量高(5万)。
反向代理、代理服务器、静态服务器。
作用:集群(减轻单台服务器压力)、反向代理(不暴露真实ip地址)、虚拟服务器(一台服务器提供多个网站)、静态的服务器(动静分离)。
选择Nginx?性能好、占用内存CPU资源少、轻量级服务器、同时处理5万条并发连接
安全架构? https防止抓包分析,Nginx反向代理(不暴露ip)、搭建企业白名单和黑名单系统(防盗链)、XSS(脚本注入攻击)、SQL注入、跨域请求、模拟请求(偏向于业务攻击,csrf 解决?token、验证码)、表单重读提交,dos流量攻击(偏向于流量攻击,让别人无法访问,使用Nginx解决)
常用的代理服务器:Nginx、ivs、F5、haproxy
1.2 Windows安装Nginx
Nginx可以做什么?反向代理、静态服务器(动静分离)、虚拟主机(一台服务器一同多个网站)、集群、负载均衡、解决跨域访问问题(使用Nginx搭建企业级API接口网关)、解决Dos攻击、高可用、会话共享、高并发解决方案等
1、下载Nginx
2、解压到相应目录,其中conf主要存放一些配置文件,html存放静态资源,temp存放临时文件,logs存放日志(比如erro.log如果为空,代表没有错误)
3、双击nginx.exe即可运行,访问http://127.0.0.1/是否出现Nginx的欢迎界面即可判断是否安装成功。
1.3 nginx实现反向代理
为什么需要集群?如果只有一台服务器处理客户端请求,那么同时来1亿条请求,岂不是爆炸,服务器可能会瘫痪,所以需要集群。
客户端发送请求,到达Nginx代理服务器后,Nginx根据他的负载均衡策略(轮询、权重、ip地址绑定),选择一台tomcat服务器,然后将该请求转发到tomcat服务器。
这个有什么问题呢?
1、分布式job幂等性问题(用户对于同一操作发起的一次请求或者多次请求的结果是一致的,举个例子,如果你购买商品付款时,第一次付款成功但是由于网络问题没有收到返回结果,第二次再付款,然后呢到另一台服务器了,钱扣了两次)
2、会话共享(多个jvm之间的会话是不能共享的)
3、分布式全局id(比如商品订单id的生成,如果同一时刻生成订单id,可能会重复哟,一般解决是将这些id提前生成好存放在redis 中,然后需要的时候再去取还有一种是通过分布式锁,同一时刻仅能一个生成订单id)
1.3.1 nginx.conf文件介绍
nginx.conf复制nginx的配置与,由简单指令和快指令组成
简单指令格式:[name 参数;]
块指令:和简单指令一样,但其结束符不是";"而是大括号{},内部可包含简单指令和块指令,也可称为上下文比如(events、http、server、location)
conf文件中,所有不属于块指令的简单指令都属于main上下文的,http块指令属于main上下文,server块指令http上下文。
1.3.2 实现反向代理
1、首先需要在hosts文件里面创建一个本地域名地址映射,比如127.0.0.1 www.losemyfuture.com
2、准备一台tomcat服务器,比如我的提供http://127.0.0.1:8080/spboot-0.0.1-SNAPSHOT/indexDev这样一个8080端口的
3、修改nginx.conf配置文件
server {
listen 80; #监听哪个端口,其实就是nginx的访问端口
server_name www.losemyfuture.com; #监听哪个域名
#charset koi8-r;
#access_log logs/host.access.log main;
location / { #拦截请求地址,/代表拦截所有请求
#root html; #路径和真实服务器的路径一致,如果配置为root /www/root/html/,访问nginx /t/a.html,则真实的为www/root/html/a.html,而如果为alias /www/root/html/new_t/ 同样的请求则为/www/root/html/new_t/a.html
proxy_pass http://127.0.0.1:8080/spboot-0.0.1-SNAPSHOT/; #真实服务器的ip端口号
index indexDev index.html index.htm; #默认展示主页, 直接访问www.losemyfuture.com,等同于www.losemyfuture.com/index.html
}
4、访问http://www.losemyfuture.com/indexDev查看是否配置成功。
一般来说,使用反向代理只是将真实服务器ip地址隐藏了,而负载均衡才是实现集群的关键
1.3.3 负载均衡策略实现
1、准备两台可访问的服务器,比如我的http://127.0.0.1:8081/index和http://127.0.0.1:8080/index,返回的是
“我是第一台服务器,我的端口号是8080”“我是第二台服务器,我的端口号是8081”
这样的字段。。。。
2、配置nginx负载均衡(轮询)。
upstream backserver {#负载均衡配置,默认轮询
server 127.0.0.1:8080;
server 127.0.0.1:8081;
}
server {
listen 80; #监听哪个端口,其实就是nginx的访问端口
server_name www.losemyfuture.com; #监听哪个域名
#charset koi8-r;
#access_log logs/host.access.log main;
location / { #拦截请求地址,/代表拦截所有请求
#root html; #路径和真实服务器的路径一致,如果配置为root /www/root/html/,访问nginx /t/a.html,则真实的为www/root/html/a.html,而如果为alias /www/root/html/new_t/ 同样的请求则为/www/root/html/new_t/a.html
proxy_pass http://backserver;
index indexDev index.html index.htm; #默认展示主页, 直接访问www.losemyfuture.com,等同于www.losemyfuture.com/index.html
}
访问http://www.losemyfuture.com/index,多次刷新可以看到在8080和8081之间来回切换。
3、配置nginx负载均衡(权重),weight和访问比率成正比,用于后端服务器性能不均的情况
server 127.0.0.1:8080 weight=2;
server 127.0.0.1:8081 weight=1;
3、ip绑定,其实就是根据请求地址ip的hash值选择一个合适的服务器。这样可以解决会话共享问题,因为同一个用户的请求固定到一台服务器。
ip_hash;
server 127.0.0.1:8080;
server 127.0.0.1:8081;
4、fair(第三方),按后端服务器的响应时间来分配请求,响应时间短的优先分配。
upstream backserver {
server server1;
server server2;
fair;
}
5、url_hash(第三方)按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。
upstream backserver {
server squid1:3128;
server squid2:3128;
hash $request_uri;
hash_method crc32;
}
1.4 tomcat服务宕机容错机制
一般采用一主一备、多主多备来解决这个高可用(宕机)问题。
如果tomcat挂掉怎么办?
1、使用keepalived,这个其实就是一个启动脚本,当扫描比如端口号,没有这台服务,他就会自动重启这个服务,除非重启多次还是不成功,那么他才会发送告警给运维人员。
2、对于上图中的tomcat宕机的话,如果其中某一台tomcat挂掉,可以在nginx里面配置比如连接超时、请求超时等,超过时间没有返回响应结果,他会自动轮询到下一台服务器。
upstream backserver {#负载均衡配置,默认轮询
server 127.0.0.1:8080 weight=4;
server 127.0.0.1:8081 weight=2;
}
server {
listen 80; #监听哪个端口,其实就是nginx的访问端口
server_name www.losemyfuture.com; #监听哪个域名
#charset koi8-r;
#access_log logs/host.access.log main;
location / { #拦截请求地址,/代表拦截所有请求
#root html; #路径和真实服务器的路径一致,如果配置为root /www/root/html/,访问nginx /t/a.html,则真实的为www/root/html/a.html,而如果为alias /www/root/html/new_t/ 同样的请求则为/www/root/html/new_t/a.html
proxy_pass http://backserver;
index indexDev index.html index.htm; #默认展示主页, 直接访问www.losemyfuture.com,等同于www.losemyfuture.com/index.html
proxy_connect_timeout 1;
proxy_send_timeout 1;
proxy_read_timeout 1;
}
这样当连接、发送、读取超过一秒时,他就会自动轮询到下一台tomcat服务器。一般用于服务发布版本(发布时肯定不能访问嘛对吧?)
重启nginx后再访问http://www.losemyfuture.com/index然后停掉8080的服务器,他会在超时后自动调转到8081这台服务器
如果nginx挂了呢?一般在生产环境中不会只有一台nginx服务器,而是会有一主一备、多主多备这样子。某一台挂了另一台直接顶上,全部挂了可以通过keepalived自动重启,再不行才到运维人员。至于怎么实现请往后看。。。。
发布版本时,在发布的tomcat不能访问,所以他会超时然后自动轮询,那么此时会话不就失效了吗?因为两台不同的服务器代表两台不同jvm两个不共享的会话,所以一般这种情况就将会话放到redis中。
1.5 使用nginx搭建企业API接口网关
这个主要是解决跨域访问问题,就是不能使用ajax来从网站访问b网站的资源。一般这种有5五种解决方案
1、jsonp,支持get,不支持Post,而且需要后端代码配合
2、后端使用httpclient来转发到相应的资源
3、使用http响应头允许访问
4、nginx搭建企业api接口网关
5、Spring cloudZULL接口网关
这里以第四种为例,项目搭建请参考这篇博客
访问http://www.a.com:8080/a/index能正常访问,以及http://www.b.com:8080/b/弹出警告“fail”代表搭建成功,也表示b网站不能通过ajax访问a网站的资源。
配置nginx.conf:
server {
listen 80; #监听哪个端口,其实就是nginx的访问端口
server_name kauyu.losemyfuture.com; #监听哪个域名
location /a {
proxy_pass http://www.a.com:8080/a/;
index index.html index.htm;
}
location /b {
proxy_pass http://www.b.com:8080/b/;
index index.html index.htm;
}
}
b网站的jsp文件
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>B网站访问</title>
</head>
<script type="text/javascript"
src="http://www.itmayiedu.com/static/common/jquery-1.7.2.min.js?t=2017-07-27"></script>
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
type : "GET",
async : false,
url : "http://kuayu.losemyfuture.com/a/FromServlet?userName=644064",
dataType : "json",
success: function(data) {
alert(data["userName"]);
},
error : function() {
alert('fail');
}
});
});
</script>
<body>
<img alt="" src="http://www.a.com/a/img/timg.jpg">
</body>
</html>
访问http://kuayu.losemyfuture.com/b/index,哟呵成功啦!快去试试吧。
原理:通过域名相同,项目不同,通过拦截跳转到真实的服务器中(不同的项目代表不同的服务器,但是域名一样的啊,所以看起来就没有什么跨域问题了),而直接通过b网站访问的话,来源是不同的。
1.6 nginx防盗链
我的这篇博客中也说到了防盗链的一些解决办法,所以这里的这个是另一种方法,这种的话比较简单,不用修改后端代码,好维护吧。
可以在nginx.conf中这样配置。
location ~*\.(gif|jpg|png|jpeg)$ {
expires 30d;
valid_referers www.a.com; #白名单,就是哪些源是可以直接访问的
if ($invalid_referer) {
rewrite ^/ http://www.a.com:8080/a/img/error.png; #当不是白名单的那些来源访问的就是这个了
}
}
根据这篇博客中的代码,http://www.a.com:8080/a/index这样可以得到图片,点击图片也可以下载,而且右键图片新标签中打开时,可以打开。
2 nginx高可用
2.1 Linux安装nginx
1、下载nginx以及所需的安装包
2、解压到/usr/local/
3、安装PCRE库
$ tar -zxvf pcre-8.36.tar.gz
$ cd pcre-8.36
$ ./configure
$ make
$ make install
./configure报错
configure: error: You need a C++ compiler for C++ support.
解决办法
yum install -y gcc gcc-c++
3、安装zlib库
$ tar -zxvf zlib-1.2.8.tar.gz
$ cd zlib-1.2.8
$ ./configure
$ make
$ make install
3、安装ssl
$ tar -zxvf openssl-1.0.1j.tar.gz
$ ./config
$ make
$ make install
4、安装nginx
$ tar -zxvf nginx-1.8.0.tar.gz
$ cd nginx-1.8.0
$ ./configure --prefix=/usr/local/nginx
$ make
$ make install
Nginx启动提示找不到libpcre.so.1解决方法
如果是32位系统
[aaa@qq.com ~]# ln -s /usr/local/lib/libpcre.so.1 /lib
如果是64位系统
[aaa@qq.com ~]# ln -s /usr/local/lib/libpcre.so.1 /lib64
然后在启动nginx就OK了
[aaa@qq.com ~]# /usr/local/nginx/sbin/nginx
访问http://192.168.245.134/是否出现欢迎界面!!!
重启:
$ /usr/local/nginx/sbin/nginx –s reload
停止:
$ /usr/local/nginx/sbin/nginx –s stop
测试配置文件是否正常:
$ /usr/local/nginx/sbin/nginx –t
强制关闭:
$ pkill nginx
2.2 nginx+keepalived高可用介绍
为什么上图这样做?如果按照上文的那种只有一台nginx服务器,虽然可以解决tomcat服务器宕机或者发布版本时不能访问的问题,但是如果这台nginx服务器挂掉了呢,由于tomcat服务器集群是不能通过外网访问的,所以整个服务就会瘫痪了。因此,需要搭建nginx服务集群一主一备或者多主多备,可是这样的话,当有客户端请求到达时,选择哪一台nginx进行转发这个请求到真实服务器呢?这又是个问题,此时keepalived就起作用了,他通过在其内部虚拟一个ip,这个ip指向这些nginx集群,当他检测到主nginx挂了,此时客户端请求时,他会将该请求指向备slave nginx进行转发,而对于master主nginx他会采取重启策略,如果重启成功,下次客户端的请求到达时,依然选择住master nginx进行转发,但是如果多次后依然不能成功,那么不好意思,他会发送邮件|告警给运维人员“兄嘚,你有事做了。。。”。
这样就实现了nginx的高可用(宕机容错机制、宕机重启)。
准备两台nginx服务器,通过克隆上述的nginx服务环境,选择其中一台为主服务器,另一台为备服务器
如果外部不能访问 可这样解决/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT或者关闭防火墙。
//临时关闭 systemctl stop firewalld
//禁止开机启动 systemctl disable firewalld
利用keepalived+nginx实现高可用,这里注意下,每台服务器都要安装keepalived,因为重启时他只会重启自己机器的nginx服务,其实这个东西相当于是另一个集群(类似redis,只不过这个如果master宕机重启后依然是master,这点和redis很不同)
1、开始之前需要安装yum install -y openssl openssl-devel
2、解压2.1中的keepalived安装包,tar -zxvf keepalived-1.2.18.tar.gz -C /usr/local/
3、将keepalived安装成Linux系统服务,因为没有使用keepalived的默认安装路径(默认路径:/usr/local),安装完成之后,需要做一些修改工作:
mkdir /etc/keepalived 存放keepalived的配置文件
cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/
复制keepalived的脚本文件
cp /usr/local/keepalived/etc/rc.d/init.d/keepalived /etc/init.d/
cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
ln -s /usr/local/sbin/keepalived /usr/sbin/
ln -s /usr/local/keepalived/sbin/keepalived /sbin/
chkconfig keepalived on 设置开机启动,一般生产环境这样使用
4、准备nginx重启脚本nginx_check.sh,放在任意位置,只要后面指定就好
#!/bin/bash
A=`ps -C nginx --no-header |wc -l`
if [ $A -eq 0 ];then
/usr/local/nginx/sbin/nginx
sleep 2
if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then
killall keepalived
fi
fi
5、主配置keepalived.conf
! Configuration File for keepalived
vrrp_script chk_nginx {
script "/etc/keepalived/nginx_check.sh" #运行脚本,脚本内容下面有,就是起到一个nginx宕机以后,自动开启服务
interval 2 #检测时间间隔
weight -20 #如果条件成立的话,则权重 -20
}
# 定义虚拟路由,VI_1 为虚拟路由的标示符,自己定义名称
vrrp_instance VI_1 {
state MASTER #来决定主从 BACKUP表示从
interface ens33 # 绑定虚拟 IP 的网络接口,根据自己的机器填写 ip a可以查看不要使用lo的这个本地ip
virtual_router_id 121 # 虚拟路由的 ID 号, 两个节点设置必须一样
mcast_src_ip 192.168.245.134 #填写本机ip
priority 100 # 节点优先级,主节点要比从节点优先级高
nopreempt # 优先级高的设置 nopreempt 解决异常恢复后再次抢占的问题
advert_int 1 # 组播信息发送间隔,两个节点设置必须一样,默认 1s
authentication {
auth_type PASS
auth_pass 1111
}
# 将 track_script 块加入 instance 配置块
track_script {
chk_nginx #执行 Nginx 监控的服务
}
virtual_ipaddress {
192.168.245.150 # 虚拟ip,也就是解决写死程序的ip怎么能切换的ip,也可扩展,用途广泛。可配置多个(多主多备),这个就是提供给外部访问的
}
}
6、从配置,修改ip、State、priority、interface其他和上面一样。
7、启动keepalived systemctl start keepalived.service访问http://192.168.245.150/,跳转到主节点,停掉主节点的keepalived,再次访问跳转到从节点,如果停掉主节点nginx,会马上重启。。
2.3 session会话共享问题
一般来说,会话共享可以通过以下几种方式解决:
1、将会话存放在cookie里面,客户端第一次访问时,首先存一份到A服务器同时也将该session存到cookie发送给客户端,当客户端请求B服务器时,如果B有直接用,如果没有那就同步客户端携带cookie中的会话信息,可是这样不安全。
2、将会话存放到数据库中,增加了数据库的压力
3、利用nginx做负载均衡时,采用ip_hash策略,这样每台客户端都定向到一台服务器,缺点是相当于没有做集群了吗???
4、利用redis存储会话信息,这种事最优秀的,下面也以这种为例
在这个这里搭好的Redis集群,和spring boot集成redis的基础上
添加依赖:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
controller
@Value("${server.port}")
private String port;
@RequestMapping("/setSession")
public String setSession(HttpServletRequest request,
@PathParam("key") String key,
@PathParam("value") String value){
HttpSession session = request.getSession(true);
session.setAttribute(key,value);
return request.getLocalAddr()+":"+port+",session:"+key+"="+session.getAttribute(key);
}
@RequestMapping("/getSession")
public String getSession(HttpServletRequest request,
@PathParam("key") String key){
HttpSession session = request.getSession(false);
return request.getLocalAddr()+":"+port+",session:"+key+"="+session.getAttribute(key);
}
会话初始化,利用Redis配置信息
public class SessionInitializer extends AbstractHttpSessionApplicationInitializer {
public SessionInitializer(){
super(RedisTemplate.class);
}
}
########################################################
spring:
redis:
password: 123456
jedis:
pool:
max-idle: 8
min-idle: 0
max-active: 8
max-wait: -1ms
timeout: 5000ms
cluster:
nodes: 192.168.245.134:6379,192.168.245.135:6379,192.168.245.136:6379
max-redirects: 2
ssl: false
sentinel:
master: mymaster
nodes: 192.168.245.136:26379
database: 0
此时将项目打包两个不同的端口或者两台不同的服务器,然后利用nginx实现负载均衡以及反向代理后,尽情的玩耍吧!!!!
3 高并发解决方案
3.1 数据库
1、慢查询定位SQL语句
2、SQL语句优化
3、避免全表扫描
4、分表分库
5、主从复制(集群)(原理二进制日志文件)
6、读写分离
7、索引优化
3.2 缓存机制
1、jvm内置缓存
2、redis缓存数据库内容(数据库IO、redis内存速度快)
3、redis集群(主从复制)、读写分离
4、哨兵机制实现高可用
3.3 服务器
1、反向代理
2、集群(负载均衡【轮询、权重、ip绑定、fair第三方响应时间、URLhash】)
3、CDN加速
3.4 客户端
1、减少请求次数
2、用户体验好,客户已使用ajax
3、动静分离
3.5 项目优化
1、代码重构
2、jvm调优 垃圾回收机制
3、分布式+微服务