@gaoxiaoyunwei2017
2019-08-06T17:23:00.000000Z
字数 5146
阅读 599
北哥
陶辉
- 杭州智链达数据有限公司CTO
- 《深入理解 Nginx:模块开发与架构解析》作者
本人出版过《深入理解Nginx:模块开发与架构解析》一书,在工作中并不是专职负责Nginx,在腾讯的时候为QQ空间负责小力度数据存储服务,在阿里云时候负责IS的基础网络存储服务,现在在杭州创立了基于传统行业的互联网转型公司。
从一直面向最基础的分布式数据,再到跳出技术回过头来看,会有对系统和架构有新的看法。我去年开了一门课程“Nginx核心知识150讲”,重新解读了我个人不同于书中的想法。
本文分享的是大规模分布式集群的高可用性和巧用Nginx。什么是巧用?很多人在用Nginx,同一个问题会有很多种解决方案,这些解决方案都能解决问题。但是,很多约束性的条件却有很大的不同。巧用就是必须要有系统化的思路去理解Nginx,才能选择比较合适的方案。
本文议题分四个部分:
对于面向互联网的环境,Nginx面对的特点会很明确。如:
架构是非常简单的REST架构,所谓的URL对应资源,每个资源都有一个表述,这个表述怎么转换于客户端和服务器之间。Nginx在这里面都会出现,图中会有各种代理,每个代理都有自己的缓存。
架构是有很多的需求,主要罗列几个主题要点:
Nginx有可插拔的模块化设计,基于统一的管道式的架构。在UPAF架构下有非常多的负载均衡策略,官方模块已经提供了最基础的基于连接的方案,远远不止这些模块,还有第三方的扩展。还有基于LUA语言的Openresty,国内使用Openresty的用户还是很多的,Openresty有自己的生态。Nginx在性能优化上做得非常极致,大家知道F5收购了Nginx公司,像这样专注于硬件的公司,Nginx可以跟它媲美。对HTTP协议的良好支持,在配置上面还支持脚本指令与变量。
我们面对的点是 Nginx 立体,大家可能会有很多的想法,把不同的用户分布在不同的集群,但这不是很系统化的思维。
我们更愿意从AKF扩展立方体与Nginx负载均衡,它有X、Y、Z轴,X轴只需要增加进程,不用改代码就能水平的扩展,本身的问题也很明显,解决不了数据不断增长的问题。
Y轴是功能切分,基于服务器和后台的功能切分,能解决数据增长的问题,但是,解决不了带来的复杂性问题,往往会导致重构迭代,成本非常高。Z轴的扩展基于用户的属性,这是我们经常用到的,要取得用户IP、URL或者其他协议进行扩展。这三种可以任意的组合以面对 Nginx 立体的问题。
要想解决上述问题,还必须要支持足够多的协议。下游的客户端大家是比较熟悉的,主要是UDP协议和TCP协议。Nginx 处理代理会有先天性的好处,受益于上面谈到的模块化设计。上游支持的协议有UDP、TCP、CGI等等。
图中是反向代理完整的流程,包括负载均衡策略抽象处理独立的模块。任何一个独立的开发者都可以贡献新的负载均衡策略,只是一个模块在这里替换而不影响整体的步骤。
Nginx要解决网络的性能问题,需要对Cache了解。Cache怎么进入到文件缓存,一开始命中了这个流程是怎么样的。之后会有很多种策略控制到底是哪边的网速更慢或者是更不稳定,就是包体,响应的包体和请求的包体。如果是上游网络好的话,会采用这种策略。
如果上游是合作的另外一个网络相对不好的话或者是下游网络比较好,就会采用这样的策略。框架本身没有可扩展性,但是设计的很好值得我们使用,可以很好的解决网络问题。
nginx配置文件最核心三个概念是我们要掌握的,也是实现复杂功能所必须的:
举个例子,处理一个请求,先设置了变量,判断是满足的,之后响应,再次判断。这个可能会觉得很无聊,但是,配置越复杂越可能碰到的。在多级指令下有很多的限制,会有上下文的配置。大家要很好的使用,不是说IF指令不能用,要理解这一套思维方式。
Nginx升级速度是非常快的,在前两年差不多是两周一个版本,现在是一个月以上一个版本。频繁的更新解决了一些问题推出了一些新功能。Nginx跟其他的服务不太一样,面向的并发式连接太多了,如果升级不能很好的处理就会出现问题。
nginx采用了多层的架构,就是master进程,为进程保留所有的网络队列不会丢掉。kbld是单进程的,只要换了新进程所有的旧进程没法保存,就会出现问题。在更新Nginx时,Master进程是在的,能同时处理新旧连接。要处理上游几千台服务器和下游上百万并发连接的情况下,做到不停机更新的同时,还对现有的网络不产生影响。
缓存一个是时间维度,一个是空间维度。时间维度就是蓝色用户访问的时候就缓存住了,导致第二次请求就命中。空间缓存要做预测,Nginx的策略相对比较简单,不是高度的面向定制化的服务。主要是基于时间的策略。这里所说的Nginx缓存是共享缓存,就是共有缓存。
图中是共享缓存和私有缓存。私有缓存只能为一个服务器服务。
解决缓存问题的时候,这里有很多的细节,大家可能不是很关注的。任何一个都是正确的处理,比如说Set-Cookie一定不能存储,存储了就有安全的问题。其他所有的规范会涉及很多的文档,Nginx是能满足常用的规则。
很多同学会经常问我的,为什么装了Nginx之后,根据官网的图,应该能看到Cache Loader和Cache Manger进程的。这两个进程是只针对缓存的,只有打开了才有。有同学还会问,我打开了还是看不到,特别是CL。为什么看不到呢?因为它为高性能做了非常多的工作。
举个例子,CL做什么呢?是处理磁盘上的文件,Nginx是把缓存写到磁盘上。但是,磁盘进行检索的话效果非常差。经常意外宕机之后,会从磁盘来恢复,CL就是做这件事的。CM是负责淘汰数据的。CL是辅助的进程,那怕缓存没有了也不会有大的影响,但是,CL进程会长期占用大量的资源,就会影响CM。
这是7033协议文档中的内容,解决了很多的问题。比如资源的问题,浏览器缓存了静态图片会怎么样?如果已经有缓存了,但是,我想确定这个缓存有没有过期,就是有条件的缓存。在协议中规定,处理业务之前先处理验证器。可以看到Nginx方方面面都体现在性能上。
Nginx有条件更新缓存是把它放到过滤模块当中,这个模块已经生成了响应,但是响应没有发给客户端,这个时候会基于条件请求。特别是我们做一些多人同时修改的效率共享协作文档时,就会经常走到上面的分支。
如果有一个热点资源,很多请求达到,可能缓存还没有拉到Nginx,就把上游服务器打挂了。这就需要合并回源请求,怎么合并回源请求呢?第一个请求过来了,我放行了,第二个请求就不放行了,包括四和五都不放行,到了第六步就返回,就能缓解上游服务的压力。
减少回源请求是一个方面,如果有一些过期的响应,能不能用?经常提到的互联网柔性、分级服务。预期我完全访问不了,不如返回过期的内容,前提是不对业务产生致命性的影响。proxy_cache_use_stale指令使用stale陈旧的缓存,第一个请求放行了,第二个请求就拒绝了,第三个拉最新的,最新的没有返回后就使用旧的,有新的就使用新的。这是Nginx很成熟的方案。
我们在网页上看视频,会拖动播放条,这是在Nginx协议当中基于字节一部分的内容。如果不启用Slice模块就会出现一些问题,比如现在拖动150-249字节,Nginx就会拉整个文件,你再拉300-500字节就很快了。但是这个步骤是很长的,怎么解决这个问题呢?Nginx有自己的策略,比如配置100字节,Nginx会段100-199、200-299,请求返回之后存入缓存再返回。这样效率就高很多,而不是一个很小的请求导致整个大文件下载下来。
Nginx对公网一定要解决安全性的问题,就是TLS。在TLS当中,Nginx可以很方便的实现很多种方案,比如与下游客户端之间SSL但是上游不是,验证下游或上游的证书,Nginx证书发给上下游,这些都可以灵活的实现。特别是在验证下游,提供了很多的变量,仅仅是通过配置就能帮助我们解决很多的问题。
Nginx处理TLS层高效率只要是有两个原因:
Nginx有很多模块,从功能角度来看,我个人把它分成四类,这四类各自有各自的设计原则。
每个模块只负责处理输入和输出,具有相同的格式。有以下特性:
Nginx过滤器管道架构的模块是有层次的,分成11个阶段,每个阶段是依次向下进行,可以跳跃,但阶段之间不可跳跃。中间灰色的是Nginx框架要求,不能修改。每个阶段的功能是非常单一的,相对来说扩展性就比较好。
这个是HTTP过滤器模块,在每一层当中是有序的,整体也是有序的。这里面有几个关键的模块,我们想使用就从第三个模块间插入,而解决的问题不一样。非关键模块可以去掉,顺序是反过来。
变量分为:提供变量模块和使用变量模块。
Nginx框架中的变量我们一定要理解好,它为我们提供了很多的可能性,觉得Nginx性能不够强,一定是没有理解到这些变量。变量分为5类:
Openresty的组成:
Lua代码怎么执行呢?Nginx框架有事件驱动系统、HTTP框架和STREAM框架组成。Nginx模块当中,ngx_http_lua_module和ngx_stream_lua_module是给Lua语言提供相应的接口,Lua语言通过它们来运行Nginx。在nginx配置文件中嵌入Lua代码指令来执行lua代码或者与Nginx交互的SDK。
SDK是非常强大的,上述的分类是基于日常使用的: