zoukankan      html  css  js  c++  java
  • 高并发 Nginx+Lua OpenResty系列——Lua模版渲染

    https://www.cnblogs.com/babycomeon/p/11109494.html

     

    模版渲染

    动态web网页开发是Web开发中一个常见的场景,比如像京东商品详情页,其页面逻辑是非常复杂的,需要使用模板技术来实现。而Lua中也有许多模板引擎,如目前京东在使用的lua-resty-template,可以渲染很复杂的页面,借助LuaJIT其性能也是可以接受的。


    如果学习过JavaEE中的servlet和JSP的话,应该知道JSP模板最终会被翻译成Servlet来执行;而lua-resty-template模板引擎可以认为是JSP,其最终会被翻译成Lua代码,然后通过ngx.print输出。


    而lua-resty-template和大多数模板引擎是类似的,大体内容有:
    模板位置:从哪里查找模板;
    变量输出/转义:变量值输出;
    代码片段:执行代码片段,完成如if/else、for等复杂逻辑,调用对象函数/方法;
    注释:解释代码片段含义;
    include:包含另一个模板片段;
    其他:lua-resty-template还提供了不需要解析片段、简单布局、可复用的代码块、宏指令等支持。

    下载lua-resty-template

    wget https://github.com/bungle/lua-resty-template/archive/v1.9.tar.gz
    tar -xvzf v1.9.tar.gz
    

    解压后可以看到lib/resty下面有一个template.lua,这个就是我们所需要的,在template目录中还有两个lua文件,将这两个文件复制到/usr/openResty/lualib/resty中即可。
    接下来就可以通过如下代码片段引用了:

    local template = require("resty.template")
    

    模版位置

    我们需要告诉lua-resty-template去哪儿加载我们的模块,此处可以通过set指令定义template_location、template_root或者从root指令定义的位置加载。
    我们可以在openResty.conf配置文件的server部分定义

    # first match ngx location
    set $template_location "/templates";
    # then match root read file
    set $template_root "/usr/openResty/templates";
    

    也可以通过在server部分定义root指令

    root /usr/openResty/templates;
    

    其顺序是

    local function load_ngx(path)
        local file, location = path, ngx_var.template_location
        if file:sub(1)  == "/" then file = file:sub(2) end
        if location and location ~= "" then
            if location:sub(-1) == "/" then location = location:sub(1, -2) end
            local res = ngx_capture(location .. '/' .. file)
            if res.status == 200 then return res.body end
        end
        local root = ngx_var.template_root or ngx_var.document_root
        if root:sub(-1) == "/" then root = root:sub(1, -2) end
        return read_file(root .. "/" .. file) or path
    end
    
    1. 通过ngx.location.capture从template_location查找,如果找到(状态为为200)则使用该内容作为模板;此种方式是一种动态获取模板方式;
    2. 如果定义了template_root,则从该位置通过读取文件的方式加载模板;
    3. 如果没有定义template_root,则默认从root指令定义的document_root处加载模板。


      此处建议首先template_root,如果实在有问题再使用template_location,尽量不要通过root指令定义的document_root加载,因为其本身的含义不是给本模板引擎使用的。


      接下来定义模板位置
    mkdir /usr/openResty/templates
    mkdir /usr/openResty/templates2
    

    openResty.conf配置文件

    # first match ngx location
    set $template_location "/templates";
    # then match root read file
    set $template_root "/usr/openResty/templates";
    
    location /templates {
        internal;
        alias /usr/openResty/templates2;
    }
    

    首先查找/usr/openResty/template2,找不到会查找/usr/openResty/templates。
    然后创建两个模板文件

    vi /usr/openResty/templates2/t1.html
    

    内容为

    template2
    
    vi /usr/example/templates/t1.html
    

    内容为

     template1
    

    test_temlate_1.lua

    local template = require("resty.template")
    template.render("t1.html")
    

    openResty.conf配置文件

    location /lua_template_1 {
        default_type 'text/html';
        lua_code_cache on;
        content_by_lua_file /usr/openResty/lua/test_template_1.lua;
    }
    

    访问如http://127.0.0.1/lua_template_1将看到template2输出。然后rm /usr/openResty/templates2/t1.html,reload nginx将看到template1输出。
    接下来的测试我们会把模板文件都放到/usr/openResty/templates下。

    API

    使用模板引擎目的就是输出响应内容;主要用法两种:直接通过ngx.print输出或者得到模板渲染之后的内容按照想要的规则输出。

    test_template_2.lua

    local template = require("resty.template")
    --是否缓存解析后的模板,默认true
    template.caching(true)
    --渲染模板需要的上下文(数据)
    local context = {title = "title"}
    --渲染模板
    template.render("t1.html", context)
    
    ngx.say("<br/>")
    --编译得到一个lua函数
    local func = template.compile("t1.html")
    --执行函数,得到渲染之后的内容
    local content = func(context)
    --通过ngx API输出
    ngx.say(content)
    

    常见用法即如下两种方式:要么直接将模板内容直接作为响应输出,要么得到渲染后的内容然后按照想要的规则输出。

    openResty.conf配置文件

    location /lua_template_2 {  
        default_type 'text/html';  
        lua_code_cache on;  
        content_by_lua_file /usr/openResty/lua/test_template_2.lua;  
    } 
    

    使用示例

    test_template_3.lua

    local template = require("resty.template")
    
    local context = {
      title = "测试",
      name = "张三",
      description = "<script>alert(1);</script>",
      age = 20,
      hobby = {"电影", "音乐", "阅读"},
      score = {语文 = 90, 数学 = 80, 英语 = 70},
      score2 = {
        {name = "语文", score = 90},
        {name = "数学", score = 80},
        {name = "英语", score = 70},
      }
    }
    
    template.render("t3.html", context)
    

    模板文件/usr/openResty/templates/t3.html

    <html>
      <head>
        <meta charset="UTF-8" />
      </head>
      <body>
        {# 不转义变量输出 #}
        姓名:{* string.upper(name) *}<br/>
        {# 转义变量输出 #}
        简介:{{description}}<br/>
        {# 可以做一些运算 #}
        年龄: {* age + 1 *}<br/>
        {# 循环输出 #}
        爱好:
        {% for i, v in ipairs(hobby) do %}
          {% if i > 1 then %},{% end %}
          {* v *}
        {% end %}<br/>
    
        成绩:
        {% local i = 1; %}
        {% for k, v in pairs(score) do %}
          {% if i > 1 then %},{% end %}
          {* k *} = {* v *}
          {% i = i + 1 %}
        {% end %}<br/>
        成绩2:
        {% for i = 1, #score2 do local t = score2[i] %}
          {% if i > 1 then %},{% end %}
          {* t.name *} = {* t.score *}
        {% end %}<br/>
        {# 中间内容不解析 #}
        {-raw-}{(file)}{-raw-}
      </body>
    </html>
    

    {(include_file)}:包含另一个模板文件;
    {* var *}:变量输出;
    {{ var }}:变量转义输出;
    {% code %}:代码片段;
    {# comment #}:注释;
    {-raw-}:中间的内容不会解析,作为纯文本输出;
    模板最终被转换为Lua代码进行执行,所以模板中可以执行任意Lua代码。

    openResty.conf配置文件

    location /lua_template_3 {
        default_type 'text/html';
        lua_code_cache on;
        content_by_lua_file /usr/openResty/lua/test_template_3.lua;
    }
    

    访问如http://127.0.0.1/lua_template_3进行测试。
    基本的模板引擎使用到此就介绍完了。

  • 相关阅读:
    MySQL百万级、千万级数据多表关联SQL语句调优
    不就是SELECT COUNT语句吗,居然有这么多学问
    分布式锁讲解
    Java 中堆和栈的区别
    Java中的回调机制
    在Eclipse上Maven环境配置使用
    项目忽然出现 The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path 解决方法
    HttpServletResponse
    com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
    深入浅出java常量池
  • 原文地址:https://www.cnblogs.com/chen-msg/p/14688628.html
Copyright © 2011-2022 走看看