从零学习Nginx

918 阅读9分钟

1、简介

Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。 其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。2011年6月1日,nginx 1.0.4发布。 Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。

2、常见问题分析

2.1、请解释一下什么是Nginx?

Nginx是一个web服务器和方向代理服务器,用于HTTP、HTTPS、SMTP、POP3和IMAP协议。

2.2、请列举Nginx的一些特性

  • 跨平台:可以在大多数Unix like 系统编译运行。而且也有Windows的移植版本。

  • 配置异常简单:非常的简单,易上手。

  • 非阻塞、高并发连接:数据复制时,磁盘I/O的第一阶段是非阻塞的。官方测试能支持5万并发连接,实际生产中能跑2~3万并发连接数(得益于Nginx采用了最新的epoll事件处理模型(消息队列)。

  • Nginx代理和后端Web服务器间无需长连接;

  • Nginx接收用户请求是异步的,即先将用户请求全部接收下来,再一次性发送到后端Web服务器,极大减轻后端Web服务器的压力。

  • 发送响应报文时,是边接收来自后端Web服务器的数据,边发送给客户端。

  • 网络依赖性低,理论上只要能够ping通就可以实施负载均衡,而且可以有效区分内网、外网流量。

  • 支持内置服务器检测。Nginx能够根据应用服务器处理页面返回的状态码、超时信息等检测服务器是否出现故障,并及时返回错误的请求重新提交到其它节点上。

  • 此外还有内存消耗小、成本低廉(比F5硬件负载均衡器廉价太多)、节省带宽、稳定性高等特点。

2.3、使用“反向代理服务器”的优点是什么?

反向代理服务器可以隐藏源服务器的存在和特征。它充当互联网云和web服务器之间的中间层。这对于安全方面来说是很好的,特别是当您使用web托管服务时。

  • 主程序 Master process 启动后,通过一个 for 循环来 接收 和 处理外部信号 ;

  • 主进程通过 fork() 函数产生 worker 子进程 ,每个子进程执行一个 for循环来实现Nginx服务器对事件的接收和处理 。

一般推荐 worker 进程数与CPU内核数一致,这样一来不存在大量的子进程生成和管理任务,避免了进程之间竞争CPU 资源和进程切换的开销。

在 Nginx 服务器的运行过程中, 主进程和工作进程 需要进程交互。交互依赖于 Socket 实现的管道来实现。

2.4、请解释代理设计中的正向代理和反向代理?

  • 正向代理的过程,它隐藏了真实的请求客户端,服务端不知道真实的客户端是谁,客户端请求的服务都被代理服务器代替来请求
  • 反向代理服务器会帮我们把请求转发到真实的服务器那里去。Nginx就是性能非常好的反向代理服务器,用来做负载均衡。
  • 两者的区别在于代理的对象不一样:正向代理代理的对象是客户端,反向代理代理的对象是服务端

2.5、在Nginx中,解释如何在URL中保留双斜线?

要在URL中保留双斜线,就必须使用merge_slashes_off;

语法:merge_slashes [on/off]

默认值: merge_slashes on

环境: http,server

2.6、nginx服务器常见错误代码500、501、502、503、504、505

  • 500:内部服务错误
  • 501:服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码。
  • 502:Bad Gateway错误
  • 503:服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。(服务不可用)
  • 504:Gateway timeout 网关超时
  • 505:服务器不支持请求中所用的 HTTP 协议版本。(HTTP 版本不受支持)

2.7、nginx的常用算法实现?

1、round-robin

round-robin的意思是循环轮询。Nginx最简单的负载均衡配置如下:

upstream app1 {
    server 10.10.10.1;
    server 10.10.10.2;
}

server {
    listen 80;
    server_name  xxxxx;
    location / {
        proxy_pass http://app1;

    }
}

2、least-connected

least-connected算法的中文翻译是最少连接,即每次都找连接数最少的服务器来转发请求。例如Nginx负载中有两台服务器,A和B,当Nginx接收到一个请求时,A正在处理的请求数是10,B正在处理的请求数是20,则Nginx会把当前请求交给A来处理。要启用最少连接负载算法只需要在定义服务器组时加上“least_conn”,如:

upstream app1 {
    least_conn;

    server 10.10.10.1;

    server 10.10.10.2;
}

3、ip-hash

ip-hash算法会根据请求的客户端IP地址来决定当前请求应该交给谁。使用ip-hash算法时Nginx会确保来自同一客户端的请求都分发到同一服务器。要使用ip-hash算法时只需要在定义服务器组时加上“ip-hash ”指令,如:

upstream app1 {
    ip_hash;

    server 10.10.10.1;

    server 10.10.10.2;
}

4、weighted

weighted算法也就是权重算法,会根据每个服务的权重来分发请求,权重大的请求相对会多分发一点,权重小的会少分发一点。这通常应用于多个服务器的性能不一致时。需要使用权重算法时只需要在定义服务器组时在服务器后面指定参数weight,如:

upstream app1 {
    server 10.10.10.1 weight=3;

    server 10.10.10.2;
}

2.8、Nginx是如何处理一个请求的呢?

首先,nginx在启动时,会解析配置文件,得到需要监听的端口与ip地址,然后在nginx的master进程里面 先初始化好这个监控的socket,再进行listen 然后再fork出多个子进程出来, 子进程会竞争accept新的连接。 此时,客户端就可以向nginx发起连接了。当客户端与nginx进行三次握手,与nginx建立好一个连接后,此时,某一个子进程会accept成功,然后创建nginx对连接的封装,即ngx_connection_t结构体接着,根据事件调用相应的事件处理模块,如http模块与客户端进行数据的交换,最后,nginx或客户端来主动关掉连接,到此,一个连接就寿终正寝了

2.9、动态资源、静态资源分离?

动态资源、静态资源分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路。 动、静分离将网站静态资源(HTML,JavaScript,CSS,img等文件)与后台应用分开部署,提高用户访问静态代码的速度,降低对后台应用访问 这里我们将静态资源放到nginx中,动态资源转发到tomcat服务器中

示例:

server {
    listen 80;
    server_name 192.168.25.35;      #当接收到http请求时,首先host和这里的server_name进行匹配,如果匹配上,则走这个虚拟主机的location路由

    location /static/~(.*)(\.jpg|\.png|\.gif|\.jepg|\.css|\.js|\.css){  #  静态资源则路由到这里
        alias /nginx/static/; ;
    }

    location / {  #  其他的url则转发到 http://192.168.25.35:8080
        proxy_pass http://192.168.25.35:8080;

    }

}

2.10、如何配置Nginx防止SQL注入、XSS攻击?

SQL 注入、XSS 攻击会绕过 CDN 缓存规则直接回源请求,造成 PHP、MySQL 运算请求越来越多,服务器负载飙升。在日志里可以看到几乎大部分都是 GET/POST 形式的请求,虽然 waf 都完美的识别和拦截了,但是因为是 Nginx 层面应对措施,所以还是会对服务器负载形成一定的压力,最有效的办法就是在 Nginx 里加入了防止 SQL 注入、XSS 攻击的配置。具体做法如下:

url问号后面的请求参数,在nginx用$query_string表 示,不能在rewrite中匹配到,需要用if判断

将下面的 Nginx 配置文件代码放入到对应站点的.conf 配置文件[server]里,然后重启 Nginx 即可生效。

if ($request_method !~* GET|POST) { return 444; }
#使用 444 错误代码可以更加减轻服务器负载压力。
#防止 SQL 注入
if ($query_string ~* (\$|'|--|[+|(%20)]union[+|(%20)]|[+|(%20)]insert[+|(%20)]|[+|(%20)]drop[+|(%20)]|[+|(%20)]truncate[+|(%20)]|[+|(%20)]update[+|(%20)]|[+|(%20)]from[+|(%20)]|[+|(%20)]grant[+|(%20)]|[+|(%20)]exec[+|(%20)]|[+|(%20)]where[+|(%20)]|[+|(%20)]select[+|(%20)]|[+|(%20)]and[+|(%20)]|[+|(%20)]or[+|(%20)]|[+|(%20)]count[+|(%20)]|[+|(%20)]exec[+|(%20)]|[+|(%20)]chr[+|(%20)]|[+|(%20)]mid[+|(%20)]|[+|(%20)]like[+|(%20)]|[+|(%20)]iframe[+|(%20)]|[\<|%3c]script[\>|%3e]|javascript|alert|webscan|dbappsecurity|style|confirm\(|innerhtml|innertext)(.*)$) { return 555; }
if ($uri ~* (/~).*) { return 501; }
if ($uri ~* (\\x.)) { return 501; }
#防止 SQL 注入
if ($query_string ~* "[;'<>].*") { return 509; }
if ($request_uri ~ " ") { return 509; }
if ($request_uri ~ (\/\.+)) { return 509; }
if ($request_uri ~ (\.+\/)) { return 509; }
 
#if ($uri ~* (insert|select|delete|update|count|master|truncate|declare|exec|\*|\')(.*)$ ) { return 503; }
#防止 SQL 注入
if ($request_uri ~* "(cost\()|(concat\()") { return 504; }
if ($request_uri ~* "[+|(%20)]union[+|(%20)]") { return 504; }
if ($request_uri ~* "[+|(%20)]and[+|(%20)]") { return 504; }
if ($request_uri ~* "[+|(%20)]select[+|(%20)]") { return 504; }
if ($request_uri ~* "[+|(%20)]or[+|(%20)]") { return 504; }
if ($request_uri ~* "[+|(%20)]delete[+|(%20)]") { return 504; }
if ($request_uri ~* "[+|(%20)]update[+|(%20)]") { return 504; }
if ($request_uri ~* "[+|(%20)]insert[+|(%20)]") { return 504; }
if ($query_string ~ "(<|%3C).*script.*(>|%3E)") { return 505; }
if ($query_string ~ "GLOBALS(=|\[|\%[0-9A-Z]{0,2})") { return 505; }
if ($query_string ~ "_REQUEST(=|\[|\%[0-9A-Z]{0,2})") { return 505; }
if ($query_string ~ "proc/self/environ") { return 505; }
if ($query_string ~ "mosConfig_[a-zA-Z_]{1,21}(=|\%3D)") { return 505; }
if ($query_string ~ "base64_(en|de)code\(.*\)") { return 505; }
if ($query_string ~ "[a-zA-Z0-9_]=http://") { return 506; }
if ($query_string ~ "[a-zA-Z0-9_]=(\.\.//?)+") { return 506; }
if ($query_string ~ "[a-zA-Z0-9_]=/([a-z0-9_.]//?)+") { return 506; }
if ($query_string ~ "b(ultram|unicauca|valium|viagra|vicodin|xanax|ypxaieo)b") { return 507; }
if ($query_string ~ "b(erections|hoodia|huronriveracres|impotence|levitra|libido)b") {return 507; }
if ($query_string ~ "b(ambien|bluespill|cialis|cocaine|ejaculation|erectile)b") { return 507; }
if ($query_string ~ "b(lipitor|phentermin|pro[sz]ac|sandyauer|tramadol|troyhamby)b") { return 507; }
#这里大家根据自己情况添加删减上述判断参数,cURL、wget 这类的屏蔽有点儿极端了,但要“宁可错杀一千,不可放过一个”。
if ($http_user_agent ~* YisouSpider|ApacheBench|WebBench|Jmeter|JoeDog|Havij|GetRight|TurnitinBot|GrabNet|masscan|mail2000|github|wget|curl|Java|python) { return 508; }
#同上,大家根据自己站点实际情况来添加删减下面的屏蔽拦截参数。
if ($http_user_agent ~* "Go-Ahead-Got-It") { return 508; }
if ($http_user_agent ~* "GetWeb!") { return 508; }
if ($http_user_agent ~* "Go!Zilla") { return 508; }
if ($http_user_agent ~* "Download Demon") { return 508; }
if ($http_user_agent ~* "Indy Library") { return 508; }
if ($http_user_agent ~* "libwww-perl") { return 508; }
if ($http_user_agent ~* "Nmap Scripting Engine") { return 508; }
if ($http_user_agent ~* "~17ce.com") { return 508; }
if ($http_user_agent ~* "WebBench*") { return 508; }
if ($http_user_agent ~* "spider") { return 508; } #这个会影响国内某些搜索引擎爬虫,比如:搜狗
#拦截各恶意请求的 UA,可以通过分析站点日志文件或者 waf 日志作为参考配置。
if ($http_referer ~* 17ce.com) { return 509; }
#拦截 17ce.com 站点测速节点的请求,所以明月一直都说这些测速网站的数据仅供参考不能当真的。
if ($http_referer ~* WebBench*") { return 509; }
#拦截 WebBench 或者类似压力测试工具,其他工具只需要更换名称即可。

把以上代码保存为block.conf,放在nginx的conf根目录,在需要防护的网站上用include引入就可以了。

include block.conf;