03-05 nginx

入门篇

基本功能

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请求时序图

来源于网络
  1. 首先客户端通过URL访问服务器建立SSL连接。
  2. 服务端收到客户端请求后,会将网站支持的证书信息(证书中包含公钥)传送一份给客户端。
  3. 客户端的服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
  4. 客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
  5. 服务器利用自己的私钥解密出会话密钥。
  6. 服务器利用会话密钥加密与客户端之间的通信。

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万用户蹲点,同时发起请求,造成瞬间巨量请求。

设计:

  1. 未到开奖时间,所有用户刷新只能看到静态页面,不会请求到后台服务。可以采用nginx拦截这些无效请求。
  2. 开奖后,对于百万的其请求,其实可能前2万请求,就已经把奖品领取完毕了,后续的请求就是无效的。需要感知奖品数量,采用nginx拦截这些无效请求。
  3. 有些恶意用户为了自己获奖,会采用脚本方式高频发起请求。恶意请求,采用nginx拦截IP,只允许请求一次。
  4. 通过前三步,基本可以拦截掉98%的请求。假如剩下还有2万请求,是一定要走后台服务的。而后台服务机器,假如一台QPS是1000,那么大概需要20台机器,同时数据库机器,因为只有1000奖品,故而写只有1000,还可以进行MQ写削峰,因为不急。但是读会有很多,需要采用缓存。

架构图

来源于网络

你可能感兴趣的:(03-05 nginx)