OpenRestry实战一:概念和应用场景

openrestry介绍:

OpenResty是一个通过Lua扩展Nginx实现的可伸缩的Web平台,内部集成了大量精良的Lua库、第三方模块以及大多数的依赖项。
用于方便地搭建能够处理超高并发、扩展性极高的动态Web应用、Web服务和动态网关。
功能和nginx类似,就是由于支持lua动态脚本,所以更加灵活,可以实现鉴权、限流、分流、日志记录、灰度发布等功能。
OpenResty通过Lua脚本扩展nginx功能,可提供负载均衡、请求路由、安全认证、服务鉴权、流量控制与日志监控等服务。

openrestry web应用集成了nginx和lua,下面分别介绍各个概念及应用场景:

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。
Lua应用场景:游戏开发、独立应用脚本、Web应用脚本、扩展和数据库插件如:MySQL Proxy和MySQL WorkBench、安全系统,如入侵检测系统、商城类的秒杀功能。

ngx_lua:

ngx_lua是Nginx的一个模块,将Lua嵌入到Nginx中,从而可以使用Lua来编写脚本,这样就可以使用Lua编写应用脚本,部署到Nginx中运行,即Nginx变成了一个Web容器;这样开发人员就可以使用Lua语言开发高性能Web应用了。
ngx_lua提供了与Nginx交互的很多的API,对于开发人员来说只需要学习这些API就可以进行功能开发,而对于开发web应用来说,如果接触过Servlet的话,其开发和Servlet类似,无外乎就是知道接收请求、参数解析、功能处理、返回响应这几步的API是什么样子的。

应用场景:
理论上可以使用ngx_lua开发各种复杂的web应用,不过Lua是一种脚本/动态语言,不适合业务逻辑比较重的场景,适合小巧的应用场景,代码行数保持在几十行到几千行。
目前见到的一些应用场景如下:
1.web应用:会进行一些业务逻辑处理,甚至进行耗CPU的模板渲染,一般流程:mysql/redis/http获取数据、业务处理、产生JSON/XML/模板渲染内容,比如京东的列表页/商品详情页,咨询类的应用首页;
2.接入网关:实现如数据校验前置、缓存前置、数据过滤、API请求聚合、AB测试、灰度发布、降级、监控等功能,比如京东的交易大Nginx节点、无线部门正在开发的无线网关、单品页统一服务、实时价格、动态服务;
3.Web防火墙:可以进行IP/URL/UserAgent/Referer黑名单、限流等功能;
4.缓存服务器:可以对响应内容进行缓存,减少到后端的请求,从而提升性能;
5.其他:如静态资源服务器、消息推送服务、缩略图裁剪等。

nginx+lua架构:

核心接入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实现内容页面的缓存;

业务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状态码错误来了解服务的可用率。

OpenResty处理请求大致分为四个阶段,可在不同阶段使用lua脚本来定制不同行为:

set_by_lua* : 流程分支处理判断变量初始化
rewrite_by_lua* : 转发、重定向、缓存等功能(例如特定请求代理到外网)
access_by_lua* : IP 准入、接口权限等情况集中处理(例如配合 iptable 完成简单防火墙)
content_by_lua* : 内容生成
header_filter_by_lua* : 响应头部过滤处理(例如添加头部信息)
body_filter_by_lua* : 响应体过滤处理(例如完成应答内容统一成大写)
执行阶段概念:

log_by_lua* : 会话完成后本地异步完成日志记录(日志可以记录在本地,还可以同步到其 他机器)
实际上我们只使用其中一个阶段
content_by_lua* ,也可以完成所有的处理。但这样做,会让我们的代码比较臃肿,越到后期越发难以维护。把我们的逻辑放在不同阶段,分工明确,代码独立,后期发力可以有很多有意思的玩法。
 

是的,OpenResty可以通过Lua脚本和Redis数据库来动态获取upstream。具体实现方式如下: 1. 在Nginx配置文件中定义upstream,并将upstream的backend地址设置为一个占位符,例如: ``` upstream backend { server 127.0.0.1:8080; } server { location / { proxy_pass http://backend; } } ``` 2. 在Lua脚本中连接Redis数据库,并获取upstream的backend地址列表。例如: ``` local redis = require "resty.redis" local red = redis:new() -- 连接Redis数据库 red:set_timeout(1000) local ok, err = red:connect("127.0.0.1", 6379) if not ok then ngx.log(ngx.ERR, "failed to connect to Redis: ", err) return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) end -- 获取backend地址列表 local backend_list, err = red:smembers("backend_list") if not backend_list then ngx.log(ngx.ERR, "failed to get backend list from Redis: ", err) return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) end -- 关闭Redis连接 red:set_keepalive(10000, 100) -- 将backend地址列表设置到upstream中 local upstream = require "ngx.upstream" local backend_servers = {} for i, backend in ipairs(backend_list) do table.insert(backend_servers, {["server"] = backend}) end local ok, err = upstream.set_servers("backend", backend_servers) if not ok then ngx.log(ngx.ERR, "failed to update backend servers: ", err) return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) end ``` 3. 在Nginx配置文件中引入Lua脚本,并定时执行该脚本。例如: ``` lua_shared_dict backend 1m; upstream backend { server 127.0.0.1:8080; } server { location / { access_by_lua_file "/path/to/lua/script.lua"; proxy_pass http://backend; } } ``` 这样,当Redis数据库中的backend地址列表发生变化时,Lua脚本会自动更新upstream的backend地址,并实现动态负载均衡。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值