由OpenResty粘合的企业Web架构

前言:
    2012年2月章亦春(agentzh)在Tech-Club的一次线下聚会上以《由Lua 粘合的Nginx生态环境》为主题做了演讲,分析了企业Web架构的趋势,即一个看起来完整的Web应用,往往在后台被拆分成多个Service,由多个部门分别实现,而每个部门提供給其它部门的都是http协议resful形式的接口,随后介绍了一些Nginx模块,最后讲到了将Lua嵌入到Nginx,对之所以采用Nginx+Lua来构建给出了原因。
相关链接:
http://www.tech-club.org/?p=247
http://blog.zoomquiet.org/pyblosxom/oss/openresty-intro-2012-03-06-01-13.html
http://agentzh.org/misc/slides/ngx-openresty-ecosystem/#1
正文:
     不久前,无意间看到春哥的这篇文章,也因为之前有了解过并发编程的一些概念,对Lua中的协程早有耳闻,于是花了几天时间看了Lua的语法,被这个小巧的高性能的语言所吸引,于是决定一探究竟,为方便实验,便直接下载了OpenResty,编译运行。下面与大家分享我的一些经验。
    这里以我写的开源防盗链系统(github地址https://github.com/Hevienz/nginx-lua-ds-hotlink)为例,防盗链系统的实现有两种方法,一种是检查HTTP头中的Referer字段,这种方法只能用于一般性的场合,第二种方法是为特定的IP和文件生成accesskey,然后用户通过特定的accesskey来访问特定的文件。
    首先,我们在nginx配置中配置如下,以使应用开发人员可以方便的取得accesskey。

    location = /get_key {
        allow 10.0.2.2;
        deny all;
        content_by_lua_file lua/ds_hotlink/get_key.lua;
    }

在accesskey.lua文件中调用hotlink_get_key() 函数,而此函数的实现放在init.lua之中,这样此函数在nginx启动时便被初始化,经测试,这种做法对性能的提升很明显。
在init.lua中我们实现hotlink_get_key()函数,如下:

function hotlink_get_key()
    local path=ngx.var.arg_path
    if not path then
        ngx.exit(405)
    end
    local ip=ngx.var.arg_ip
    if not ip then
        ngx.exit(405)
    end
    local time=ngx.now()
    local string=time..path..ip
    local digest = ngx.hmac_sha1(config.secret_key, string)
    ngx.say(ngx.encode_base64(time..":"..digest))
end

通过ngx.var.arg_path和ngx.var.arg_ip来分别取得url中的path和ip两个参数,然后基于时间生成散列值,附上时间信息后用base64编码后返回给客户端。
然后在需要防盗链的location下配置如下:

access_by_lua_file lua/ds_hotlink/accesskey.lua;

在accesskey.lua中依然是调用在init.lua中初始化的hotlink_accesskey_module()函数,如下:

function hotlink_accesskey_module()
    local key=ngx.var.arg_key
    if not key then
        log{module_name="HOTLINK_ACCESSKEY_MODULE"}
        ngx.exit(405)
    end
    local uri=ngx.var.request_uri
    local path=nil
    for i in string.gmatch(uri, "[^\\?]+") do
        path=i
        break
    end
    local user_ip=get_user_ip()
    local time_digest=ngx.decode_base64(key)
    if not time_digest then
        log{module_name="HOTLINK_ACCESSKEY_MODULE"}
        ngx.exit(405)
    end
    if not time_digest:match(":") then
        log{module_name="HOTLINK_ACCESSKEY_MODULE"}
        ngx.exit(405)
    end
    local tmp_dic_time_digest={}
    for i in string.gmatch(time_digest,"[^\\:]+") do
        table.insert(tmp_dic_time_digest,i)
    end
    local time=tmp_dic_time_digest[1]
    local digest=tmp_dic_time_digest[2]
    if not time or not tonumber(time) or not digest or time+config.expiration_time < ngx.now() then
        log{module_name="HOTLINK_ACCESSKEY_MODULE"}
        ngx.exit(405)
    end
    local string=time..path..user_ip
    local real_digest = ngx.hmac_sha1(config.secret_key, string)
    if digest ~=real_digest then
        log{module_name="HOTLINK_ACCESSKEY_MODULE"}
        ngx.exit(405)
    end

    return
end

在url中取得参数key,用base64解码后得到时间和散列信息,然后用ngx.var.request_uri,用户IP和解析得到的时间来判断散列值是否相匹配,并且在其中提供诸如超时时间和日志的附属功能,相关代码比较简单,贴在这里:

local config=require("config")
fd = io.open(config.logs_pwd.."hotlink.log","ab")

local function get_user_ip()
    local req_headers = ngx.req.get_headers()
    return (req_headers["X-Real-IP"] or req_headers["X_Forwarded_For"]) or ngx.var.remote_addr
end

local function log(args)
   local req_headers = ngx.req.get_headers()
   local time=ngx.localtime()
   local user_ip = get_user_ip()
   local method=ngx.req.get_method()
   local request_uri=ngx.var.request_uri
   local user_agent=ngx.var.http_user_agent or "-"
   local http_version=ngx.req.http_version()
   local header_refer=req_headers["Referer"] or "-"
   local key=ngx.var.arg_key or "-"
   local line = "["..args.module_name.."] "..user_ip.." ["..time.."] \""..method.." "..request_uri.." "..http_version.."\" \""..user_agent.."\" \""..header_refer.."\" \""..key.."\"\n"
   fd:write(line)
   fd:flush()
end

其中的config模块如下:

module("config")

logs_pwd="/var/log/hotlink/"

----refer module----
white_domains={
    "geekhub\\.cc",
}

----accesskey module----
secret_key="naudw72h2iu34298dnawi81"
expiration_time=3600

开发人员可以修改其中的配置,如日志路径,refer模块的域白名单,accesskey模块的secret_key和超时时间。
可能有人会问这和Web开发有什么关系,其实从这个开源软件的开发过程就可以扩展到resful形式的接口的开发。
Openresty提供了以下lib用于与上游的各种数据库进行通信:
lua-resty-memcached
lua-resty-mysql
lua-resty-redis
除此之外还有各种第三方的lib,如用于mongodb的bigplum/lua-resty-mongol等,这些lib都在其github页上提供了例子和接口文档,而将从数据库中取得的数据格式化为json格式,也是十分的方便,只需将相关的数据放到table(类似于Python中数组和字典)中,然后写下如下的代码:

local cjson = require "cjson"
ngx.say("result: ", cjson.encode(res))

对于新手来说,golgote/lua-resty-info 是一个获取OpenResty相关信息的好方法,它会提供重要的package.path和package.cpath的信息,还会列出一些变量,函数和模块的信息。这是一个demo,http://www.kembox.com/lua-resty-info.html

感谢:

在探索OpenResty的过程中,得到了春哥以及开源社区许多朋友的帮助,才得以能够有此文章,在此表示感谢。

ruoshan https://github.com/ruoshan

wendal https://github.com/wendal

agentzh https://github.com/agentzh

由OpenResty粘合的企业Web架构

时间: 2024-10-05 03:16:47

由OpenResty粘合的企业Web架构的相关文章

可灵活装配的web组件化企业应用架构

可灵活装配的web组件化企业应用架构 一. 问题和目标 B/S架构的web企业应用越来越流行,为解决企业用户灵活多变的业务需求和业务流程,将不同的业务应用开发为web组件,实现用户业务处理界面和业务流程的灵活配置和部署,基于SOA的服务思想设计企业web组件化应用架构. 其核心思想是: (1) 用户界面可装配 用户的业务操作界面可以通过管理员或者自己通过动态配置的方式形成,称之为视图.一个用户可以用不同的视图来处理不同的业务,或者设计自己个性化的视图满足个性化的需要. (2) 业务流程可装配 企

企业应用架构之分层 - 总结

原网址将会不断更新 :   作程的技术博客  <企业应用架构之分层 - 总结> it.zuocheng.net 常见分层架构模式 三层架构 3-tier architecture 微软.net 体系推荐的分层结构,因此早期在ASP编码的系统中被广泛应用,同时也被其他语言广泛借鉴. 表现层, Presentation layer(PL) 主要负责数据的输入接口和输出.输入指在WEB.客户端或为外界提供的API的数据请求接口:输出则是Web界面.客户端输出.API的数据输出. 页面模版. 对外AP

系统架构师-基础到企业应用架构-服务层

一.上章回顾 上篇我们主要讲解了系统架构中的四种架构模式,并且分析了四种架构模式的实现及应用场景,那么先来回顾下架构中的业务逻辑层的使用及总结.  如果大家对图中讲述的内容不明白或者说是不深入那么可以参考上篇讲 解的内容:系统架构师-基础到企业应用架构-业务逻辑层. 二.摘要 本文将已架构的方式去分析分层结构中的服务层的设计,如何设计出来满足我们说的业务需求及设计规范的服务层将是我们的目标,可能我想大家在项目架构的 过程中可能有些同仁,没有用到该层,或者说是采用的是常用的分层结构的设计,而没有把

系统架构师-基础到企业应用架构-企业应用架构

一.上篇回顾 我们先来回顾下上篇讲解的内容,我们前面的几节分别讲述了,业务逻辑层.数据访问层.服务层.表现层,我们了解了这些分层的职责和分层之间的大概的关联 关系,本篇可能主要是简单的介绍下企业应用的几类模式,结合这几个分层直接的交互来完成系统功能的构建.我们还是先对我们学习的四个分层的职责和功能做个大 概的回顾,我们先来看看下图来回顾下我们讲述的内容. 我想通过上图,大家能回忆起我们讲述的相关内容,然后整理好自己的思路,我们本文将会针对这几个分层进行相应的模式的讲解,并且会结合实例来说明企业应

系统架构师-基础到企业应用架构-系统建模[中篇](上)

一.上章回顾 上篇文章主要简单的介绍了建模中使用的标准建模语言UML的相关内容,包括用例图与类图的使用方法及如何建模.相信大家对UML建模语言已经有了初步的认 识,还请大家谨记UML不同的建模图形的用处.比如,用例图主要用来描述系统的功能需求.类图主要用来描述实体间的关系.谨记这些就可以帮助我们在系统架构的 过程中深入的分析. 首先向大家道歉,上篇中有部分描述错误的地方,可能对大家造成一定的错误引导.  这是上篇给出的图,我描述的是组合关系. 特别更正为:  这是正确的结果.箭头指向聚合类.描述

分享一个大型进销存供应链项目(多层架构、分布式WCF多服务器部署、微软企业库架构)

分享一个大型进销存供应链项目(多层架构.分布式WCF多服务器部署.微软企业库架构) 这是一个比较大型的项目,准备开源了.支持N家门店同时操作.远程WCF+企业库5.0实现. 这块应该算是库存模块中的核心模块了,因为该块的业务逻辑比较多,比较繁琐,大致讲讲业务逻辑吧,大致的逻辑为:出库单/出库单-->填写订单-->出库/入库-->修改库存信息,按照这个顺序来完成入库出库,顺序不能颠倒,同时还要实现订单的删除,修改,在修改库存信息时由于表和表之间有很多的外键关系,所以要同时删除多张表中含有删

企业应用架构学习笔记

最近因为要学习领域驱动设计,在看到<领域驱动设计:软件核心复杂性应对之道>时,里面很多地方都提到了<企业应用架构模式>,企业应用架构这本书是由非常著名的Martin Fowler 写的,Martin Fowler 除了总结过企业架构的设计模式之外,还写了非常多的其它软件工程相关的文章,比如曾总结过IOC,写过分析模式,写过代码重构模式,具体可参看他的个人网站http://www.martinfowler.com/articles/injection.html,等我英文阅读能力提高了

《企业应用架构模式》(POEAA)读书笔记

原文地址:<企业应用架构模式>(POEAA)读书笔记作者:邹齐龙(技术-5013 什么是架构 Rolph Johnson认为:架构是一种主观上的东西,是专家级的项目开发人员对系统设计的一些可共享的理解 架构中包括一些决定,开发者希望这些决定能尽早作出,因为在开发者看来它们是难以改变的. 如果你发现某些决定不像你想象中的那么难以改变,那么它就不再与架构相关 理解: B/S (SmartClient.C/S) 架构, DotNet 架构, J2EE架构 企业应用的特点 涉及到持久化数据 很多人同时

Web 架构师的能力(转)

文/刘如鸿 最近和几个朋友在谈到时下流行的Web 2.0,也提到了其中最重要的角色——架构师.多方各有争执,不外乎是因为背景和视角的缘故,包括架构一词,本身就从建筑学借鉴而来,至于架构师,则可以 简单地从建筑学的设计师来引申,不外乎就是设计结构,设计一个大楼的结构.回到软件本身,那就可以简单地理解为负责设计软件框架的人了. 我们没有讨论清楚架构师.软件架构师.系统架构师及其Web 架构师这些看似相同却有所区别的角色的关键,本身智者见智,仁者见仁,也不是一时半会能够说清楚的,最后我们讨论作为一个W