OpenResty学习笔记:使用 Lapis 框架开发 Web 应用

不知道从哪一届开始,老罗开锤子手机发布会的时候,会当场捐赠一款开源软件,第一次是捐赠给 OpenSSL,第二次是 。那次发布会开完, 在程序员圈子里火了一把,成为一种热门技术了。其实, 在后端程序员世界里,早就享有盛名。当你不得不去面对高并发的场景的时候,你一定会发现 Nginx 的巧妙和强大,然后你会想到用 Nginx 解决问题,然后你一定会发现

三年前就想学 OpenResty,一直被拖延症耽搁了。这期间,OpenResty 发展很快,无论是自身还是社区都进步很快,甚至也有了配套的 awesome-resty 项目。我下定决心这次好好学学。

因为我是 开发,所以自然最关心的还是 框架。之前就了解过新浪出的 Vanilla,但是我看了一点 语法,不是很喜欢,更不要提用其实现的 框架了,好在我碰巧点开了 Lapis 的官网,发现还有一种语言叫 MoonScript,简洁优雅,真是 Lua 的绝佳替代。

安装 OpenResty

目前最新版本的 OpenResty 是 v1.11.2.4 发布于 2017/07/31 真是新鲜得发烫。一般,我使用 Debian 作为自己使用的操作系统环境,有比较方便的包管理器和比较稳定的服务器环境。Ubuntu 也是不错的选择。以下,我都会使用 Debian(jessie)。首先安装依赖的软件包,然后编译安装 OpenResty。

以上的命令会把 OpenResty 安装到默认目录 /usr/local/openresty,也可以用 ./configure --help 来查询编译选项,改变安装的路径。里面自带了 LuaJIT,在 /usr/local/openresty/luajit/bin 下有个 luajit 的二进制。执行 luajit 命令,会进入 Lua 的交互式命令提示符,使用下面的命令可以查询 Lua 的版本 print(_VERSION) Lua 的各个版本,语法不完全兼容,后续默认的讲的都是 Lua 的 5.1 版本。

安装 LuaRocks

Python 有 pip,PHP 有 Composer,Lua 有 LuaRocks,还真是酷呢。LuaRocks 在 Linux 环境默认只有编译安装的方式,依赖本机的 Lua 环境。虽然 OpenResty 里面带有了 LuaJIT,但是我试了一下,并不能简单代替原生 Lua 环境。所以,还是建议再安装 Lua 环境,避免踩坑和麻烦。

到这里,luarocks 就安装完毕了。

安装 Lapis

有了 LuaRocks,安装 Lapis 会非常简单,如下单个命令就能完成安装,一共会安装 12 个软件包,一个是 Lapis 自己,以及其依赖。

实现一个简单的 Demo

为了学习一个 Web 框架,我打算用这个框架开发一款最简单的应用。这里要用到框架的一些基础特性,比如 MVC,ORM,缓存等等。想了想,我要做一个手机号归属地查询的服务。以下是需求的细化:

需求:手机号归属地查询
功能:
1. 一个查询页面,上面一个查询框和一个按钮,输入手机号码,点击查询,显示出手机号归属地和运营商;
2. 这个功能需要提供 API 的版本,传入一个手机号,返回一个 JSON 格式的归属地和运营商标识;

非公能性需求:
1. 因为可能有巨量的查询,应该有比较好的性能,所以考虑把结果缓存起来;

首先,我们来建立一个最简单的项目,先建立一个项目文件夹叫 mobile-search,然后在该目录下,用 lapis 命令创建项目基础文件:

这样项目脚手架就做出来了。执行lapis server,nginx 就会启动了。然后,我们访问 http://localhost:8080,然后,你可能会遇到 Internal Server Error 500。原因是找不到 app.lua,Lapis 框架同时支持两种语言,Lua 和 MoonScript,后者是前者的语法的一种美化和简化,相当于 CoffeeScript 之于 JavaScript。在实际执行的时候,MoonScript 需要被编译成 Lua 才能工作。Lapis 默认是使用 MoonScript 作为原生语言的,所以,我们刚生成的是使用 MoonScript 语言的模板,没有执行编译的过程,所以,app.lua 还没有编译出来。

同样使用 LuaRocks 我们来安装 MoonScript 的软件包,然后使用编译器编译 MoonScropt:

之后再次执行 lapis server 你会看到,Welcome to Lapis 1.6.0!,可以开工了。

数据库连接

首先,我们来看看我们要做的 Demo 的表结构。我去淘宝上 2 块钱买了一个用于查询手机号码归属地的数据库,用来完成这次的 Demo。以下是这个数据的表结构:

手机号码归属地查询的原理不难理解,就是使用手机号码的前 7 位数字(phone_header),查询对应的条目,查到后,展示province 字段和 city 字段的值,也可以显示当地的区号和邮编。

首先,我们来创建一个配置文件,进入到 mobile-search 目录,创建一个名为 config.moon 的文件:

然后,我们可以测试一下,配置文件是否有效。我们打开 app.moon 来编辑一下:

然后,我们访问 http://localhost:8080 ,又看到了:Welcome to Lapis 1.6.0! 并且,我们在 shell 的 log 里看到:

这些迹象表明,我们的数据库连接已经通了。我们把代码优化一下:

改完,我们刷新页面,发现了乱码,把编码格式改成 utf-8 后,我们看到:

其实,到这里,我们的第一个需求,竟然就已经实现了(从本质上看)。剩下的,就是美化的工作了。

使用 Model

Lapis 框架是提供 Model 这个抽象形式的。跟一般的 Web 框架区别也是不大的,一个 Model 代表着一张数据库的表,一个实例代表着数据库表里的一行记录。

定义一个 Model 最简单的办法是使用约定。数据库表用下划线分割的小写单词的话,Model 名字用对应的驼峰命名法。

app.moon 代码经过改写后,变成上面的样子。表名是 phonenumber_info,则 Model 名为 PhonenumberInfo。参见这个表的建表语句,我们看到这个表的主键不是缺省的 id 字段,所以,用 @primary_key 指定主键的字段。

Lapis 的 Model 封装了一系列的单行变量的操作方法。insert,select,update,delete等等,也实现了 relation 等 ORM 层常见的功能。

View 的使用

我们再来看看,视图是怎么使用的,在 Lapis 里面,支持 etlua 的一个模板库。只要开启模板库,就可以使用 render 方法来输出内容。

我们在 mobile-search 目录下,创建一个目录叫 views,这也是一种约定。在里面放一个模板文件,叫 result.etlua

写入如上的内容。

然后,我们把 app.moon 改成:

经过上面的方法,我们把复杂的 HTML 给挪到了代码外面做到了逻辑和表现分离。

项目代码组织

经过上面的一些改造,基本体验到了 MVC 中的 M 和 V,但是没有 C,就我自己目前学习的知识看,这个框架里是没有C这个抽象的,本质上就是 Application,这个框架的抽象认为,一个网站,就是由一系列的 Request 和 Action 组成的。Request 通过 URL 的 path 映射到 Action,Application 就是用来组织这种映射的地方。那么复杂的业务逻辑怎么组织呢,可以放到 Application 的子类里面,切小后,放到子目录里。

让我们来试试。

创建一个目录叫 applications ,然后,在里面创建一个文件叫 search.moon 用来把逻辑组织在一个单独文件,然后分出去,然后再建个目录叫 models,把刚才的 Model 也挪出去。

目录布局就是上面这个样子。

压测

还是很好奇这个东西的性能的吧,哈哈哈

对比一下同一台机器上的 PHP + Apache

结果,才跑完2000,我有点舍不得跑了,风扇那个吹啊,改成2并发,100请求:

注意,上面压测,完全胡乱玩的,请勿胡乱参考。对 Lapis 的压测是,Debian 8 虚拟机,1核1G,OpenResty 1.11.2.4 + Lapis 1.6.0,压得时候还开着调试模式,而 PHP 呢,是 Debian 的宿主机,iMac,i7 4GHz 4核16G,Apache 2.x + PHP_Module 7.1.6,完全不对等的配置,也未做调优等等。主要是我心里也知道,一个是纯异步通信,一个是同步的,这个根本就没有太大比的必要,PHP真要认真测,应该也没这么烂,主要我写得辛苦,也懒得花时间去努力测试了,诸君谅解。真想参考性能,请看这个

发表评论

电子邮件地址不会被公开。 必填项已用*标注

CAPTCHA
Change the CAPTCHA codeSpeak the CAPTCHA code