背景
先来说下今天的主角openresty,它是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。可以使用Lua编写脚本,然后部署到Nginx Web容器中运行。利用nginx的高并发处理能力,轻松构建自己的高性能的Web服务。
但个人觉得,使用lua编写一些复杂业务逻辑不是它的专长,编写成本也比较高。现有业务的web系统,大多还是以java、c#等为主。如要完全改为lua实现,几乎不可能完成。
上一篇分享《京东活动系统亿级流量架构应对之术》的核心,其实是活动页面的浏览与页面的渲染的异步化。即:活动页面在被浏览时,view工程直接从redis或者硬盘获取已经在engine工程被渲染好的页面返回。简而言之,就是通过engine工程提前渲染好页面,实现页面全静态化。保证view工程每次请求都直接获取静态页面返回,无需等待页面渲染。
这样复杂的业务逻辑已经被完全剥离到engine工程。view工程可以采用nginx+lua+redis+硬盘实现自己的高性能web服务。最终架构如下:
其中,硬盘和redis互为主备,做一个开关相互切换。
lua+redis 字符串压缩
engine工程采用java把静态html字符串放入redis,view工程再采用lua从redis中获取。这里本身没有问题,但是由于活动页面的内容较多,一个静态页面html动辄几百kb,大的甚至几兆。存取速度势必会很慢,很自然的会想到压缩。
在view工程没有引入openresty之前,我们所有的活动页面都是采用的java的gzip(GZIPInputStream和GZIPOutputStream)来进行压缩和解压,也收到很好的收益,并且待压缩内容越长压缩效果越是明显。存入redis前先用GZIPInputStream压缩,把压缩后的字节数组存入redis(在engine中完成)。view工程从redis中取出,再用GZIPOutputStream解压,还原成正常的活动页面静态html字符串返回。如下:
现在要把view工程改为用openresty实现,难点就在于用lua从redis中取出字符串的内容是用gzip压缩后的,直接返回是乱码。通过查找资料,lua可以调用zlib来实现对字符串的压缩和解压(参考
https://github.com/brimworks/lua-zlib)。
在engine工程压缩端把gzip压缩方式改成zlib压缩、并存入redis(java zlib压缩可以参考http://snowolf.iteye.com/blog/465433)。view工程就可以直接从redis中获取,再通过zlib解压即可。但由于redis已存在大量gzip压缩的活动页,这种方式没办法实现平滑过渡。
通过分析gzip和zlib,可以发现他们本质上都是deflate算法进行压缩。只是gzip相对于zlib会多一个描述头部,如果直接用lua-zlib解压gzip压缩的内容,头部会多出一段乱码,其余内容完全一样。我们直接去掉这部分乱码头部即可:
lua实现如下:
nginx.conf配置如下:
location ~ ^/act/(\w+)\.html{ default_type "text/html"; charset utf-8,gbk; set $shortUrl $1; content_by_lua_file /export/app/view/page_view_redis.lua; }
/export/app/view/page_view_redis.lua代码如下:
local redis_pool = require "redis_pool" -- lua redis缓存池封装 local zlib = require "zlib" local stream = zlib.inflate() local key = "pc-page-cache-key-"..ngx.var.shortUrl local red = redis_pool:new() local res, err = red:get(key) if res ~= nil then -- 解压, page内容为:"xxxxxx<html><header></header><body>test page</body></html>"其中xxxxx即为gzip压缩的乱码头 local page=stream(res); -- 去掉乱码头 local h1,h2=string.find(r, "<html>") page= string.sub(page,h1) -- 返回页面内容 ngx.say(page) end ngx.say("页面不存在") ngx. ngx.exit(200)
lua+硬盘
先说下,静态活动页面存硬盘:
1、engine:渲染页面完成;
2、engine:页面内容存入redis;
3、engine:根据活动url hash规则,向指定的view服务器发送数据推送请求;
4、view:view服务器收到请求,从redis中获取页面内容存到指定目录下。
步骤4中,从redis中取出,还是要先进行解压,再存储到指定目录,lua代码如下:
nginx.conf配置:
# engine 发起的保存页面到本地硬盘请求 location ~ ^/save/(\w+)\.html{ default_type "text/html"; charset utf-8,gbk; set $key $1; content_by_lua_file /export/app/view/save_disk.lua; }
/export/app/view/save_disk.lua代码内容如下:
local redis_pool = require "redis_pool" -- lua redis缓存池封装 local util_tools = require "util_tools" local zlib = require "zlib" local stream = zlib.inflate() local key = "pc-page-cache-key-"..ngx.var.shortUrl local red = redis_pool:new(); local res, err = red:get(key) if res ~= nil then -- 解压, page内容为:"xxxxxx<html><header></header><body>test page</body></html>"其中xxxxx即为gzip压缩的乱码头 local page=stream(res); -- 去掉乱码头 local h1,h2=string.find(page, "<html>") page= string.sub(page,h1) -- 通过md5(uri),构造存储目录和文件名 local pageUri="/act/"..ngx.var.shortUrl..".html" local filePath,pageName = util_tools:get_abpath(pageUri) local file,err=io.open(filePath) if not file then --如果文件目录不存在,就先创建 os.execute("mkdir -p "..filePath) end local f = assert(io.open(filePath..pageName,'w')) --写入硬盘文件中 f:write(page) -- 存储页面 f:close() end ngx.exit(200)
util_tools.lua代码内容:
local rootPath="/export/static/page/" local uri_pre = "page" local util_tools = {} -- get static html page -- 如md5(url)=xxxxxxabc,最终的页面内容存放到:"/export/static/page/c/ab/xxxxxxabc",拼装的新uri为:/page/c/ab/xxxxxxabc function util_tools:get_path(url) local md5path = ngx.md5(url) local pathlen = string.len(md5path) local path1 = string.sub(md5path,-1) local path2 = string.sub(md5path,pathlen-2,-2) local abPathFile = uri_pre.."/"..path1.."/"..path2.."/"..md5path return abPathFile end -- get static html page path and name --类似nginx proxy cache对文件的存储结构,把链接先md5,取后三位字符串,拼成文件存放目录 --存取文件:如md5(url)=xxxxxxabc,最终的页面内容存放到:"/export/static/page/c/ab/xxxxxxabc" function util_tools:get_abpath(url) local md5path = ngx.md5(url) local pathlen = string.len(md5path) local path1 = string.sub(md5path,-1) local path2 = string.sub(md5path,pathlen-2,-2) local abPathFile = rootPath.."/"..path1.."/"..path2.."/" return abPathFile,md5path end return util_tools
步骤3中的url hash规则讲解:由于京东活动页面较多,所有在线的活动静态页面大小已经超过10G。如果把如此大量的数据存放到每台sale服务上,是不可取的。
首先不方便扩展,随着京东业务快速增长,活动数据也会快速增加,而硬盘大小始终有限。
其次,在用lua读取静态页面时,从如此大量的静态文件中查找也会增加耗时。通过活动url规则进行hash,把这些活动页面内容平均分配到不同sale服务器分组,是我们目前采取的方式。其中还涉及到sale服务器主备分组,这里就不再详细讲解,后面单独再做一次分享。
再说下从硬盘获取,根据url拼装出文件存放路径,直接从本地硬盘获取即可。代码如下:
location ~ ^/act/(\w+)\.html{ default_type "text/html"; charset utf-8,gbk; set $shortUrl $1; content_by_lua_file /export/app/view/page_view_redis.lua; } /export/app/view/page_view_redis.lua代码内容: local util_tools = require "util_tools" -- 如果链接为http://sale.jd.com/act/Ok70VSmKFZo1Weca.html,pageUri为: -- /act/Ok70VSmKFZo1Weca.html local pageUri="/act/"..ngx.var.key..".html" local abpath = util_tools:get_abpath(pageUri) --abpath相对路径 规则为:/page/c/ab/xxxxxxabc local resSta = ngx.location.capture(abpath) if resSta.status == ngx.HTTP_OK then --读取到硬盘 if resSta.body ~= nil and resSta.body ~= "nil" and resSta.body ~= "" then ngx.say(resSta.body) --返回页面内容 return ngx.exit(200) end end
view 工程代码结构(lua)
再来看下代码结构:
|--export
|-----|app
|---------|view
|--------------| page_view_redis.lua
|--------------| page_view_disk.lua
|--------------| save_disk.lua
|--------------| util_tools.lua
|-----|pro
|--------|nginx
|------------|conf
|----------------|nginx.conf
比起java来是不是简洁多了。页面浏览可以读redis和硬盘,可以在lua_shared_dict中存放一个切换开关,根据开关值选择执行page_view_redis.lua或者page_view_disk.lua。
好了就写这么多了吧,回顾下这次讲解的主要内容:lua读redis、lua-zlib读gzip压缩字符串、lua读硬盘等。
相关推荐
openresy最佳实践 Lua 入门 Nginx LuaCjsonLibrary PostgresNginxModule LuaNginxModule LuaRestyDNSLibrary LuaRestyLock 测试
**标题详解:**"lua-resty-mongol_openresty连接mongoldb的lua库" “lua-resty-mongol”是专为OpenResty设计的一个lua库,用于在OpenResty环境中与MongoDB数据库进行交互。这个库的创建是为了在高性能的Web服务中...
1、资源内容:taotao-weblog-analysis基于openresty kafka hadoop hive 离线网站日志点击流数据分 2、代码特点:内含运行结果,不会运行可私信,参数化编程、参数可方便更改、代码编程思路清晰、注释明细,都经过...
基于 lua-nginx-module(openresty) 的 web 应用防火墙 - 不懂运行,下载完可以私聊问,可远程教学 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关...
lua-resty-shell, 用于OpenResty应用服务器的微型子进程/shell 库 简介当你需要执行子进程( 或者 shell 命令) 时,这个小型库是用于与OpenResty应用程序一起使用的。 除了 non completely完全不阻塞外,即使是完全不...
《OpenResty WEB安全防护漏洞详解:CVE-2018-9230》 OpenResty,作为一款基于Nginx与Lua...对于OpenResty用户来说,定期更新和维护是最基本的安全实践,同时结合有效的输入验证和过滤策略,可以大大增强系统的安全性。
【标题】"taotao-weblog-analysis基于openresty kafka hadoop hive 日志点击流数据分析"涉及的关键技术点包括OpenResty、Kafka、Hadoop和Hive,这些都是大数据处理和分析领域的重要组件。 OpenResty是基于Nginx与...
z-博客-openresty依赖 :基于Nginx的高性能Web服务器 :适用Nginx和OpenResty的HTML模板渲染引擎 : PostgreSQL的Lua驱动 :基于Woothee的User-Agent解析器 : OpenResty和ngx_lua中的HTTP客户端 :自动注册和更新...
Nginx 高并发系统内核优化 nginx 并发数问题思考:worker_connections,worker_processes与 max clients 如何在工作中提高Ngixn服务器性能?达到高效 并发 = 同步/异步/阻塞/非阻塞/进程/线程 The Complete NGINX...
lua-resty-session是OpenResty的安全,灵活的会话库。 Hello World与lua-resty-session worker_processes 1 ; events { worker_connections 1024 ; } http { server { listen 8080 ; server_name localhost; ...
OpenResty(openresty-1.21.4.1.tar.gz) OpenResty是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的...
OpenResty 的高性能和可扩展性使其成为直播服务的不二之选。 体系结构 ---------- OpenResty 的体系结构主要包括前端的 Nginx 服务器、后端的云存储、RTMP 服务器和 CDN 节点。Nginx 服务器负责处理用户的请求和...
《OpenResty最佳实践》这本书籍,旨在向读者介绍OpenResty的使用方法和最佳实践,从而让读者能够充分利用OpenResty进行高效、安全的Web开发。 书籍涵盖了多个知识点,从最基础的Lua脚本语言学习,到OpenResty的高级...
Web 应用服务器,Web 开发人员可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,更主要的是在性能方面,OpenResty可以 快速构造出足以胜任 10K 以上并发连接响应的超高性能 Web 应用系统。 360,UPYUN...
快速,自动的OpenResty分层缓存。 结合 API和,可以将该库作为键/值存储缓存标量Lua类型和表,从而提供了一种性能且灵活的缓存解决方案。 特征: 使用TTL进行缓存和否定缓存。 通过内置的互斥可以防止在缓存未...
heroku-openresty-buildpack-template 这是使用的模板。 对不起,我听到了满嘴的名字,但至少它是确定的。 用法: git clone ...
1. 解压所有文件,例如:`tar -zxvf openssl-1.1.0j.tar.gz`,`tar -zxvf openresty-1.13.6.2.tar.gz`,等等。 2. 配置和编译OpenSSL,根据系统需求设置合适的选项,如`./config --prefix=/usr/local/openssl && ...
lua-resty-waf作为OpenResty生态系统的一部分,为Nginx提供了强大的WAF能力,它的轻量级设计和高效的Lua集成使得它成为保护Web服务安全的理想选择。通过深入理解和定制lua-resty-waf,开发者可以构建出更健壮、更...
OpenResty 是一个通过扩展 nginx 的快速 Web 应用服务器。 Nginx Openresty For Windows (NOW) 是带有 Openresty 的 Windows 版本中的 Nginx。 它有一些特点: 高性能 并发两万多个连接 多进程 支持共享内存 支持...
OpenResty 1.25.3.1的安装通常涉及下载压缩包(openresty-1.25.3.1),然后按照官方文档的步骤进行编译和安装。这可能包括设置环境变量、安装依赖库(如pcre、openssl等)、配置Nginx模块,以及最终的make和make ...