• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            Fork me on GitHub
            隨筆 - 215  文章 - 13  trackbacks - 0
            <2017年2月>
            2930311234
            567891011
            12131415161718
            19202122232425
            2627281234
            567891011


            專注即時通訊及網游服務端編程
            ------------------------------------
            Openresty 官方模塊
            Openresty 標準模塊(Opm)
            Openresty 三方模塊
            ------------------------------------
            本博收藏大部分文章為轉載,并在文章開頭給出了原文出處,如有再轉,敬請保留相關信息,這是大家對原創作者勞動成果的自覺尊重!!如為您帶來不便,請于本博下留言,謝謝配合。

            常用鏈接

            留言簿(1)

            隨筆分類

            隨筆檔案

            相冊

            Awesome

            Blog

            Book

            GitHub

            Link

            搜索

            •  

            積分與排名

            • 積分 - 215741
            • 排名 - 118

            最新評論

            閱讀排行榜

            http://lua.ren/topic/135/%E5%85%B3%E4%BA%8E-openresty-%E7%9A%84%E4%B8%A4%E4%B8%89%E4%BA%8B
            基礎原理 Nginx 采用的是 master-worker 模型,一個 master 進程管理多個 worker 進程,基本的事件處理都是放在 woker 中,master 負責一些全局初始化,以及對 worker 的管理。

            每個 woker 使用一個 LuaVM,當請求被分配到 woker 時,將在這個 LuaVM 里創建一個 coroutine。協程之間數據隔離,每個協程具有獨立的全局變量 _G。

            關于 LUACODECACHE 關閉 luacodecache 時,require 的處理方式是每次都強制重新加載和解析,也就是說,你對代碼的任何修改的效果,都將在上傳后立即體現。

            開啟 luacodecache 時,在同一個 LuaVM 中,模塊將在首次加載并解析后被緩存,之后再次 require 將直接返回緩存的內容。換句話說,同一 worker 上的所有請求將共享已加載的模塊,任意一個請求對于模塊屬性的修改,都將影響到同一 worker 上的其他請求。

            不應使用模塊級的局部變量以及模塊屬性,存放任何請求級的數據。否則在 luacodecache 開啟時,會造成請求間相互影響和數據競爭,產生不可預知的異常狀況。

            關閉 luacodecache 會極大的降低性能,在生產環境中應開啟 luacodecache 。

            雖然開發環境中關閉 luacodecache 會有一些便利性,但我強烈建議開啟 luacodecache ,與線上保持一致,以減少不必要的差異性問題和額外測試需求。

            開啟 luacodecache 時,可用 nginx -s reload 或 kill -HUP masterPID 方式熱重載代碼,無需重啟 Nginx。

            關于 PATH 和 CPATH OpenResty 會將它的 lib 目錄加入 package.path 和 package.cpath,但你的項目目錄需要自己處理。

            在入口文件中,將項目目錄加入 package.path 和 package.cpath 是不可取的。因為 luacodecache 開啟時,package 模塊是同一 worker 上所有請求共享的,如果無條件追加,package.path 和 package.cpath 將不斷變長,并最終導致內存溢出。

            以下是我采用的解決方案:

            local ok, app = pcall(require, "core.app")

            if ok then
                app:run()
            else
                local rootPath = ngx.var.document_root

                if not (package.path:find(rootPath)) then
                    package.path = package.path .. ";" .. rootPath .. "/?.lua;;"
                end

                if not (package.cpath:find(rootPath)) then
                    package.cpath = package.cpath .. ";" .. rootPath .. "/?.so;;"
                end

                require("core.app"):run()
            end
            關于 LUA-RESTY-MYSQL 和 LUA-RESTY-REDIS 不應使用模塊級的局部變量以及模塊屬性,存放 resty.mysql 和 resty.redis 實例。否則,在 luacodecache 開啟時,同一 worker 的所有請求將共享該實例,造成數據競爭問題。建議將 resty.mysql 和 resty.redis 實例存放到 ngx.ctx 中。

            不能在 require 過程中實例化 resty.mysql 和 resty.redis 實例,否則會報錯。例如,模塊返回一個 function,此 function 直接或間接調用實例化 resty.mysql 和 resty.redis 的代碼,將會導致報錯。

            在首次查詢時實例化是一個比較好的解決方案:

            local mysql = require("resty.mysql")
            local exception = require("core.exception")
            local dbConf = require("config.mysql")
            local sysConf = require("config.system")

            local MySQL = {}

            --- 獲取連接
            --
            -- @return resty.mysql MySQL連接
            -- @error mysql.socketFailed socket建立失敗
            -- @error mysql.cantConnect 無法連接數據庫
            -- @error mysql.queryFailed 數據查詢失敗
            function MySQL:getClient()
                if ngx.ctx[MySQL] then
                    return ngx.ctx[MySQL]
                end

                local client, errmsg = mysql:new()

                if not client then
                    exception:raise("mysql.socketFailed", { message = errmsg })
                end

                client:set_timeout(3000)

                local options = {
                    user = dbConf.USER,
                    password = dbConf.PASSWORD,
                    database = dbConf.DATABASE
                }

                if dbConf.SOCK then
                    options.path = dbConf.SOCK
                else
                    options.host = dbConf.HOST
                    options.port = dbConf.PORT
                end

                local result, errmsg, errno, sqlstate = client:connect(options)

                if not result then
                    exception:raise("mysql.cantConnect", {
                        message = errmsg,
                        code = errno,
                        state = sqlstate
                    })
                end

                local query = "SET NAMES " .. sysConf.DEFAULT_CHARSET
                local result, errmsg, errno, sqlstate = client:query(query)

                if not result then
                    exception:raise("mysql.queryFailed", {
                        query = query,
                        message = errmsg,
                        code = errno,
                        state = sqlstate
                    })
                end

                ngx.ctx[MySQL] = client
                return ngx.ctx[MySQL]
            end

            --- 關閉連接
            function MySQL:close()
                if ngx.ctx[MySQL] then
                    ngx.ctx[MySQL]:set_keepalive(0, 100)
                    ngx.ctx[MySQL] = nil
                end
            end

            --- 執行查詢
            --
            -- 有結果數據集時返回結果數據集
            -- 無數據數據集時返回查詢影響,如:
            -- { insert_id = 0, server_status = 2, warning_count = 1, affected_rows = 32, message = nil}
            --
            -- @param string query 查詢語句
            -- @return table 查詢結果
            -- @error mysql.queryFailed 查詢失敗
            function MySQL:query(query)
                local result, errmsg, errno, sqlstate = self:getClient():query(query)

                if not result then
                    exception:raise("mysql.queryFailed", {
                        query = query,
                        message = errmsg,
                        code = errno,
                        state = sqlstate
                    })
                end

                return result
            end

            return MySQL
            使用 setkeepalive(maxidletimeout, poolsize) 替代 close() 將啟用連接池特性。set_keepalive 的意思可以理解為,保持連接,并將連接歸還到連接池內。這樣在下次連接時,會首先會嘗試從連接池獲取連接,獲取不成功才會創建新的連接。在高并發下,連接池能大大的減少連接 MySQL 和 Redis 的次數,明顯的提升性能。

            使用模塊緩存靜態數據 利用 luacodecache 開啟時模塊會被緩存的特性,我們可以使用模塊來緩存靜態數據,其效率接近于將數據緩存在內存中。

            存儲方法:

            local exception = require("core.exception")
            local mysql = require("core.driver.mysql")

            --- 實現示例,可以根據項目情況,完善后封裝在數據查詢層
            local function makeCityCache()
                local citys = mysql:query("SELECT * FROM `data_city` WHERE 1")
                local cityData = {}

                for _, city in ipairs(citys) do
                    cityData[city.id] = city
                end

                package.loaded["cache.city"] = cityData
            end
            讀取方法:
            --- 實現示例,可以根據項目情況,完善后封裝在數據查詢層
            local function getCityCache(id)
                local ok, cacheData = pcall(require, "cache.city")

                if ok then
                    return cacheData[id]
                end

                return nil
            end
            清理方法:
            --- 實現示例,可以根據項目情況,完善后封裝在數據查詢層
            local function clearCityCache()
                package.loaded["cache.city"] = nil
            end
            關于 OPENRESTY 的兩三事 火星梅梅 | 5 八月, 2013 | OpenResty, 愛 Coding | 2條評論 基礎原理 Nginx 采用的是 master-worker 模型,一個 master 進程管理多個 worker 進程,基本的事件處理都是放在 woker 中,master 負責一些全局初始化,以及對 worker 的管理。

            每個 woker 使用一個 LuaVM,當請求被分配到 woker 時,將在這個 LuaVM 里創建一個 coroutine。協程之間數據隔離,每個協程具有獨立的全局變量 _G。

            關于 LUACODECACHE 關閉 luacodecache 時,require 的處理方式是每次都強制重新加載和解析,也就是說,你對代碼的任何修改的效果,都將在上傳后立即體現。

            開啟 luacodecache 時,在同一個 LuaVM 中,模塊將在首次加載并解析后被緩存,之后再次 require 將直接返回緩存的內容。換句話說,同一 worker 上的所有請求將共享已加載的模塊,任意一個請求對于模塊屬性的修改,都將影響到同一 worker 上的其他請求。

            不應使用模塊級的局部變量以及模塊屬性,存放任何請求級的數據。否則在 luacodecache 開啟時,會造成請求間相互影響和數據競爭,產生不可預知的異常狀況。

            關閉 luacodecache 會極大的降低性能,在生產環境中應開啟 luacodecache 。

            雖然開發環境中關閉 luacodecache 會有一些便利性,但我強烈建議開啟 luacodecache ,與線上保持一致,以減少不必要的差異性問題和額外測試需求。

            開啟 luacodecache 時,可用 nginx -s reload 或 kill -HUP masterPID 方式熱重載代碼,無需重啟 Nginx。

            關于 PATH 和 CPATH OpenResty 會將它的 lib 目錄加入 package.path 和 package.cpath,但你的項目目錄需要自己處理。

            在入口文件中,將項目目錄加入 package.path 和 package.cpath 是不可取的。因為 luacodecache 開啟時,package 模塊是同一 worker 上所有請求共享的,如果無條件追加,package.path 和 package.cpath 將不斷變長,并最終導致內存溢出。

            以下是我采用的解決方案:

            local ok, app = pcall(require, "core.app")
             
            if ok then
                app:run()
            else
                local rootPath = ngx.var.document_root
             
                if not (package.path:find(rootPath)) then
                    package.path = package.path .. ";" .. rootPath .. "/?.lua;;"
                end
             
                if not (package.cpath:find(rootPath)) then
                    package.cpath = package.cpath .. ";" .. rootPath .. "/?.so;;"
                end
             
                require("core.app"):run()
            end
            關于 LUA-RESTY-MYSQL 和 LUA-RESTY-REDIS 不應使用模塊級的局部變量以及模塊屬性,存放 resty.mysql 和 resty.redis 實例。否則,在 luacodecache 開啟時,同一 worker 的所有請求將共享該實例,造成數據競爭問題。建議將 resty.mysql 和 resty.redis 實例存放到 ngx.ctx 中。

            不能在 require 過程中實例化 resty.mysql 和 resty.redis 實例,否則會報錯。例如,模塊返回一個 function,此 function 直接或間接調用實例化 resty.mysql 和 resty.redis 的代碼,將會導致報錯。

            在首次查詢時實例化是一個比較好的解決方案:

            local mysql = require("resty.mysql")
            local exception = require("core.exception")
            local dbConf = require("config.mysql")
            local sysConf = require("config.system")
             
            local MySQL = {}
             
            --- 獲取連接
            --
            -- @return resty.mysql MySQL連接
            -- @error mysql.socketFailed socket建立失敗
            -- @error mysql.cantConnect 無法連接數據庫
            -- @error mysql.queryFailed 數據查詢失敗
            function MySQL:getClient()
                if ngx.ctx[MySQL] then
                    return ngx.ctx[MySQL]
                end
             
                local client, errmsg = mysql:new()
             
                if not client then
                    exception:raise("mysql.socketFailed", { message = errmsg })
                end
             
                client:set_timeout(3000)
             
                local options = {
                    user = dbConf.USER,
                    password = dbConf.PASSWORD,
                    database = dbConf.DATABASE
                }
             
                if dbConf.SOCK then
                    options.path = dbConf.SOCK
                else
                    options.host = dbConf.HOST
                    options.port = dbConf.PORT
                end
             
                local result, errmsg, errno, sqlstate = client:connect(options)
             
                if not result then
                    exception:raise("mysql.cantConnect", {
                        message = errmsg,
                        code = errno,
                        state = sqlstate
                    })
                end
             
                local query = "SET NAMES " .. sysConf.DEFAULT_CHARSET
                local result, errmsg, errno, sqlstate = client:query(query)
             
                if not result then
                    exception:raise("mysql.queryFailed", {
                        query = query,
                        message = errmsg,
                        code = errno,
                        state = sqlstate
                    })
                end
             
                ngx.ctx[MySQL] = client
                return ngx.ctx[MySQL]
            end
             
            --- 關閉連接
            function MySQL:close()
                if ngx.ctx[MySQL] then
                    ngx.ctx[MySQL]:set_keepalive(0, 100)
                    ngx.ctx[MySQL] = nil
                end
            end
             
            --- 執行查詢
            --
            -- 有結果數據集時返回結果數據集
            -- 無數據數據集時返回查詢影響,如:
            -- { insert_id = 0, server_status = 2, warning_count = 1, affected_rows = 32, message = nil}
            --
            -- @param string query 查詢語句
            -- @return table 查詢結果
            -- @error mysql.queryFailed 查詢失敗
            function MySQL:query(query)
                local result, errmsg, errno, sqlstate = self:getClient():query(query)
             
                if not result then
                    exception:raise("mysql.queryFailed", {
                        query = query,
                        message = errmsg,
                        code = errno,
                        state = sqlstate
                    })
                end
             
                return result
            end
             
            return MySQL
            使用 setkeepalive(maxidletimeout, poolsize) 替代 close() 將啟用連接池特性。set_keepalive 的意思可以理解為,保持連接,并將連接歸還到連接池內。這樣在下次連接時,會首先會嘗試從連接池獲取連接,獲取不成功才會創建新的連接。在高并發下,連接池能大大的減少連接 MySQL 和 Redis 的次數,明顯的提升性能。

            使用模塊緩存靜態數據 利用 luacodecache 開啟時模塊會被緩存的特性,我們可以使用模塊來緩存靜態數據,其效率接近于將數據緩存在內存中。

            存儲方法:

            local exception = require("core.exception")
            local mysql = require("core.driver.mysql")
             
            --- 實現示例,可以根據項目情況,完善后封裝在數據查詢層
            local function makeCityCache()
                local citys = mysql:query("SELECT * FROM `data_city` WHERE 1")
                local cityData = {}
             
                for _, city in ipairs(citys) do
                    cityData[city.id] = city
                end
             
                package.loaded["cache.city"] = cityData
            end
            讀取方法:
            --- 實現示例,可以根據項目情況,完善后封裝在數據查詢層
            local function getCityCache(id)
                local ok, cacheData = pcall(require, "cache.city")
             
                if ok then
                    return cacheData[id]
                end
             
                return nil
            end
            清理方法:
            --- 實現示例,可以根據項目情況,完善后封裝在數據查詢層
            local function clearCityCache()
                package.loaded["cache.city"] = nil
            end
            數據存儲 _G

            請求級 table 變量,生命周期為本次請求,可存儲請求級任意 Lua 數據。

            NGX.CTX

            請求級 table 變量,生命周期為本次請求,可存儲請求級任意 Lua 數據。

            NGX.SHARED.DICT

            全局級 key-value 字典,使用共享內存實現,實現了讀寫鎖,所有請求均可安全讀寫。 value 只能為布爾值、數字和字符串。Reload Nginx 時不會受影響,只有當 Nginx 被關閉時才會丟失。

            模塊屬性和模塊級局部變量

            worker 級變量,同一 worker 的所有請求共享,沒有讀寫鎖,多個請求同時寫入時不安全。

            多謝原作者的分享: http://zivn.me/?p=157

            LUA.REN
            www.lua.ren 
            posted on 2016-08-02 13:48 思月行云 閱讀(749) 評論(0)  編輯 收藏 引用 所屬分類: Nginx\Openresty
            亚洲狠狠婷婷综合久久蜜芽 | 香蕉久久夜色精品国产小说| 一本久久综合亚洲鲁鲁五月天亚洲欧美一区二区 | 久久久99精品成人片中文字幕 | 欧洲成人午夜精品无码区久久| 精品国产婷婷久久久| 四虎国产精品免费久久5151| 久久不射电影网| 99久久成人18免费网站| 亚洲午夜久久影院| 国产精品女同一区二区久久| 精品久久久无码中文字幕天天| 99久久www免费人成精品| 人妻精品久久久久中文字幕| 久久婷婷色香五月综合激情| 少妇无套内谢久久久久| 久久精品国产欧美日韩99热| 精品久久久中文字幕人妻| 欧美喷潮久久久XXXXx| 久久久精品一区二区三区| 久久精品无码一区二区app| 欧美成a人片免费看久久| 精品国产日韩久久亚洲| 久久精品亚洲精品国产色婷| 日本免费一区二区久久人人澡| 久久综合成人网| 久久久久久人妻无码| 精品无码久久久久久久久久| 99精品久久久久久久婷婷| 久久久久久狠狠丁香| 少妇熟女久久综合网色欲| a级毛片无码兔费真人久久| 少妇人妻综合久久中文字幕| 国产一区二区三区久久| 波多野结衣久久一区二区| 国产精品青草久久久久婷婷 | 无码超乳爆乳中文字幕久久| 91亚洲国产成人久久精品| 精品一二三区久久aaa片| 色综合久久中文综合网| 久久久久人妻一区二区三区vr|