运用。
在互联网公司,Nginx可以说是标配组件,但是紧张场景还是负载均衡、反向代理、代理缓存、限流等场景;而把Nginx作为一个Web容器利用的还不是那么广泛。Nginx的高性能是大家公认的,而Nginx开拓紧张因此C/C++模块的形式进行,整体学习和开拓本钱偏高;如果有一种大略的措辞来实现Web运用的开拓,那么Nginx绝对是把好的瑞士军刀;目前Nginx团队也开始意识到这个问题,开拓了nginxScript:可以在Nginx中利用JavaScript进行动态配置一些变量和动态脚本实行;而目前市情上用的非常成熟的扩展是由章亦春将Lua和Nginx粘合的ngx_lua模块,并且将Nginx核心、LuaJIT、ngx_lua模块、许多有用的Lua库和常用的第三方Nginx模块组合在一起成为OpenResty,这样开拓职员就可以安装OpenResty,利用Lua编写脚本,然后支配到Nginx Web容器中运行。从而非常轻松就能开拓出高性能的Web做事。
接下来我们就认识下Nginx、Lua、ngx_lua模块和ngx_lua到底能开拓哪些类型的web运用。

Nginx优点
Nginx设计为一个主进程多个事情进程的事情模式,每个进程是单线程来处理多个连接,而且每个事情进程采取了非壅塞I/O来处理多个连接,从而减少了线程高下文切换,从而实现了公认的高性能、高并发;因此在天生环境中会通过把CPU绑定给Nginx事情进程从而提升其性能;其余由于单线程事情模式的特点,内存占用就非常少了。
Nginx变动配置重启速率非常快,可以毫秒级,而且支持一直止Nginx进行升级Nginx版本、动态重载Nginx配置。
Nginx模块也是非常多,功能也很强劲,不仅可以作为http负载均衡,Nginx发布1.9.0版本还支持TCP负载均衡,还可以很随意马虎的实现内容缓存、web做事器、反向代理、访问掌握等功能。
Lua的优点
Lua是一种轻量级、可嵌入式的脚本措辞,这样可以非常随意马虎的嵌入到其他措辞中利用。其余Lua供应了协程并发,即以同步调用的办法进行异步实行,从而实现并发,比起回调机制的并发来说代码更随意马虎编写和理解,排查问题也会随意马虎。Lua还供应了闭包机制,函数可以作为First Class Value 进行参数通报,其余实在现了标记打消垃圾网络。
由于Lua的小巧轻量级,可以在Nginx中嵌入Lua VM,要求的时候创建一个VM,要求结束的时候回收VM。
什么是ngx_lua
ngx_lua是Nginx的一个模块,将Lua嵌入到Nginx中,从而可以利用Lua来编写脚本,这样就可以利用Lua编写运用脚本,支配到Nginx中运行,即Nginx变成了一个Web容器;这样开拓职员就可以利用Lua措辞开拓高性能Web运用了。
ngx_lua供应了与Nginx交互的很多的API,对付开拓职员来说只须要学习这些API就可以进行功能开拓,而对付开拓web运用来说,如果打仗过Servlet的话,其开拓和Servlet类似,无外乎便是知道吸收要求、参数解析、功能处理、返回相应这几步的API是什么样子的。
开拓环境
我们可以利用OpenResty来搭建开拓环境,OpenResty将Nginx核心、LuaJIT、许多有用的Lua库和Nginx第三方模块打包在一起;这样开拓职员只须要安装OpenResty,不须要理解Nginx核心和写繁芜的C/C++模块就可以,只须要利用Lua措辞进行Web运用开拓了。
如何安装可以参考《跟我学Nginx+Lua开拓》。
OpenResty生态
OpenResty供应了一些常用的ngx_lua开拓模块:如
lua-resty-memcached
lua-resty-mysql
lua-resty-redis
lua-resty-dns
lua-resty-limit-traffic
lua-resty-template
这些模块涉及到如mysql数据库、redis、限流、模块渲染等常用功能组件;其余也有很多第三方的ngx_lua组件供我们利用,对付大部分运用处景来说现在生态环境中的组件已经足够多了;如果不知足需求也可以自己去写来完本钱身的需求。
场景
理论上可以利用ngx_lua开拓各种繁芜的web运用,不过Lua是一种脚本/动态措辞,不适宜业务逻辑比较重的场景,适宜小巧的运用处景,代码行数保持在几十行到几千行。目前见到的一些运用处景:
web运用:会进行一些业务逻辑处理,乃至进行耗CPU的模板渲染,一样平常流程:mysql/redis/http获取数据、业务处理、产生JSON/XML/模板渲染内容,比如京东的列表页/商品详情页;
接入网关:实现如数据校验前置、缓存前置、数据过滤、API要求聚合、AB测试、灰度发布、降级、监控等功能,比如京东的交易大Nginx节点、无线部门正在开拓的无线网关、单品页统一做事、实时价格、动态做事;
Web防火墙:可以进行IP/URL/UserAgent/Referer黑名单、限流等功能;
缓存做事器:可以对相应内容进行缓存,减少到后真个要求,从而提升性能;
其他:如静态资源做事器、推送做事、缩略图裁剪等。
二、基于Nginx+Lua的常用架构模式
1、负载均衡
如上图,我们首先通过LVS+HAProxy将流量转发给核心Nginx 1和核心Nginx 2,即实现了流量的负载均衡,此处可以利用如轮训、同等性哈希等调度算法来实现负载的转发;然后核心Nginx会根据要求特色如“Host:item.jd.com”,转发给相应的业务Nginx节点如单品页Nginx 1。此处为什么分两层呢?
1)核心Nginx层是无状态的,可以在这一层实现流量分组(内网和外网隔离、爬虫和非爬虫流量隔离)、内容缓存、要求头过滤、故障切换(机房故障切换到其他机房)、限流、防火墙等一些通用型功能;
2)业务Nginx如单品页Nginx,可以在在业务Nginx实现业务逻辑、或者反向代理到如Tomcat,在这一层可以实现内容压缩(放在这一层的目的是减少核心Nginx的CPU压力,将压力分散到各业务Nginx)、AB测试、降级;即这一层的Nginx跟业务有关联,实现业务的一些通用逻辑。
不管是核心Nginx还是业务Nginx,都该当是无状态设计,可以水平扩容。
业务Nginx一样平常会把要求直接转发给后真个业务运用,如Tomcat、PHP,即将要求内部转发到相应的业务运用;当有的Tomcat涌现问题了,可以在这一层摘掉;或者有的业务路径变了在这一层进行rewrite;或者有的后端Tomcat压力太大也可以在这一层降级,减少对后真个冲击;或者业务须要灰度发布时也可以在这一层Nginx上掌握。
2、单机闭环
所谓单机闭环即所有想要的数据都能从本做事器直接获取,在大多数时候无需通过网络去其他做事器获取。
如上所示,紧张有三种运用模式:
2.1、第一张图运用处景是Nginx运用谁也不依赖,比如我们的Cookie白名单运用,其目的是不在白名单中的Cookie将被清理,防止大家随便将Cookie写到jd.om根下;大家访问www.jd.com时,会看到一个http://ccc.jd.com/cookie_check的要求用来清理Cookie的;对付这种运用非常大略,不须要依赖数据源,直接单运用闭环即可。
2.2、第二张图,是读取本机文件系统,如静态资源合并:比如访问http://item.jd.com/1856584.html,查看源码会创造【<link type="text/css" rel="stylesheet" href="//misc.360buyimg.com/jdf/1.0.0/unit/??ui-base/1.0.0/ui-base.css,shortcut/2.0.0/shortcut.css,global-header/1.0.0/global-header.css,myjd/2.0.0/myjd.css,nav/2.0.0/nav.css,shoppingcart/2.0.0/shoppingcart.css,global-footer/1.0.0/global-footer.css,service/1.0.0/service.css"/>】这种要求,即多个要求合并为一个发给做事端,做事端进行了文件资源的合并;
目前有成熟的Nginx模块如nginx-http-concat进行静态资源合并;由于我们利用了OpenResty,那么我们完备可以利用Lua编写程序实现该功能,比如已经有人写了nginx-lua-static-merger来实现这个功能。
还一些业务型运用处景如下图所示
商品页面是由商品框架和其他维度的页面片段(面包屑、干系分类、商家书息、规格参数、商品详情)组成;或者首页是由首页框架和一些页面片段(分类、轮播图、楼层1、楼层N)组成;分维度是由于不同的维度是独立变革的。对付这种静态内容但是须要进行框架内容嵌入的办法,Nginx自带的SSI(Server Side Include)可以很轻松的完成;也可以利用Lua程序更灵巧的完成(读取框架、读取页面片段、合并输出)。
比如商品页面的架构我们可以这样:
首先吸收到商品变更,商品页面同步Worker会根据维度天生干系的页面推送到Nginx做事器;Nginx运用再通过SSI输出。目前京东商品详情页没有再采取这种架构,详细架构可以参考《构建需求相应式亿级商品详情页》。
对付首页的架构是类似的,由于其特点(框架变革少,楼层变革较频繁)和个性化的哀求,楼层一样平常实现为异步加载。
2.3、 第三张图和第二张图的不同处是不再直接读取文件系统,而是读取本机的Redis或者Redis集群或者如SSDB这种持久化存储或者其他存储系统都是可以的,比如直接说的商品页面可以利用SSDB进行存储实现。文件系统一个很大的问题是当多台做事器时须要Worker去写多台做事器,而这个过程可以利用SSDB的主从实现。
此处可以看到,不管是图二还是图三架构,都须要Worker去进行数据推送;假设本机数据丢了可怎么办?因此实际大部分运用不会是完备单机闭环的,而是会采取如下架构:
即首先读本机,如果没数据会回源到相应的Web运用从数据源拉取原始数据进行处理。这种架构的大部分场景本机都可以命中数据,只有很少一部分情形会回源到Web运用。
如京东的实时价格/动态做事便是采取类似架构。
3、分布式闭环
单机闭环会碰着如下两个紧张问题: 1、数据不一致问题(比如没有采取主从架构导致不同做事器数据不一致);2、碰着存储瓶颈(磁盘或者内存碰着了天花板)。
办理数据不一致的比较好的办法是采取主从或者分布式集中存储;而碰着存储瓶颈就须要进行按照业务键进行分片,将数据分散到多台做事器。
如采取如下架构,按照尾号将内容分布到多台做事器。
即第一步先读取分布式存储(JIMDB是京东的一个分布式缓存/存储系统,类似于Redis);如果不命中则回源到Tomcat集群(其会调用数据库、做事总线获取干系数据)来获取干系数据。可以参考《构建需求相应式亿级商品详情页》来获取更详细的架构实现。
JIMDB集群会进行多机房东从同步,各自机房读取自己机房的从JIMDB集群,如下图
4、接入网关
接入网关也可以叫做接入层,即吸收到流量的入口,在入口我们可以进行如下事情:
4.1、核心接入Nginx会做如下事情:
1、动态负载均衡;1、普通流量走同等性哈希,提升命中率;热点流量走轮训减少单做事器压力;2、根据要求特色将流量分配到不同分组并限流(爬虫、或者流量大的IP);3、动态流量(动态增加upstream或者减少upstream或者动态负载均衡)可以利用balancer_by_lua或者微博开源的upsync;
2、防DDOS攻击限流:可以将要求日志推送到实时打算集群,然后将须要限流的IP推送到核心Nginx进行限流;
3、造孽要求过滤:比如该当有Referer却没有,或者该当带着Cookie却没有Cookie;
4、要求聚合:比如要求的是http://c.3.cn/proxy?methods=a,b,c,核心接入Nginx会在做事端把Nginx并发的要求并把结果聚合然后一次性吐出;
5、要求头过滤:有些业务是不须要要求头的,因此可以在往业务Nginx转发时把这些数据过滤掉;
6、缓存做事:利用Nginx Proxy Cache实现内容页面的缓存;
4.2、业务Nginx会做如下事情:
1、缓存:对付读做事会利用大量的缓存来提升性能,我们在设计时紧张有如下缓存运用:首先读取Nginx本地缓存 Shared Dict或者Nginx Proxy Cache,如果有直接返回内容给用户;如果本地缓存不命中,则会读取分布式缓存如Redis,如果有直接返回;如果还是不命中则回源到Tomcat运用读取DB或调用做事获取数据。其余我们会按照维度进行数据的缓存。
2、业务逻辑:我们会进行一些数据校验/过滤逻辑前置(如商品ID必须是数字)、业务逻辑前置(获取原子数据,然后在Nginx上写业务逻辑)。
3、细粒度限流:按照接口特色和接口吞吐量来实现动态限流,比如后端做事快扛不住了,那我们就须要进行限流,被限流的要求作为降级要求处理;通过lua-resty-limit-traffic可以通过编程实现更灵巧的降级逻辑,如根据用户、根据URL等等各种规则,如降级了是让用户要求等待(比如sleep 100ms,这样用户要求就慢下来了,但是做事还是可用)还是返回降级内容。
4、降级:降级紧张有两种:主动降级和被动降级;如要求量太大扛不住了,那我们须要主动降级;如后端挂了或者被限流了或者后端超时了,那我们须要被动降级。降级方案可以是:1、返回默认数据如库存默认有货;2、返回静态页如预师长西席成的静态页;3、部分用户降级,见告部分用户等待下再操作;4、直接降级,做事没数据,比如商品页面的规格参数不展示;5、只降级回源做事,即可以读取缓存的数据返回,实现部分可用,但是不会回源处理;
5、AB测试/灰度发布:比如要上一个新的接口,可以通过在业务Nginx通过Lua写繁芜的业务规则实现不同的人看到不同的版本。
6、做事质量监控:我们可以记录要求相应韶光、缓存相应韶光、反向代理做事相应韶光来详细理解到底哪块做事慢了;其余记录非200状态码缺点来理解做事的可用率。
京东的交易大Nginx节点、无线部门正在开拓的无线Nginx网关、和单品页统一做事都是接入网关的实践,而单品页统一做事架构可以参考《京东商品详情页做事闭环实践》链接:https://www.iteye.com/blog/jinnianshilongnian-2258111
5、Web运用
此处所说的Web运用指的是页面模板渲染类型运用或者API做事类型运用;比如京东列表页/商品详情页便是一个模板渲染类型的运用,核心业务逻辑都是利用Lua写的,支配到Nginx容器。目前核心业务代码行数有5000多行,模板页面有2000多行,涉及到大量的打算逻辑,性能数据可以参考《构建需求相应式亿级商品详情页》链接:https://www.iteye.com/blog/jinnianshilongnian-2235572
整体处理过程和普通Web运用没什么差异:首先吸收要求并进行解析;然后读取JIMDB集群数据、如果没有则回源到Tomcat获取;然后进行业务逻辑处理;渲染模板;将相应内容返回给用户。
三、如何利用Nginx+Lua开拓Web运用
开拓一个Web运用我们须要从项目搭建、功能开拓、项目支配几个层面完成。
3.1、项目搭建
/export/App/nginx-app -------bin(脚本) ------------start.sh ------------stop.sh -------config(配置文件) ------------nginx.conf ------------domain ----------------nginx_product.conf ------------resources.properties -------lua(业务代码) ------------init.lua ------------product_controller.lua -------template(模板) --------------prodoct.html -------lualib(公共Lua库) ------------jd ----------------product_util.lua ----------------product_data.lua ------------resty ----------------redis.lua ----------------template.lua
全体项目构造从启停脚本、配置文件、公共组件、业务代码、模板代码几块进行划分。
1、启停脚本
启停脚本放在项目目录/export/App/nginx-app/bin/下。
start.sh是启动和更新脚本,即如果nginx没有启动则启动起来,否则reload:
if nginx没启动 then sudo /export/servers/nginx/sbin/nginx -t -c /export/App/nginx-app/config/nginx.conf sudo /export/servers/nginx/sbin/nginx -c /export/App/nginx-app/config/nginx.conf else sudo /export/servers/nginx/sbin/nginx -t sudo /export/servers/nginx/sbin/nginx -s reload end
stop.sh是停滞Nginx脚本:
sudo /export/servers/nginx/sbin/nginx -s quit
2、配置文件
配置文件放在/export/App/nginx-app/config目录下,包括了nginx.conf配置文件、nginx项目配置文件和资源配置文件。
nginx.confg配置文件
worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type text/html; #gzip干系 #超时时间 #日志格式 #反向代理配置 #lua依赖路径 lua_package_path "/export/App/nginx-app/lualib/?.lua;;"; lua_package_cpath "/export/App/nginx-app/lualib/?.so;;"; #server配置 include /export/App/nginx-app/config/domains/; #初始化脚本 init_by_lua_file "/export/App/nginx-app/lua/init.lua"; }
对付nginx.conf会进行一些通用的配置,如事情进程数、超时时间、压缩、日志格式、反向代理等干系配置;其余须要指定如下配置:
lua_package_path、lua_package_cpath指定我们依赖的通用Lua库从哪里加载;
include /export/App/nginx-app/config/domains/:用于加载server干系的配置,此处通过可以在一个nginx下指定多个server配置;
init_by_lua_file "/export/App/nginx-app/lua/init.lua":实行项目的一些初始化配置,比如加载配置文件。
nginx项目配置文件
/export/App/nginx-app/config/domains/nginx_product.conf用于配置当前web运用的一些server干系的配置:
#upstream upstream item_http_upstream { server 192.168.1.1 max_fails=2 fail_timeout=30s weight=5; server 192.168.1.2 max_fails=2 fail_timeout=30s weight=5; } #缓存 lua_shared_dict item_local_shop_cache 600m; server { listen 80; server_name item.jd.com item.jd.hk; #模板文件从哪加载 set $template_root "/export/App/nginx-app/template "; #url映射 location ~ "^/product/(\d+)\.html$" { rewrite /product/(.) http://item.jd.com/$1 permanent; } location ~ "^/(\d{6,12})\.html$" { default_type text/html; charset gbk; lua_code_cache on; content_by_lua_file "/export/App/nginx-app/lua/product_controller.lua"; } }
我们须要指定如upstream、共享字典配置、server配置、模板文件从哪加载、url映射,比如我们访问http://item.jd.com/1856584.html将交给/export/App/nginx-app/lua/product_controller.lua处理;也便是说我们项目的入口就有了。
资源配置文件resources.properties包含了我们的一些比如开关的配置、缓存做事器地址的配置等等。
3、业务代码
/export/App/nginx-app/lua/目录里存放了我们的lua业务代码,init.lua用于读取如resources.properties来进行一些项目初始化;product_controller.lua可以算作Java Web中的Servlet,吸收、处理、响运用户要求。
4、模板
模板文件放在/export/App/nginx-app/template/目录下,利用相应的模板引擎进行编写页面模板,然后渲染输出。
5、公共Lua库
存放了一些如redis、template等干系的公共Lua库,还有一些我们项目中通用的工具库如product_util.lua。
到此一个大略的项目的构培养先容完了,对付开拓一个项目来说还会牵扯到分模块等事情,不过对付我们这种Lua运用来说,建议不要过度抽象,只管即便小巧即可。
3.2、功能开拓
接下来就须要利用相应的API来实现我们的业务了,比如product_controller.lua:
--加载Lua模块库 local template = require("resty.template") --1、获取要求参数中的商品ID local skuId = ngx.req.get_uri_args()["skuId"]; --2、调用相应的做事获取数据 local data = api.getData(skuId) --3、渲染模板 local func = template.compile("product.html") local content = func(data) --4、通过ngx API输出内容 ngx.say(content)
开拓完成后将项目支配到测试环境,实行start.sh启动nginx然后进行测试。
详细的开拓过程和API的利用,请参考《跟我学Nginx+Lua开拓》。此处不做详细编码实现。
四、基于Nginx+Lua的常用功能总结到此我们对付Nginx开拓已经有了一个整体的认识,对付Nginx粘合Lua来开拓运用可以说是一把锋利的瑞士军刀,可以帮我们很随意马虎的办理很多问题,可以开拓Web运用、接入网关、API网关、推送、日志采集等运用,不过个人认为适宜开拓业务逻辑单一、核心代码行数较少的运用,不适宜业务逻辑繁芜、功能繁多的业务型或者企业级运用;末了我们总结下基于Nginx+Lua的常用架构模式中一些常见实践和场景:
动态负载均衡;
防火墙(DDOS、IP/URL/UserAgent/Referer黑名单、防盗链等);
限流;
降级;
AB测试/灰度发布;
多级缓存模式;
做事端要求聚合;
做事质量监控。
一些问题
1、在开拓nginx运用时利用UTF-8编码可以减去很多麻烦;
2、GBK转码解码时利用GB18030,否则一些分外字符会涌现乱码;
3、cjson库对付如\uab1这种缺点的unicode转码会失落败,可以利用纯Lua编写的dkjson;
4、社区版nginx不支持upstream的域名动态解析;可以考虑proxy_pass http://p.3.local/prices/mgets$is_args$args,然后合营resolver来实现;或者在lua中进行http调用;如果DNS碰着性能瓶颈可以考虑在本机支配如dnsmasq来缓存;或者考虑利用balancer_by_lua功能实现动态upstream;
5、为相应添加处理做事器IP的相应头,方便定位问题;
6、根据业务设置合理的超时时间;
7、走CDN的业务当发生缺点时返回的500/503/302/301等非正常相应不要设置缓存。
参考:
OpenResty: https://openresty.org
构建需求相应式亿级商品详情页: https://www.iteye.com/blog/jinnianshilongnian-2235572
京东商品详情页做事闭环实践:https://www.iteye.com/blog/jinnianshilongnian-2258111
跟我学Nginx+Lua开拓:https://www.iteye.com/blog/jinnianshilongnian-2190344
转自:https://blog.csdn.net/moonpure/article/details/78410193