luodw.cc - 阅读全文 »









Search Preview

Lua原来这么好用 | 罗道文的私房菜

luodw.cc
今天这篇文章就随意谈谈lua。最近在看nginx+lua,刚好学了lua,就被这门语言的简练给吸引了。lua语言可以大大减轻一个人的心智负担,除了变量,操作符,语句以及函数这些最基础的语言功能外,剩下最重要的就是数据结构table(数组本质上也是table的特例)了。不用去记那么多的语法以及黑魔法,简直大快人心。 我现在对Lua使用感兴趣部分就是nginx+lua和redis内嵌lua;特别是前者
.cc > luodw.cc

SEO audit: Content analysis

Language Error! No language localisation is found.
Title Lua原来这么好用 | 罗道文的私房菜
Text / HTML ratio 45 %
Frame Excellent! The website does not use iFrame solutions.
Flash Excellent! The website does not have any flash contents.
Keywords cloud = local
end return sock err sex args SCRIPT ngxsayk age headers boy girl redis ARGV curl function KEYS
Keywords consistency
Keyword Content Title Description Headings
= 31
local 25

23
end 17
return 9
sock 6
Headings
H1 H2 H3 H4 H5 H6
5 0 0 0 0 0
Images We found 2 images on this web page.

SEO Keywords (Single)

Keyword Occurrence Density
= 31 1.55 %
local 25 1.25 %

23 1.15 %
end 17 0.85 %
return 9 0.45 %
sock 6 0.30 %
err 6 0.30 %
sex 6 0.30 %
args 6 0.30 %
SCRIPT 5 0.25 %
ngxsayk 5 0.25 %
age 5 0.25 %
headers 5 0.25 %
boy 4 0.20 %
girl 4 0.20 %
redis 4 0.20 %
ARGV 4 0.20 %
curl 4 0.20 %
function 4 0.20 %
KEYS 3 0.15 %

SEO Keywords (Two Word)

Keyword Occurrence Density
2 3 13 0.65 %
1 2 13 0.65 %
3 4 12 0.60 %
4 5 12 0.60 %
5 6 11 0.55 %
6 7 9 0.45 %
12 13 8 0.40 %
8 9 8 0.40 %
9 10 8 0.40 %
11 12 8 0.40 %
10 11 8 0.40 %
7 8 8 0.40 %
18 19 7 0.35 %
17 18 7 0.35 %
15 16 7 0.35 %
14 15 7 0.35 %
13 14 7 0.35 %
16 17 7 0.35 %
if not 5 0.25 %
19 20 5 0.25 %

SEO Keywords (Three Word)

Keyword Occurrence Density Possible Spam
1 2 3 13 0.65 % No
3 4 5 12 0.60 % No
2 3 4 12 0.60 % No
4 5 6 11 0.55 % No
5 6 7 9 0.45 % No
11 12 13 8 0.40 % No
10 11 12 8 0.40 % No
9 10 11 8 0.40 % No
8 9 10 8 0.40 % No
6 7 8 8 0.40 % No
7 8 9 8 0.40 % No
15 16 17 7 0.35 % No
14 15 16 7 0.35 % No
12 13 14 7 0.35 % No
16 17 18 7 0.35 % No
17 18 19 7 0.35 % No
13 14 15 7 0.35 % No
18 19 20 5 0.25 % No
sex 2 boy 4 0.20 % No
21 22 23 4 0.20 % No

SEO Keywords (Four Word)

Keyword Occurrence Density Possible Spam
2 3 4 5 12 0.60 % No
1 2 3 4 12 0.60 % No
3 4 5 6 11 0.55 % No
4 5 6 7 9 0.45 % No
9 10 11 12 8 0.40 % No
5 6 7 8 8 0.40 % No
6 7 8 9 8 0.40 % No
7 8 9 10 8 0.40 % No
8 9 10 11 8 0.40 % No
10 11 12 13 8 0.40 % No
15 16 17 18 7 0.35 % No
16 17 18 19 7 0.35 % No
11 12 13 14 7 0.35 % No
12 13 14 15 7 0.35 % No
13 14 15 16 7 0.35 % No
14 15 16 17 7 0.35 % No
17 18 19 20 5 0.25 % No
sex 2 boy 3 4 0.20 % No
1 sex 2 boy 4 0.20 % No
23 24 25 26 4 0.20 % No

Internal links in - luodw.cc

归档
归档 | 罗道文的私房菜
书签
index | 罗道文的私房菜
关于
关于我 | 罗道文的私房菜
kafka
分类: kafka | 罗道文的私房菜
阅读全文 »
kafka源码分析之Producer | 罗道文的私房菜
阅读全文 »
kafka源码分析之概述 | 罗道文的私房菜
nsq
分类: nsq | 罗道文的私房菜
阅读全文 »
nsqd执行解析 | 罗道文的私房菜
lua
分类: lua | 罗道文的私房菜
阅读全文 »
Lua原来这么好用 | 罗道文的私房菜
2
罗道文的私房菜
22
罗道文的私房菜
RSS
罗道文的私房菜

Luodw.cc Spined HTML


Lua原来这么好用 | 罗道文的私房菜 罗道文的私房菜 分享知识,分享快乐 首页 归档 阅读 书签 关于 搜索 Lua原来这么好用 发表于 2017-03-24   |   分类于 lua   |   阅读次数 今天这篇文章就随意谈谈lua。最近在看nginx+lua,刚好学了lua,就被这门语言的简练给吸引了。lua语言可以大大减轻一个人的心智负担,除了变量,操作符,语句以及函数这些最基础的语言功能外,剩下最重要的就是数据结构table(数组本质上也是table的特例)了。不用去记那么多的语法以及黑魔法,简直大快人心。 我现在对Lua使用感兴趣部分就是nginx+lua和redis内嵌lua;特别是前者,nginx+lua+redis构建高性能应用。lua+nginx可以使nginx不需要重新编译的情况下添加功能,特别是,如果只是修改lua文件,nginx都不需要重新加载配置文件,极大便利了nginx开发;而redis内嵌lua,让redis服务器有了计算能力,而且减小带宽传输(多个命令)以及原子执行多个命令达到cas效果。 下面就说说最近的一些内容和心得,主要以下三部分: 通过lua脚本操作redis; nginx+lua示例; lua面向对象实现; 总结; 【版权声明】博客内容由罗道文的私房菜拥有版权,允许转载,但请标明原文链接http://luodw.cc/2017/03/24/lua/#more 通过lua脚本操作redis 之前在学习redis以及看源码时,由于不懂lua,所以redis内嵌Lua模块就跳过去了,如今学了Lua,因此想把这块知识补上。redis对Lua的支持,主要有以下以下7个命令, EVAL EVALSHA SCRIPT DEBUG SCRIPT EXISTS SCRIPT FLUSH SCRIPT KILL SCRIPT LOAD 具体这些命令怎么使用,以及实现原理如何,可以看链接Lua脚本-Redis设计与实现和redis官网。这里主要介绍下EVAL命令。 EVAL命令格式为 EVAL script numkeys key [key ...] arg [arg ...] 这里的 script即为lua脚本或lua脚本文件; key一般指lua脚本操作的键,在lua脚本文件中,通过KEYS[i]获取; arg指外部传递给lua脚本的参数,可以通过ARGV[i]获取; 下面通过两个简单的示例展示eval命令的用法。首先是脚本语句,来自redis官网,如下 12345127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second1) "key1"2) "key2"3) "first"4) "second" 这个示例中lua脚本为一个return语句,返回了lua一个数组,这个数组四个元素分别是通过外部传入lua脚本。因为redis内嵌了Lua虚拟机,因此redis接收到这个lua脚本之后,然后交给lua虚拟机执行。当lua虚拟机执行结束,即将执行结果返回给redis,redis将结果按自己的协议转换为返回给客户端的回复,最后再通过TCP将回复发回给客户端。 通过这个例子也可以看出,在lua脚本中可以通过KEYS[i]来获取外部传入的键值,通过ARGV[i]来获取外部传入的参数。 下面给出一个复杂点的lua脚本,在给出脚本之前,先看下redis中有哪些需要的数据 12345678910111213141516171819127.0.0.1:6379> zrange people 0 31) "jom"2) "lily"3) "tom"127.0.0.1:6379> hgetall jom1) "sex"2) "boy"3) "age"4) "12"127.0.0.1:6379> hgetall lily1) "age"2) "15"3) "sex"4) "girl"127.0.0.1:6379> hgetall tom1) "sex"2) "boy"3) "girl"4) "13" 下面lua脚本的作用就是通过一次网络请求获取'jom','lily','tom'三个人的个人信息 1234567891011121314151617181920212223242526--[[--Keys[1] is the key--argv[1] is the offset--argv[2] is the limit--]]-- 获取外部传入的命令local key,offset,limit = KEYS[1], ARGV[1], ARGV[2]-- 通过ZRANGE获取键为key的有序集合元素,偏移量为offset,个数为limit,即所有人名字local names = redis.call('ZRANGE', key, offset, limit)-- infos存储所有个人信息local infos = {}-- 遍历所有名字for i=1,#names do local ck = names[i] -- 通过HGETALL命令获取每个人的个人信息 local info = redis.call('HGETALL',ck) -- 并且在个人信息中插入姓名 table.insert(info,'name') table.insert(info,names[i]) -- 插入infos中 infos[i] = infoend-- 将结果返回给redisreturn infos 在命令行上执行eval命令,即可以得到结果,如下: 12345678910111213141516171819$ ../redis/src/redis-cli --eval redlua.lua people , 0 21) 1) "sex" 2) "boy" 3) "age" 4) "12" 5) "name" 6) "jom"2) 1) "age" 2) "15" 3) "sex" 4) "girl" 5) "name" 6) "lily"3) 1) "sex" 2) "boy" 3) "girl" 4) "13" 5) "name" 6) "tom" 试想,如果没有使用lua脚本,那么上述功能需要在应用程序中先发送ZRANGE命令,获取所有姓名;获取到所有姓名之后,在遍历所有姓名,针对每个姓名访问redis获取给人信息,至少四次网络IO。而管道在这种场景上也派不上用场。由此可知,lua脚本在这种场景下提高redis性能是有多大的帮助了。 需要特别注意的是people(键)后面的逗号左右都要留空格,不然会报错! ngx+lua示例 ngx+lua是我最近非常感兴趣的一门技术,因为在带来高性能的同时,并没有提高编程的复杂性。如果是用c语言开发nginx第三方模块,需要了解nginx内部的数据结构以及接口,难度和心智上都有挑战。本小节也是通过简单的ngx+lua示例来展示在lua是如何在nginx中使用的。 开发环境可以通过极客学院教程安装。即安装OpenResty框架。安装的目录在/usr/servers,测试目录在/usr/example。 第一个示例展示如何获在lua脚本中获取http请求所有信息,这个示例来自开涛的博客,可以很好理解在lua中如何获取nginx请求信息。在example.conf中添加如下路径映射 1234567location ~/lua_test/(\d+)/(\d+)$ { set $a $1; set $b $host; default_type 'text/html'; lua_code_cache on; content_by_lua_file /usr/example/lua/lua_test.lua;} 然后在/usr/example/lua/文件下新建lua_test.lua文件,复制如下代码 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657-- nginx变量local var = ngx.varngx.say("ngx.var.a :",var.a,"<br/>")ngx.say("ngx.var.b :",var.b,"<br/>")ngx.say("ngx.var[2] :",var[2],"<br/>")-- 请求头local headers = ngx.req.get_headers()ngx.say("headers begin", "<br/>")ngx.say("Host : ", headers["Host"], "<br/>")ngx.say("user-agent : ", headers["user-agent"], "<br/>")for k,v in pairs(headers) do if type(v) == "table" then ngx.say(k, " : ", table.concat(v, ","), "<br/>") else ngx.say(k, " : ", v, "<br/>") endendngx.say("headers end", "<br/>")ngx.say("<br/>")--get请求uri参数 ngx.say("uri args begin", "<br/>")local uri_args = ngx.req.get_uri_args()for k,v in pairs(uri_args) do if type(v) == "table" then ngx_say(k, " : ", table.concat(v, ". "),"<br/>") else ngx.say(k, ": ", v, "<br/>") endend--post请求参数 ngx.req.read_body()ngx.say("post args begin", "<br/>")local post_args = ngx.req.get_post_args()for k, v in pairs(post_args) do if type(v) == "table" then ngx.say(k, " : ", table.concat(v, ", "), "<br/>") else ngx.say(k, ": ", v, "<br/>") endendngx.say("post args end", "<br/>")ngx.say("<br/>")--请求的http协议版本 ngx.say("ngx.req.http_version : ", ngx.req.http_version(), "<br/>")--请求方法 ngx.say("ngx.req.get_method : ", ngx.req.get_method(), "<br/>")--原始的请求头内容 ngx.say("ngx.req.raw_header : ", ngx.req.raw_header(), "<br/>")--请求的body内容体 ngx.say("ngx.req.get_body_data() : ", ngx.req.get_body_data(), "<br/>")ngx.say("<br/>") 由代码可以看出,lua脚本中主要信息来自于ngx.var和ngx.req这两个属性。在执行程序之前,先nginx需要先reload一次,最后我们就可以通过curl命令来访问nginx 123456789101112131415161718192021222324252627282930313233$curl -XPOST -d 'name=json&age=26' 'http://127.0.0.1:80/lua_test/1/2?token=ds43klk54fd'ngx.var.a :1<br/>ngx.var.b :127.0.0.1<br/>ngx.var[2] :2<br/>headers begin<br/>Host : 127.0.0.1<br/>user-agent : curl/7.35.0<br/>host : 127.0.0.1<br/>content-type : application/x-www-form-urlencoded<br/>accept : */*<br/>content-length : 16<br/>user-agent : curl/7.35.0<br/>headers end<br/><br/>uri args begin<br/>token: ds43klk54fd<br/>post args begin<br/>age: 26<br/>name: json<br/>post args end<br/><br/>ngx.req.http_version : 1.1<br/>ngx.req.get_method : POST<br/>ngx.req.raw_header : POST /lua_test/1/2?token=ds43klk54fd HTTP/1.1User-Agent: curl/7.35.0Host: 127.0.0.1Accept: */*Content-Length: 16Content-Type: application/x-www-form-urlencoded<br/>ngx.req.get_body_data() : name=json&age=26<br/><br/> 通过代码和结果一一比对,可以看出已经正确获取请求信息。我觉得这是一个很好的入门示例,因为处理http请求,获取http请求头信息是非常有必要的,而上述例子则展示接口信息。 下面另一个示例是展示ngx+lua存取redis。同样在example.conf添加路径映射 123456 location ~/redis_test/(\d+)$ { default_type 'text/html'; charset utf-8; lua_code_cache on; content_by_lua_file /usr/example/lua/redis.lua;} 然后在/usr/example/lua/文件夹下新建redis.lua,复制如下代码: 123456789101112131415161718192021222324252627282930local json = require("cjson")local redis = require("resty.redis")local red = redis:new()red:set_timeout(1000)local ip = "127.0.0.1"local port = 6379local ok, err = red:connect(ip, port)if not ok then ngx.say("connect to redis error : ", err) return ngx.exit(500)endlocal id = ngx.var[1]local value = "value-"..idred:set(id,value)local resp, err = red:get(id)if not resp then ngx.say("get from redis error : ", err) return ngx.exit(500)endred:close()ngx.say(json.encode({content=resp})) OpenResty将常用的lua包都已经打包好,包括redis客户端,memcache客户端,mysql客户端以及cjson等等,非常方便ngx+lua的开发。在执行程序之前,先reload一次配置文件,执行结果如下: 123456$ flourish http://127.0.0.1:80/redis_test/1{"content":"value-1"}$ flourish http://127.0.0.1:80/redis_test/2{"content":"value-2"}$ flourish http://127.0.0.1:80/redis_test/3{"content":"value-3"} 最后可以通过redis-cli客户端验证上述三个键值对是否插入成功。由这个例子可以展开讨论,如下 因为OpenResty提供了redis,mysql以及模板渲染,因此利用ngx+lua可以直接部署一个小型网站; 可以在nginx本地搭一个redis缓存;当数据请求时,先访问本地的redis缓存,如果redis缓存需要的数据,直接直接在nginx层返回;如果redis没有缓存需要的数据,则转向web服务器。这样可以减轻后端服务器和数据库的压力,以及缩短请求时间。 lua面向对象实现 lua没有提供面像对象的实现,但是可以通过table来模拟。所以可知table在lua中是多么核心的数据结构。这里,我用redis客户端来说明lua是如何模拟面像对象的实现。 1234567891011121314151617181920----引入table.new函数,兼容版本恩体local ok, new_tab = pcall(require, "table.new")if not ok or type(new_tab) ~= "function" then----如果还没有table.new接口,则自定义一个函数,返回一个空表格 new_tab = function (narr, nrec) return {} endend-------------------------------------------------------新建一个表格 local _M = new_tab(0, 155) _M._VERSION = '0.24'-----------------------------------------------------local mt = { __index = _M }function _M.new(self) local sock, err = tcp() if not sock then return nil, err end --设置返回表的元表 return setmetatable({ sock = sock }, mt)end 上述new方法即相当于类的构造函数,因此,我们可以通过如下方式新建一个对象 123-----此时require返回的redis即为模块中的_Mlocal redis = require("resty.redis")local red = redis:new() 通过redis:new()方法返回一个{sock=sock}表格,然后设置这个表格的元表为resty.redis模块中的_M表格;__index元表格的意思就是当在red中访问一个不存在的属性时,则可以到元表中查询;因此当red调用下述函数时: 12345678910111213red:connect('127.0.0.1',9999)--------------------------------------------------function _M.connect(self, ...)----如果是red调用改方法,则self即为red local sock = self.sock if not sock then return nil, "not initialized" end self.subscribed = nil return sock:connect(...)end 因为red中并不存在connect方法,因此red到它的_M中查找connect方法,并执行;其实_M和red在lua中是两个不同的对象,就是因为red的元表为_M,因此可以把_M当作是对象red的模板,也就是类的概念。 总结 本文主要介绍了lua在redis和nginx的应用,以及lua面向对象的实现。多了解一些编程语言,可以更好对编程语言的理解。后面还是深入再看看ngx+lua的开发。 #lua Nginx是这么运行的 nsqd执行解析 文章目录 站点概览 罗道文 分享知识,分享快乐 106 日志 33 分类 116 标签 RSS github weibo zhihu Links 赖明星的博客地址 蔡珉星的博客地址 阮榕城的博客地址 陈友兵的博客地址 郑江龙的博客地址 厦门大学数据库实验室 1. 通过lua脚本操作redis2. ngx+lua示例3. lua面向对象实现4. 总结 © 2017 罗道文 由 Hexo 强力驱动 本站总访问量次 | 主题 - NexT.Pisces