我们重新发布会议报告的文字记录 2016年,去年7月8日至XNUMX日在莫斯科附近的斯科尔科沃举行。 讲述如何使用 OpenResty 和 Lua 扩展 NGINX 功能。
大家好,我叫 Vladimir Protasov,在 Parallels 工作。 我会告诉你一些关于我自己的事情。 我一生四分之三的时间都在写代码。 我成为了一名真正的程序员:我有时会在梦中看到代码。 人生四分之一的时间都花在工业开发上,编写直接投入生产的代码。 你们中有些人使用但不知道的代码。
让你知道情况有多糟糕。 当我还是大三的时候,我进来了,他们给了我这两个 TB 的数据库。 现在它已经为大家带来了高负载。 我去参加会议并问:“伙计们,告诉我,你们有大数据吗?一切都很酷吗? 你们那里有多少个基地? 他们回答我:“我们有100GB!” 我说:“酷啊,100GB!” 我心里想着如何巧妙地挽回扑克脸。 你认为,是的,这些人很酷,然后你回来修改这些多 TB 的数据库。 这就是初级。 你能想象它有多受欢迎吗?
我知道 20 多种编程语言。 这是我在工作中必须弄清楚的。 他们给你用 Erlang、C、C++、Lua、Python、Ruby 或其他语言编写的代码,你必须把它们全部删掉。 一般来说,我不得不这样做。 无法计算出确切的数字,但大约有 20 个数字丢失了。
由于这里的每个人都知道 Parallels 是什么以及我们做什么,所以我不会谈论我们有多酷以及我们做什么。 我只告诉你,我们在全球有13个办事处,300多名员工,发展在莫斯科、塔林和马耳他。 如果你愿意,你可以搬到马耳他,如果冬天很冷,你需要温暖你的背部。
具体来说,我们部门是用Python 2编写的。我们是做生意的,没有时间引入流行的技术,所以我们很痛苦。 我们有 Django,因为它拥有一切,我们把多余的扔掉了。 还有 MySQL、Redis 和 NGINX。 我们还有很多其他很酷的东西。 我们有 MongoDB,我们有兔子跑来跑去,我们只是什么都没有——但它不是我的,而且我不这样做。
OpenResty
我讲述了我自己。 让我们看看今天我要讲什么:
- OpenResty 是什么?它和什么一起吃?
- 当我们拥有 Python、NodeJS、PHP、Go 和其他人人都满意的很酷的东西时,为什么要重新发明轮子呢?
- 以及一些现实生活中的例子。 我不得不把报告删减很多,因为我花了3,5个小时才拿到它,所以例子很少。
OpenResty 是 NGINX。 感谢他,我们拥有了一个成熟的网络服务器,它写得很好,运行速度很快。 我认为我们大多数人在生产中使用 NGINX。 你们都知道他又快又酷。 他们在其中做了很酷的同步 I/O,所以我们不需要像 Python 中 gevent 循环那样循环任何东西。 Gevent 很酷,很酷,但是如果你编写 C 代码并且 gevent 出了问题,你会疯狂地调试它。 我有经验:花了整整两天的时间才弄清楚那里出了什么问题。 如果有人没有在几周前挖掘,发现问题,把它写在互联网上,而谷歌也没有找到它,那么我们就会彻底疯了。
NGINX 已经做了缓存和静态内容。 你不需要担心如何人为地做到这一点,这样你就不会在某个地方放慢速度,这样你就不会在某个地方丢失描述符。 Nginx部署起来非常方便,你不需要考虑拿什么——WSGI、PHP-FPM、Gunicorn、Unicorn。 Nginx 已安装,并交给管理员,他们知道如何使用它。 Nginx 以结构化的方式处理请求。 我稍后会讨论这个问题。 简而言之,他有一个阶段,当他刚刚接受请求时,当他处理时,当他将内容提供给用户时。
Nginx 很酷,但有一个问题:尽管人们在配置中加入了所有这些很酷的功能,但它仍然不够灵活,尽管它可以定制。 这个力量还不够。 所以,淘宝的人,我记得是八年前,就内置了Lua。 他给什么?
- 大小。 它很小。 LuaJIT 提供了大约 100-200 KB 的内存开销和最小的性能开销。
- 速度。 LuaJIT解释器在很多情况下都接近C,在某些情况下它输给了Java,在某些情况下它超过了它。 有一段时间,它被认为是最先进的、最酷的 JIT 编译器。 现在有更酷的,但它们很重,例如相同的V8。 一些 JS 解释器和 Java HotSpot 在某些点上速度更快,但在某些点上仍然会丢失。
- 简单易学。 比如说,如果您有 Perl 代码库并且您不是 Booking,那么您将找不到 Perl 程序员。 因为他们不在,都被带走了,教他们又漫长又困难。 如果你想要程序员做其他事情,他们可能还需要重新培训或找到。 对于 Lua 来说,一切都很简单。 Lua任何一个小学生三天就能学会。 我花了大约两个小时才弄清楚。 两个小时后,我已经在生产环境中编写代码了。 大约一周后,他直接投入生产并离开。
结果,它看起来像这样:

这里有很多。 OpenResty 组装了一堆模块,包括 luash 和引擎。 您已准备好一切 - 已部署并可运行。
Примеры
歌词已经说得够多了,让我们继续看代码。 这是一个小Hello World:

还有什么? 这是发动机位置。 我们不担心,我们不会编写自己的路由,我们不会采用一些现成的路由 - 我们已经在 NGINX 中拥有了它,我们生活得很好,也很懒惰。
content_by_lua_block 是一个块,表示我们正在使用 Lua 脚本提供内容。 我们采用一个引擎变量 remote_addr 并将其滑入 string.format. 这与 sprintf,只有在Lua中,才正确。 我们把它交给客户。
结果,它将如下所示:

但回到现实世界。 在生产环境中,没有人部署 Hello World。 我们的应用程序通常会访问数据库或其他地方,并且大多数时间都会等待响应。

只是坐着等待。 这不太好。 当100.000万用户来的时候,我们就很难了。 因此,我们以一个简单的应用程序为例。 我们会寻找图片,例如猫。 只是我们不只是搜索,我们会扩展关键词,如果用户搜索“小猫”,我们会找到猫、绒毛等等。 首先我们需要获取后端的请求数据。 它看起来像这样:

两行允许您获取 GET 参数,没有任何复杂性。 然后,例如,我们使用常规 SQL 查询从包含关键字和扩展名的表的数据库中获取此信息。 一切都很简单。 它看起来像这样:

我们连接图书馆 resty.mysql,我们已经在套件中了。 我们不需要安装任何东西,一切都准备好了。 指定如何连接并进行 SQL 查询:

这有点可怕,但它确实有效。 这里10是极限。 我们拿出10条记录,我们很懒,我们不想展示更多。 在 SQL 中,我忘记了限制。
然后我们找到所有查询的图像。 我们收集一堆请求并填写一个名为的 Lua 表 reqs,并做 ngx.location.capture_multi.

所有这些请求同时进行,并将答案返回给我们。 运行时间等于最慢的响应时间。 如果我们都在 50 毫秒内回复,并且发送了一百个请求,那么我们将在 50 毫秒内收到响应。
由于我们很懒,不想编写 HTTP 处理和缓存,所以我们会让 NGINX 为我们做所有事情。 正如您所看到的,有一个请求 url/fetch,这是:

我们做简单 proxy_pass,指定缓存位置、如何缓存,一切都对我们有用。
但这还不够,我们还需要把数据交给用户。 最简单的想法是用两行轻松地将所有内容序列化为 JSON。 我们给出 Content-Type,我们给出 JSON。
但有一个困难:用户不想读取 JSON。 我们需要吸引前端开发人员。 有时我们一开始并不想这样做。 是的,SEO 专家会说,如果我们正在寻找图片,那么他们不在乎。 如果我们给他们一些内容,他们会说我们的搜索引擎不会索引任何内容。
该怎么办? 当然,我们会给用户 HTML。 使用句柄生成并不常见,因此我们想使用模板。 有一个库可以解决这个问题 lua-resty-template.

您一定见过“OPM”这三个可怕的字母。 OpenResty 带有自己的包管理器,通过它您可以安装一堆不同的模块,特别是, lua-resty-template。 它是一个类似于 Django 模板的简单模板引擎。 您可以在那里编写代码并进行变量替换。
结果,一切都会看起来像这样:

我们获取数据并用两行再次渲染模板。 用户很高兴,得到了猫。 由于我们扩大了要求,他还收到了一只小海狗。 你永远不知道,也许他正在寻找它,但他无法正确地表达他的要求。
一切都很酷,但我们正在开发中,我们还不想向用户展示。 我们来做一下授权吧。 为此,我们来看看 NGINX 如何根据 OpenResty 处理请求:
- 第一阶段 - ACCESS,当用户刚来的时候,我们通过标头、IP 地址、其他数据来查看他。 如果我们不喜欢你可以立即把它砍掉。 这可以用于授权,或者如果我们收到很多请求,我们可以在这个阶段轻松地砍掉它们。
- 改写。 重写一些请求数据。
- 内容。 我们向用户提供内容。
- 标头过滤器。 更改响应标头。 如果我们使用
proxy_pass,我们可以在将其提供给用户之前重写一些标头。 - 身体过滤器。 我们可以改变身体。
- 日志 - 日志记录。 无需额外层即可在elasticsearch中写入日志。
我们的授权将如下所示:

我们将其添加到其中 location,我们之前描述过,并将以下代码放在那里:

我们查看是否有 cookie 令牌。 如果没有,那么我们就进行授权。 用户很狡猾,可能会猜测需要设置 cookie 令牌。 因此,我们也将其放入Redis中:

使用 Redis 的代码非常简单,与其他语言没有什么不同。 同时,所有的输入/输出,那里有什么,这里有什么,都不是阻塞的。 如果您编写同步代码,那么它就会异步工作。 就像 gevent 一样,只是做得好。

让我们自己进行授权:

我们说我们需要读取请求正文。 我们收到 POST 参数,检查登录名和密码是否正确。 如果不正确,那么我们会进行授权。 如果它们是正确的,那么我们将令牌写入 Redis:

不要忘记设置 cookie,这也是通过两行完成的:

这个例子很简单,是推测性的。 当然,我们不会提供向人们展示猫的服务。 但谁认识我们呢。 那么让我们看一下在生产中可以做什么。
- 极简后端。 有时我们需要向后端提供相当多的数据:在某个地方我们需要替换日期,在某个地方我们需要显示某种列表,比如现在有多少用户在网站上,拧上计数器或统计数据。 这么小的东西。 一些最小的部件可以很容易地制作。 这将是快速、简单且出色的。
- 数据预处理。 有时我们想在页面中嵌入广告,并通过 API 请求获取这些广告。 这在这里很容易做到。 我们不加载已经在努力工作的后端。 您可以在这里提货和收集。 我们可以塑造一些 JS,或者相反,在将某些东西提供给用户之前取消粘连、预处理。
- 微服务的外观。 这也是一个非常好的案例,我实现了。 在此之前,我曾在 Tenzor 工作,这是一家电子报告公司,为该国大约一半的法人实体提供报告。 我们做了一个服务,很多事情都是使用相同的机制完成的:路由、授权等等。
OpenResty 可以用作微服务的粘合剂,提供对所有内容的单一访问和单一接口。 由于微服务可以这样编写:这里有 Node.js,这里有 PHP,这里有 Python,这里有一些 Erlang 的东西,我们知道我们不想到处重写相同的代码。 因此,OpenResty可以插在前面。 - 统计和分析。 通常NGINX在入口处,所有请求都经过它。 就是在这个地方,采集起来非常方便。 你可以立即计算出一些东西并将其扔到某个地方,例如相同的Elasticsearch、Logstash,或者只是将其写入日志然后将其发送到某个地方。
- 多用户系统。 比如网络游戏也很好做。 今天,Alexander Gladysh 将在开普敦告诉您如何使用 OpenResty 快速制作多人游戏原型。
- 请求过滤(WAF)。 现在制作各种Web应用程序防火墙很流行,有很多服务提供它们。 借助 OpenResty,您可以为自己打造一个 Web 应用程序防火墙,它将根据您的要求简单轻松地过滤请求。 如果您有 Python,那么您就会明白 PHP 绝对不会注入给您,当然,除非您从控制台的任何地方生成它。 你知道你有 MySQL 和 Python。 也许,他们可以在这里尝试进行某种目录遍历并向数据库中注入一些内容。 因此,您可以在前面快速且廉价地过滤掉愚蠢的请求。
- 社区。 由于 OpenResty 是基于 NGINX 的,所以它有一个好处——这就是 NGINX 社区。 它非常大,NGINX 社区已经解答了您一开始遇到的很多问题。
Lua开发者。 昨天我和参加 HighLoad++ 培训日的人交谈,听说只有 Tarantool 是用 Lua 编写的。 事实并非如此,很多东西都是用Lua写的。 示例:OpenResty、Prosody XMPP 服务器、Love2D 游戏引擎、魔兽争霸和其他地方编写的 Lua 脚本。 Lua 开发者很多,他们有一个庞大且反应灵敏的社区。 我所有的 Lua 问题都在几个小时内得到了解答。 当你写信给邮件列表时,几分钟之内就已经有一堆答案,它们描述了什么、如何、什么是什么。 这很棒。 不幸的是,这样善良真诚的社区并不是到处都有。
OpenResty 有 GitHub,如果出现问题,您可以在其中提出问题。 Google 网上论坛上有一个邮件列表,您可以在其中讨论一般问题,也有一个中文邮件列表 - 你永远不知道,也许你不会说英语,但你懂中文。
结果
- 我希望我能够传达 OpenResty 是一个非常方便的 Web 框架。
- 它的入门门槛较低,由于代码与我们编写的类似,因此语言相当简单和简约。
- 它提供了没有回调的异步 I/O,我们不会像有时在 NodeJS 中编写的那样有面条。
- 它的部署很简单,因为我们只需要 NGINX 以及正确的模块和我们的代码,一切都会立即生效。
- 大型且反应灵敏的社区。
我没有详细讲路由是怎么做的,原来是一个很长的故事。
谢谢你!

来源: habr.com
