高性能web网关之Openresty
- 一、Openresty 简介
- 二、Openresty 应用场景
- 三、lua-nginx-module
- 3.1、Lua 模块指令顺序
- 3.2、Lua嵌入nginx
- 四、责任链
- 五、cosocket
- 后言
一、Openresty 简介
openresty 是一个基于 nginx 与 lua 的高性能 web 平台,其内部集成了大量精良的 lua 库、第三方模块以及大数的依赖项。用于方便搭建能够处理超高并发、扩展性极高的动态 web 应用、web 服务和动态网关。
openresty 通过汇聚各种设计精良的 nginx 模块,从而将 nginx有效地变成一个强大的通用 Web 应用平台。这样,可以使用 Lua 脚本语言调动 Nginx 支持的各种C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。
openresty 的目标是让你的 Web 服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型(多reactor 模型),不仅仅对 HTTP 客户端请求(stream),甚至于对远程后端诸如MySQL、PostgreSQL、Memcached 以及 Redis etcd kafka grpc等都进行一致的高性能响应(upstream)。
二、Openresty 应用场景
奇虎360的所有服务端团队都在使用,京东、百度、魅族、知乎、优酷、新浪这些互联网公司都在使用。有用来写 WAF(web application firewall)、有做 CDN 调度、有做广告系统、消息推送系统,API server 的。还有用在非常关键的业务上。
- 在请求真正到达上游服务之前,Lua 可以随心所欲的做复杂的访问控制和安全检测。
- 随心所欲的操控响应头里面的信息。
- 从外部存储服务(比如 Redis,Memcached,MySQL,Postgres)中获取后端信息,并用这些信息来实时选择哪一个后端来完成业务访问。
- 在内容 handler 中随意编写复杂的 Web 应用,使用同步但依然非阻塞的方式,访问后端数据库和其他存储。
- 在 rewrite 阶段,通过 Lua 完成非常复杂的 URL dispatch。
- 用 Lua 可以为 nginx 子请求和任意 location,实现高级缓存机制。
三、lua-nginx-module
nginx 采用模块化设计,使得每一个 http 模块可以仅专注于完成一个独立的、简单的功能,而一个请求的完整处理过程可以由无数个 http 模块共同合作完成。为了灵活有效地指定下一个http 处理模块是哪一个;http 框架依据常见的的处理流程将处理阶段划分为 11 个阶段,其中每一个阶段都可以由任意多个http 模块流水式地处理请求。
openresty 将 lua 脚本嵌入到 nginx 阶段处理的末尾模块下;这样以来并不会影响 nginx 原有的功能,而是在 nginx 基础上丰富它的功能。
嵌入 lua 的优点是:使用 openresty 开发,不需要重新编译,直接修改 lua 脚本,重新启动即可。
抽象:虚拟主机对应一个lua虚拟机,每个请求对应一个协程。
3.1、Lua 模块指令顺序
3.2、Lua嵌入nginx
(1)init_by_lua。master fork之前调用,此阶段初始化的数据将被复制多个worker进程中。它的作用有:加载一些耗时模块、设置全局变量、初始化共享内存等。
在 nginx 重新加载配置文件时,运行里面 lua 脚本,常用于全局变量的申请。例如 lua_shared_dict 共享内存的申请,只有当 nginx 重启后,共享内存数据才清空,这常用于统计。
(2)init_worker_by_lua。master fork之后,worker初始化时调用,在每个 Nginx 工作进程启动时执行;此阶段初始化的数据各个worker可不同。它很重要的一个作用是开启定时器。
(3)ssl_certificate_by_lua。ssl阶段,在握手时设置安全证书。
(4)set_by_lua。用于设置nginx变量;设置一个变量,常用与计算一个逻辑,然后返回结果,该阶段不能运行Output API、Control API、Subrequest API、Cosocket API。
(5)rewrite_by_lua。用于执行内部url重写或外部重定向。在 access 阶段前运行,主要用于 rewrite url。
(6)access_by_lua。用于访问控制。这条指令运行于 nginx access 阶段的末
尾,因此总是在 allow 和 deny 这样的指令之后运行,它们同属 access 阶段。可用来判断请求是否具备访问权限。
(7)content_by_lua。用于内容管理。此阶段是所有请求处理阶段中最为重要的一个,运行在这个阶段的配置指令一般都肩负着生成内容(content)并输出HTTP 响应。
(8)header_filter_by_lua。设置应答消息的头部信息;一般只用于设置 Cookie 和 Headers 等。
(9)body_filter_by_lua。用于修改应答body的内容。一般会在一次请求中被调用多次,因为这是实现基于 HTTP 1.1 chunked 编码的所谓“流式输出”的。
(10)log_by_lua。用于log请求处理阶段,用lua处理日志。该阶段总是运行在请求结束的时候,用于请求的后续操作,如在共享内存中进行统计数据,如果要高精确的数据统计,应该使用 body_filter_by_lua
(11)balancer_by_lua。上游服务器的负载均衡。
四、责任链
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。接收者是由多个处理对象构成,把这些处理对象连接成一个链表,并沿着这个链条传递请求,直到由一个对象处理它为止。
责任链的执行流程是通过链表组合处理对象,按需打断。openresty将lua嵌入nginx处理阶段中,通过ngx.exit进行打断:ngx.OK打断当前阶段,执行下一个阶段;而>=200打断整个请求处理。
五、cosocket
openresty 为 nginx 添加的最核心的功能就是 cosocket。自cosocket 加入,可以在 http 请求处理中访问第三方服务。cosocket 主要依据 nginx 中的事件机制和 lua 的协程结合后实现了非阻塞网络 io。在业务逻辑使用层面上可以通过同步非阻塞的方式来写代码。引入 cosocket 后,nginx 中相当于有了多条并行同步逻辑线(lua 协程),nginx 中单线程负责唤醒或让出其中 lua 协程。唤醒或让出依据来源于协程运行的条件是否得到满足。
通过lua协程将socket的异步事件编程转化为同步处理。基于cosocket实现了很多精良的第三方库。
cosoket有:ngx.socket.tcp、ngx.socket.udp、resty.redis、resty.mysql等。
后言
本专栏知识点是通过<零声教育>的系统学习,进行梳理总结写下文章,对c/c++linux系统提升感兴趣的读者,可以点击链接查看详细的服务:C/C++服务器开发 。