入门篇
基本功能
Nginx是一款web服务器,进而是一款所有请求的入口服务器。
根据这些功能,可以实现的使用场景
Web服务器,实现动静分离
前端页面与后端代码分离,缓存前端的html,js,css等资源,提高响应速度。
反向代理
客户端点击页面请求,不需要知道请求具体到了那一台服务器。
负载均衡
请求均匀分配到服务器上,不至于某一台负载过大,或过小。
负载均衡算法
1.轮询
2.加权轮询
3.最少连接数:把请求分配给连接数最小的服务器
4.Ip 哈希:同一个ip只能请求到具体的某台服务器
带来的问题
基本命令
参考:https://github.com/dunwu/nginx-tutorial
常用命令
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 的版本,编译器版本和配置参数。
常用配置
反向代理
# 运行用户
user nginx;
# 启动进程数
worker_processes 1;
# 全局错误日志
error_log /var/log/nginx/error.log warn;
# 当前进程的ID
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
# http服务器,进行静态资源、反向代理、负载均衡
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
#sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件
sendfile on;
#tcp_nopush on;
# 页面连接超时时间
keepalive_timeout 60;
#gzip on;
# http服务器
server {
# 监听端口
listen 80;
# 服务名,通过http://localhost:80访问改服务器
server_name localhost;
location / {
proxy_pass http://192.168.207.1:8080
}
}
}
负载均衡
# http服务器
server {
# 监听端口
listen 80;
# 服务名,通过http://localhost:80访问改服务器
server_name localhost;
location / {
proxy_pass http://myserver
}
}
# 负载均衡服务器列表
upstream myserver {
# 权重轮询,权重越大,分配几率越高
server 192.168.207.1:8080 weight=5;
server 192.168.207.1:8081 weight=2;
server 192.168.207.1:8082 weight=7;
}
# 负载均衡服务器列表
upstream myserver {
# 最少连接
least_conn;
server 192.168.207.1:8080;
server 192.168.207.1:8081;
server 192.168.207.1:8082;
}
# 负载均衡服务器列表
upstream myserver {
# 最少连接
ip_hash
server 192.168.207.1:8080;
server 192.168.207.1:8081;
server 192.168.207.1:8082;
}
高级篇
解决跨域问题
什么是跨域问题?
浏览器的一种安全策略:同源策略,认为协议+IP+端口都相同就是同源,安全的,否则就是恶意攻击。
同源策略
- DOM 层面的同源策略:限制了来自不同源的”Document”对象或 JS 脚本,对当前“document”对象的读取或设置某些属性
- XMLHttprequest层面的同源策略:禁止 Ajax 直接发起跨域HTTP请求(其实可以发送请求,结果被浏览器拦截,不展示),同时 Ajax 请求不能携带与本网站不同源的 Cookie。
案例:
比如,在http://192.168.207.129:8080请求返回页面中,添加一个按钮去Ajax 请求http://192.168.207.129:9090,就是跨域问题。
为什么浏览器要有同源策略,限制跨域?
安全问题,解决CSRF攻击。
CSRF攻击者,在用户已经登录目标网站之后,诱使用户访问一个具有攻击性的页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。
CSRF 攻击可以简单理解为:攻击者盗用了你的身份,以你的名义发送而已请求。
有些时候确实存在跨域请求,怎么办?
前端
JSONP
利用
JSONP 的缺点是:它只支持 GET 请求,而不支持 POST 请求等其他类型的 HTTP 请求
CORS 跨源资源共享
新增的一组HTTP header,允许服务端其声明哪些源站有权限访问哪些资源。
Access-Control-Allow-Origin:表示服务器允许哪些域可以访问该资源
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Max-Age
Nginx 配置中添加
add_header Access-Control-Allow-Origin “http://192.168.207.129:9090”;
后端
参考:https://blog.csdn.net/weixin_42036952/article/details/88564647
1. @CrossOrigin("http://localhost:8080")
2. **CORS****全局配置-实现**WebMvcConfigurer
3. 通过实现Fiter接口在请求中添加一些Header
http与https的区别
什么是网络安全?
在通信过程中,要具备以下几点:机密性、完整性、不可否认、身份认证。
机密性
数据必须保密,只能被信任的人读取到,其他人不可见,或可见但不可解释。
完整性
即一致性,在数据传输过程中,没有被非法篡改,内容不能多也不能少,保持原状。
不可否认
不能否认已经发生的事情。通信过程中,暂时不能理解?
身份认证
证实对方身份,保证消息发送到可信之人,而不是非法之徒。
为什么HTTPS比HTTP更安全?
因为在网络传输协议TCP/IP与应用层协议HTTP之间加了一层SSL/TLS,综合了对称加密、非对称加密,身份认证等技术。
对称加密与非对称加密
实现机密性的手段就是加密,即通过加密秘钥将明文数据替换成加密数据,之后再由解密秘钥将加密数据替换为明文数据。
对称加密
加密和解密的秘钥是同一个,只要通信双方保证不会泄露,就是机密性的。目前常用的只有 AES 和 ChaCha20
非对称加密
对称加密最大的问题:如何把秘钥安全的传递给对方。这个秘钥如果被拦截了,那么加密就没有了意义。故而衍生了非对称加密,采用公钥和私钥,公钥加密的数据只能由私钥解释,私钥加密的数据只能由公钥解释。常见的加密算法有 RSA、DSA 等
完整性
消息摘要算法
通过对所有数据提取指纹信息以实现数据签名、数据完整性校验等功能,任何消息经过散列函数处理后,都会获得唯一的散列值,这一过程称为 “消息摘要”,其散列值称为 “数字指纹”,其算法自然就是 “消息摘要算法”了。换句话说,如果其数字指纹一致,就说明其消息是一致的。MD5和SHA
身份认证
到这里对于通信双方可以说已经足够安全了。但是还有问题,怎么保证通信的对方是你期望的人呢?
这里其实使用非对称机密就可以了,客户端采用公钥加密的数据,发送给服务端采用私钥解密;服务端采用私钥加密的数据到客户端采用公钥解密。
Nginx https
https请求时序图
- 首先客户端通过URL访问服务器建立SSL连接。
- 服务端收到客户端请求后,会将网站支持的证书信息(证书中包含公钥)传送一份给客户端。
- 客户端的服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
- 客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
- 服务器利用自己的私钥解密出会话密钥。
- 服务器利用会话密钥加密与客户端之间的通信。
nginx实现
待更新。。。
分布式session
Nginx+Lua+Redis实现高性能缓存数据读取
参考:https://blog.csdn.net/hero272285642/article/details/100032699
Nginx中安转lua模块,实现调用lua脚本
docker 安装 openresty,底层nginx,集成了大多数模块。
docker run -d --name openresty -p 1080:80 \
-v /home/docker/nginx/open-conf:/usr/local/openresty/nginx/conf \
-v /home/docker/nginx/logs:/usr/local/openresty/nginx/logs \
-v /home/docker/nginx/html:/usr/local/openresty/nginx/html -d \ openresty/openresty:1.19.3.1-centos7
实现
参考:https://blog.csdn.net/qq_42236935/article/details/106905970
nginx.conf
server {
listen 80;
location /lua {
content_by_lua 'ngx.say("hello, world")';
}
location /luafile {
content_by_lua_file /usr/local/openresty/nginx/logs/lua/ngx.lua;
}
}
ngx.lua
- 脚本
ngx.say("hello lua")
- 连接redis
--设置响应头类型
ngx.header.content_type = "application/json;charset=utf8"
local uri_args = ngx.req.get_uri_args();
local key = uri_args["key"];
ngx.say(key);
-- 引入redis库
local redis = require("resty.redis");
-- 创建对象
local red = redis:new();
--设置超时时间
red:set_timeout(2000);
--连接
local ok, err = red:connect("172.17.0.4", 6379);
ngx.say(ok, err);
if not ok then
ngx.say("failed to connect: ", err)
return
end
local authok, err = red:auth("111111");
if not authok then
ngx.say("failed to auth: ", err)
return
end
-- 获得key值
local res, err = red:get(key)
if not res then
ngx.say("failed to get key: ", key, err)
return
end
ngx.say(res);
-- 关闭链接
red:close();
秒杀限流
限流算法
参考:https://www.codingsky.com/doc/2020/7/16/71.html
漏桶算法
原理:水滴(用户请求)优先注入到桶中(定长队列、先进先出队列),桶(队列)盛满后自动抛弃(限流)多余的水(请求),另外桶以匀速的方式漏出水滴(处理请求)。
令牌桶算法
原理:一个桶,用来存放固定数量的令牌。以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。
Nginx限流
limit_req_zone
作用:限制某个用户IP在一个时间段内能够产生的HTTP请求数。
参考:https://www.jianshu.com/p/2cf3d9609af3
http {
# 语法:limit_req_zone key zone rate,参考:https://www.jianshu.com/p/2cf3d9609af3
# key,根据什么来分类请求
# zone 一个nginx进程共享的内存空间,存储每个IP地址状态和它访问受限请求URL的频率
# rate 最大请求速率
limit_req_zone $binary_remote_addr zone=test:10m rate=10r/s;
server {
location / {
# 使用改限流策略,burst处理突发请求。
# 比如:策略中相当于每100毫秒处理一个请求,如果2个请求间隔小于100毫秒,就加入队列。
# 同时也是每100毫秒转发队列中的就转发一个队列中的请求
limit_req zone=test burst=20;
}
limit_conn_zone
作用:限制某个用户IP的最大连接数
http {
# 语法:limit_conn_zone key zone=name:size;
limit_conn_zone $binary_remote_addr zone=mycoon:1m;
server {
location / {
#是限制每个IP只能发起5个连接
limit_conn mycoon 5;
#限速为 100KB/秒
limit_rate 100k;
}
问题
1,根据以上理解,nginx只是对每个用户IP进行限流,但秒杀是大量用户IP的,这样还要有巨量的请求服务?
a. 可以设置key值,例如:limit_req_zone $server_name zone=sname:10m rate=1r/s; #限制服务器每秒只能有一次访问成功。参考:https://www.zhangshengrong.com/p/bYXxZBLlaZ/
b. OpenResty采用 lua-resty-limit-traffic模块中的resty.limit.count模块实现,由于文章篇幅具体代码参见源码openresty/lua/limit_count.lua。参考:https://cloud.tencent.com/developer/article/1181668
2,session存在nginx+redis上,那么设置限流后,那些被限制的请求,会去redis拉取session,还是直接被屏蔽掉?
keepalived实现nginx集群高可用
nacos集成,nginx动态刷新配置,实现网关高可用
待更新。。。
实战篇
如何设计一个百万级用户的抽奖系统?
场景:某个网站规定在某个时间段内,开展一个抽奖活动,活动奖品数量1000,价值2000万。可能出现有100万用户蹲点,同时发起请求,造成瞬间巨量请求。
设计:
- 未到开奖时间,所有用户刷新只能看到静态页面,不会请求到后台服务。可以采用nginx拦截这些无效请求。
- 开奖后,对于百万的其请求,其实可能前2万请求,就已经把奖品领取完毕了,后续的请求就是无效的。需要感知奖品数量,采用nginx拦截这些无效请求。
- 有些恶意用户为了自己获奖,会采用脚本方式高频发起请求。恶意请求,采用nginx拦截IP,只允许请求一次。
- 通过前三步,基本可以拦截掉98%的请求。假如剩下还有2万请求,是一定要走后台服务的。而后台服务机器,假如一台QPS是1000,那么大概需要20台机器,同时数据库机器,因为只有1000奖品,故而写只有1000,还可以进行MQ写削峰,因为不急。但是读会有很多,需要采用缓存。