关于openresty可参考官方文档:css
http://openresty.org/cn/download.htmlhtml
一、这个是什么?mysql
在互联网公司,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
OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建可以处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。git
OpenResty® 经过汇聚各类设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可使用 Lua 脚本语言调动 Nginx 支持的各类 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发链接的高性能 Web 应用系统。github
OpenResty® 的目标是让你的Web服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不只仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。web
二、这里复制了他人写的比较好的博客:总的来讲:redis
ngx_lua(OpenResty)是Nginx的一个模块,将Lua嵌入到Nginx中,从而可使用Lua来编写脚本,这样就可使用Lua编写应用脚本,部署到Nginx中运行,即Nginx变成了一个Web容器;这样开发人员就可使用Lua语言开发高性能Web应用了。算法
1、Nginx优势sql
Nginx设计为一个主进程多个工做进程的工做模式,每一个进程是单线程来处理多个链接,并且每一个工做进程采用了非阻塞I/O来处理多个链接,从而减小了线程上下文切换,从而实现了公认的高性能、高并发;所以在生成环境中会经过把CPU绑定给Nginx工做进程从而提高其性能;另外由于单线程工做模式的特色,内存占用就很是少了。
Nginx更改配置重启速度很是快,能够毫秒级,并且支持不中止Nginx进行升级Nginx版本、动态重载Nginx配置。
Nginx模块也是很是多,功能也很强劲,不只能够做为http负载均衡,Nginx发布1.9.0版本还支持TCP负载均衡,还能够很容易的实现内容缓存、web服务器、反向代理、访问控制等功能。
2、Lua的优势
Lua是一种轻量级、可嵌入式的脚本语言,这样能够很是容易的嵌入到其余语言中使用。另外Lua提供了协程并发,即以同步调用的方式进行异步执行,从而实现并发,比起回调机制的并发来讲代码更容易编写和理解,排查问题也会容易。Lua还提供了闭包机制,函数能够做为First Class Value 进行参数传递,另外其实现了标记清除垃圾收集。
由于Lua的小巧轻量级,能够在Nginx中嵌入Lua VM,请求的时候建立一个VM,请求结束的时候回收VM。
3、什么是ngx_lua
ngx_lua是Nginx的一个模块,将Lua嵌入到Nginx中,从而可使用Lua来编写脚本,这样就可使用Lua编写应用脚本,部署到Nginx中运行,即Nginx变成了一个Web容器;这样开发人员就可使用Lua语言开发高性能Web应用了。
ngx_lua提供了与Nginx交互的不少的API,对于开发人员来讲只须要学习这些API就能够进行功能开发,而对于开发web应用来讲,若是接触过Servlet的话,其开发和Servlet相似,无外乎就是知道接收请求、参数解析、功能处理、返回响应这几步的API是什么样子的。
4、开发环境
咱们可使用OpenResty来搭建开发环境,OpenResty将Nginx核心、LuaJIT、许多有用的Lua库和Nginx第三方模块打包在一块儿;这样开发人员只须要安装OpenResty,不须要了解Nginx核心和写复杂的C/C++模块就能够,只须要使用Lua语言进行Web应用开发了。
如何安装能够参考《跟我学Nginx+Lua开发》。
5、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组件供咱们使用,对于大部分应用场景来讲如今生态环境中的组件已经足够多了;若是不知足需求也能够本身去写来完成本身的需求。
6、场景
理论上可使用ngx_lua开发各类复杂的web应用,不过Lua是一种脚本/动态语言,不适合业务逻辑比较重的场景,适合小巧的应用场景,代码行数保持在几十行到几千行。目前见到的一些应用场景:
web应用:会进行一些业务逻辑处理,甚至进行耗CPU的模板渲染,通常流程:mysql/redis/http获取数据、业务处理、产生JSON/XML/模板渲染内容,好比京东的列表页/商品详情页;
接入网关:实现如数据校验前置、缓存前置、数据过滤、API请求聚合、AB测试、灰度发布、降级、监控等功能,好比京东的交易大Nginx节点、无线部门正在开发的无线网关、单品页统一服务、实时价格、动态服务;
Web防火墙:能够进行IP/URL/UserAgent/Referer黑名单、限流等功能;
缓存服务器:能够对响应内容进行缓存,减小到后端的请求,从而提高性能;
其余:如静态资源服务器、消息推送服务、缩略图裁剪等。
1、负载均衡
如上图,咱们首先经过LVS+HAProxy将流量转发给核心Nginx 1和核心Nginx 2,即实现了流量的负载均衡,此处可使用如轮训、一致性哈希等调度算法来实现负载的转发;而后核心Nginx会根据请求特征如“Host:item.jd.com”,转发给相应的业务Nginx节点如单品页Nginx 1。此处为何分两层呢?
一、核心Nginx层是无状态的,能够在这一层实现流量分组(内网和外网隔离、爬虫和非爬虫流量隔离)、内容缓存、请求头过滤、故障切换(机房故障切换到其余机房)、限流、防火墙等一些通用型功能;
二、业务Nginx如单品页Nginx,能够在在业务Nginx实现业务逻辑、或者反向代理到如Tomcat,在这一层能够实现内容压缩(放在这一层的目的是减小核心Nginx的CPU压力,将压力分散到各业务Nginx)、AB测试、降级;即这一层的Nginx跟业务有关联,实现业务的一些通用逻辑。
不论是核心Nginx仍是业务Nginx,都应该是无状态设计,能够水平扩容。
业务Nginx通常会把请求直接转发给后端的业务应用,如Tomcat、PHP,即将请求内部转发到相应的业务应用;当有的Tomcat出现问题了,能够在这一层摘掉;或者有的业务路径变了在这一层进行rewrite;或者有的后端Tomcat压力太大也能够在这一层降级,减小对后端的冲击;或者业务须要灰度发布时也能够在这一层Nginx上控制。
2、单机闭环
所谓单机闭环即全部想要的数据都能从本服务器直接获取,在大多数时候无需经过网络去其余服务器获取。
如上所示,主要有三种应用模式:
2.一、第一张图应用场景是Nginx应用谁也不依赖,好比咱们的Cookie白名单应用,其目的是不在白名单中的Cookie将被清理,防止你们随便将Cookie写到jd.om根下;你们访问http://www.jd.com时,会看到一个http://ccc.jd.com/cookie_check的请求用来清理Cookie的;对于这种应用很是简单,不须要依赖数据源,直接单应用闭环便可。
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来实现这个功能。
还一些业务型应用场景以下图所示
商品页面是由商品框架和其余维度的页面片断(面包屑、相关分类、商家信息、规格参数、商品详情)组成;或者首页是由首页框架和一些页面片断(分类、轮播图、楼层一、楼层N)组成;分维度是由于不一样的维度是独立变化的。对于这种静态内容可是须要进行框架内容嵌入的方式,Nginx自带的SSI(Server Side Include)能够很轻松的完成;也可使用Lua程序更灵活的完成(读取框架、读取页面片断、合并输出)。
好比商品页面的架构咱们能够这样:
首先接收到商品变动消息,商品页面同步Worker会根据消息维度生成相关的页面推送到Nginx服务器;Nginx应用再经过SSI输出。目前京东商品详情页没有再采用这种架构,具体架构能够参考《构建需求响应式亿级商品详情页》。
对于首页的架构是相似的,由于其特色(框架变化少,楼层变化较频繁)和个性化的要求,楼层通常实现为异步加载。
2.三、 第三张图和第二张图的不一样处是再也不直接读取文件系统,而是读取本机的Redis或者Redis集群或者如SSDB这种持久化存储或者其余存储系统都是能够的,好比直接说的商品页面可使用SSDB进行存储实现。文件系统一个很大的问题是当多台服务器时须要Worker去写多台服务器,而这个过程可使用SSDB的主从实现。
此处能够看到,不论是图二仍是图三架构,都须要Worker去进行数据推送;假设本机数据丢了可怎么办?所以实际大部分应用不会是彻底单机闭环的,而是会采用以下架构:
即首先读本机,若是没数据会回源到相应的Web应用从数据源拉取原始数据进行处理。这种架构的大部分场景本机均可以命中数据,只有不多一部分状况会回源到Web应用。
如京东的实时价格/动态服务就是采用相似架构。
3、分布式闭环
单机闭环会遇到以下两个主要问题: 一、数据不一致问题(好比没有采用主从架构致使不一样服务器数据不一致);二、遇到存储瓶颈(磁盘或者内存遇到了天花板)。
解决数据不一致的比较好的办法是采用主从或者分布式集中存储;而遇到存储瓶颈就须要进行按照业务键进行分片,将数据分散到多台服务器。
如采用以下架构,按照尾号将内容分布到多台服务器。
即第一步先读取分布式存储(JIMDB是京东的一个分布式缓存/存储系统,相似于Redis);若是不命中则回源到Tomcat集群(其会调用数据库、服务总线获取相关数据)来获取相关数据。能够参考《构建需求响应式亿级商品详情页》来获取更详细的架构实现。
JIMDB集群会进行多机房主从同步,各自机房读取本身机房的从JIMDB集群,以下图
4、接入网关
接入网关也能够叫作接入层,即接收到流量的入口,在入口咱们能够进行以下事情:
4.1、核心接入Nginx会作以下事情:
一、动态负载均衡;一、普通流量走一致性哈希,提高命中率;热点流量走轮训减小单服务器压力;二、根据请求特征将流量分配到不一样分组并限流(爬虫、或者流量大的IP);三、动态流量(动态增长upstream或者减小upstream或者动态负载均衡)可使用balancer_by_lua或者微博开源的upsync;
二、防DDOS攻击限流:能够将请求日志推送到实时计算集群,而后将须要限流的IP推送到核心Nginx进行限流;
三、非法请求过滤:好比应该有Referer却没有,或者应该带着Cookie却没有Cookie;
四、请求聚合:好比请求的是http://c.3.cn/proxy?methods=a,b,c,核心接入Nginx会在服务端把Nginx并发的请求并把结果聚合而后一次性吐出;
五、请求头过滤:有些业务是不须要请求头的,所以能够在往业务Nginx转发时把这些数据过滤掉;
六、缓存服务:使用Nginx Proxy Cache实现内容页面的缓存;
4.2、业务Nginx会作以下事情:
一、缓存:对于读服务会使用大量的缓存来提高性能,咱们在设计时主要有以下缓存应用:首先读取Nginx本地缓存 Shared Dict或者Nginx Proxy Cache,若是有直接返回内容给用户;若是本地缓存不命中,则会读取分布式缓存如Redis,若是有直接返回;若是仍是不命中则回源到Tomcat应用读取DB或调用服务获取数据。另外咱们会按照维度进行数据的缓存。
二、业务逻辑:咱们会进行一些数据校验/过滤逻辑前置(如商品ID必须是数字)、业务逻辑前置(获取原子数据,而后在Nginx上写业务逻辑)。
三、细粒度限流:按照接口特征和接口吞吐量来实现动态限流,好比后端服务快扛不住了,那咱们就须要进行限流,被限流的请求做为降级请求处理;经过lua-resty-limit-traffic能够经过编程实现更灵活的降级逻辑,如根据用户、根据URL等等各类规则,如降级了是让用户请求等待(好比sleep 100ms,这样用户请求就慢下来了,可是服务仍是可用)仍是返回降级内容。
四、降级:降级主要有两种:主动降级和被动降级;如请求量太大扛不住了,那咱们须要主动降级;如后端挂了或者被限流了或者后端超时了,那咱们须要被动降级。降级方案能够是:一、返回默认数据如库存默认有货;二、返回静态页如预先生成的静态页;三、部分用户降级,告诉部分用户等待下再操做;四、直接降级,服务没数据,好比商品页面的规格参数不展现;五、只降级回源服务,便可以读取缓存的数据返回,实现部分可用,可是不会回源处理;
五、AB测试/灰度发布:好比要上一个新的接口,能够经过在业务Nginx经过Lua写复杂的业务规则实现不一样的人看到不一样的版本。
六、服务质量监控:咱们能够记录请求响应时间、缓存响应时间、反向代理服务响应时间来详细了解到底哪块服务慢了;另外记录非200状态码错误来了解服务的可用率。
京东的交易大Nginx节点、无线部门正在开发的无线Nginx网关、和单品页统一服务都是接入网关的实践,而单品页统一服务架构能够参考《京东商品详情页服务闭环实践》。
5、Web应用
此处所说的Web应用指的是页面模板渲染类型应用或者API服务类型应用;好比京东列表页/商品详情页就是一个模板渲染类型的应用,核心业务逻辑都是使用Lua写的,部署到Nginx容器。目前核心业务代码行数有5000多行,模板页面有2000多行,涉及到大量的计算逻辑,性能数据能够参考《构建需求响应式亿级商品详情页》。
总体处理过程和普通Web应用没什么区别:首先接收请求并进行解析;而后读取JIMDB集群数据、若是没有则回源到Tomcat获取;而后进行业务逻辑处理;渲染模板;将响应内容返回给用户。