• <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年6月>
            28293031123
            45678910
            11121314151617
            18192021222324
            2526272829301
            2345678


            專注即時(shí)通訊及網(wǎng)游服務(wù)端編程
            ------------------------------------
            Openresty 官方模塊
            Openresty 標(biāo)準(zhǔn)模塊(Opm)
            Openresty 三方模塊
            ------------------------------------
            本博收藏大部分文章為轉(zhuǎn)載,并在文章開(kāi)頭給出了原文出處,如有再轉(zhuǎn),敬請(qǐng)保留相關(guān)信息,這是大家對(duì)原創(chuàng)作者勞動(dòng)成果的自覺(jué)尊重!!如為您帶來(lái)不便,請(qǐng)于本博下留言,謝謝配合。

            常用鏈接

            留言簿(1)

            隨筆分類

            隨筆檔案

            相冊(cè)

            Awesome

            Blog

            Book

            GitHub

            Link

            搜索

            •  

            積分與排名

            • 積分 - 215428
            • 排名 - 118

            最新評(píng)論

            閱讀排行榜

            • https://www.aliyun.com/jiaocheng/647017.html
              https://github.com/chenxiaofa/p
              前言

              作為一個(gè)游戲從業(yè)者不可能不使用推方案,以前一直使用 nginx-push-stream-module這個(gè)模塊的 Forever Iframe模式來(lái)實(shí)現(xiàn)推方案。

              最近決定研究下 lua-resty-websocket來(lái)實(shí)現(xiàn)一個(gè)更加高效好用推方案

              不推薦使用的場(chǎng)景

              由于 OpenResty目前還不能做到跨 worker通信,所以想到實(shí)現(xiàn)指定推送需要中轉(zhuǎn)一次,效率上可能不如其他語(yǔ)言如 golang等

              過(guò)于復(fù)雜的業(yè)務(wù)邏輯 頻繁的指定推送(單對(duì)單、組、Tag等) 廣播過(guò)多 一起工作的好基友們

              想要推的優(yōu)雅以下幾個(gè)基友的幫忙是不可或缺的

              ngx.semaphore

              它可以讓你在想要發(fā)消息的地方優(yōu)雅的進(jìn)入到發(fā)送階段,也可以讓你來(lái)優(yōu)雅的控制一個(gè)鏈接超時(shí)的關(guān)閉。

              ngx.shared

              由于目前我們無(wú)法做到跨 worker的通信,所以必須借助共享內(nèi)存來(lái)中轉(zhuǎn)不屬于當(dāng)前 worker的消息。

              lua-resty-websocket

              由于貪圖方便還是直接使用了現(xiàn)成的庫(kù),喜歡折騰的小伙伴請(qǐng)移步 stream-lua-nginx-module

              大概的思路

              由于不能跨 worker通信所以我給每個(gè) worker申請(qǐng)了一個(gè) shared共享內(nèi)存來(lái)保存消息。

              理論上 shared的數(shù)量等于 worker的數(shù)量最佳。

              然后每個(gè) worker啟動(dòng)一個(gè) timer來(lái)判斷當(dāng)前 worker的 message id和 shared中的 message id是否有變化。

              這里為什么不用 shared的有序列表來(lái)做,容我先賣個(gè)關(guān)子。

              當(dāng)發(fā)生變化時(shí),判斷消息的目標(biāo)是否在自己的 session hash中,如果在則發(fā)之。

              開(kāi)始準(zhǔn)備工作 修改配置文件

              首先修改 nginx.conf配置,增加以下設(shè)置

              lua_shared_dict message_1 10m;
              lua_shared_dict message_2 10m;
              lua_shared_dict message_n 10m;
              init_worker_by_lua_file scripts/init_worker_by_lua.lua;
              init_worker_by_lua
              local ngx = ngx
              local ngx_log = ngx.log
              local ngx_ERR = ngx.ERR
              local ngx_timer_at = ngx.timer.at
              local require = require
              local socketMgr = require("socketMgr")
              local delay = 1
              local loopMessage
              loopMessage = function(premature)
              if premature then
              ngx_log(ngx_ERR, "timer was shut: ", err)
              return
              end
              socketMgr:loopMessages()
              local ok, err = ngx_timer_at(delay, loopMessage)
              if not ok then
              ngx_log(ngx_ERR, "failed to create the timer: ", err)
              return
              end
              end
              loopMessage()
              loopMessages

              判斷 local message id和 shared message id是否不等。

              隨后每次 local message id+ 1 從 shared拉取數(shù)據(jù),進(jìn)行消息推送邏輯。

              建立連接

              不做過(guò)多說(shuō)明,自行查看 lua-resty-websocket的 wiki

              當(dāng)連接監(jiān)聽(tīng)好之后,要進(jìn)行一系列的管理。如:

              session id和 user id的雙向映射 session id和 group name的雙向映射

              后面再詳細(xì)說(shuō)明

              生成 session id

              我是用 ( worker id+ 1) * 100000 + worker's local incr id來(lái)生成唯一 session id比較簡(jiǎn)陋,但是夠用。

              這么做的原因是,通過(guò)對(duì) session id進(jìn)行取余可以很方便的得知 worker id,可以方便的給 shared寫(xiě)消息。

              local ngx_worker_id = ngx.worker.id()local _incr_id = 0local _gen_session_id = function()_incr_id = _incr_id + 1return (ngx_worker_id + 1) * 100000 + _incr_idend 設(shè)置消息映射

              這個(gè)可以用于收到當(dāng)前 worker所屬的 shared message判斷是否在當(dāng)前進(jìn)程。

              _messages[session_id] = {}_semaphores[session_id] = semaphore.new(0) 接收消息&;發(fā)送消息

              代碼和 官方例子類同不做過(guò)多說(shuō)明,只說(shuō)我改了什么。

              在接收消息中管理了一個(gè)變量即 close_flag用于管理 send message輕線程的退出。

              以下是一段偽代碼,含義的話請(qǐng)聯(lián)系上下文。

              local session_id = sessionMgr:gen_session_id()
              local send_semaphore = sessionMgr:get_semaphore(session_id)
              local close_flag = false
              local function _push_thread_function()
              while close_flag == false do
              local ok, err = send_semaphore:wait(300)
              if ok then
              local messages = socketMgr:getMessages(session_id)
              while messages and #messages > 0 do
              local message = messages[1]
              table_remove(messages, 1)
              --- your send message function handler
              end
              end
              if close_flag then
              socketMgr:destory(session_id)
              break
              end
              end
              end
              local push_thread = ngx_thread_spawn(_push_thread_function)
              while true do
              local data, typ, err = wbsocket:recv_frame()
              while err == "again" do
              local cut_data cut_data, _, err = wbsocket:recv_frame()
              data = data .. cut_data
              end
              if not data then
              close_flag = true
              send_semaphore:post(1)
              break
              elseif typ == 'close' then
              close_flag = true
              send_semaphore:post(1)
              break
              elseif typ == 'ping' then
              local bytes, err = wbsocket:send_pong(data)
              if not bytes then
              close_flag = true
              send_semaphore:post(1)
              break
              end
              elseif typ == 'pong' then
              elseif typ == 'text' then
              -- your receive function handler
              elseif typ == 'continuation' then
              elseif typ == 'binary' then
              end
              end
              ngx_thread_wait(push_thread)
              wbsocket:send_close() 消息推送

              現(xiàn)在說(shuō)說(shuō)為什么不用 shared的有序列表來(lái)存儲(chǔ)消息,我是使用了 shared的 set方法中的 flag屬性來(lái)存放 session id。

              這樣在獲得一個(gè)消息的時(shí)候,能很方便的知道消息是發(fā)給哪個(gè) session id的。

              繼續(xù)一段偽代碼。

              local ngx_shared = ngx.shared
              local _shared_dicts = {ngx_shared.message_1,ngx_shared.message_2,ngx_shared.message_n,}
              local current_shared = _shared_dicts[ngx_worker_id + 1]
              local current_message_id = 1
              --- 如果在當(dāng)前進(jìn)程
              if _messages[session_id] then
              table.insert(_messages[session_id], "message data")
              _semaphores[session_id]:post(1)
              --- 會(huì)進(jìn)入到,上述 _push_thread_function 方法中,進(jìn)行發(fā)送邏輯
              else
              local shared_id = session_id % 100000
              local message_shared = _shared_dicts[shared_id]
              local message_id = message_shared:incr("id", 1, 0)
              message_shared:set("message." .. message_id, "message data", 60, session_id)
              end
              其他

              https://github.com/chenxiaofa/p

              借鑒了這位同學(xué)的設(shè)計(jì)思路,實(shí)現(xiàn)了額外邏輯。如:

              加入、退出、銷毀組 各 worker之間的 cmd內(nèi)部命令執(zhí)行 熱更新的特殊處理 等
            posted on 2018-05-04 12:05 思月行云 閱讀(3191) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Nginx\Openresty
            久久综合丝袜日本网| 久久99精品国产麻豆不卡| 久久毛片免费看一区二区三区| 久久丫精品国产亚洲av不卡| 777午夜精品久久av蜜臀 | 香蕉久久夜色精品国产2020| 久久精品国产WWW456C0M| 99精品久久久久久久婷婷| 一级做a爰片久久毛片人呢| 亚洲国产精品婷婷久久| 99久久国产免费福利| 久久久久国产视频电影| 亚洲欧洲久久av| 久久精品人人做人人爽电影| 亚洲AV无一区二区三区久久| 亚洲精品乱码久久久久久| 精品永久久福利一区二区| 99久久精品午夜一区二区 | 精品久久久久久中文字幕大豆网| 久久狠狠爱亚洲综合影院| 亚洲va中文字幕无码久久不卡| 久久久久亚洲精品无码蜜桃| 亚洲AV无码1区2区久久| 久久免费国产精品一区二区| 久久青青草原精品国产不卡| 久久综合亚洲鲁鲁五月天| 久久99国内精品自在现线| 青青草国产精品久久久久| 久久综合狠狠综合久久97色| 一本一本久久a久久综合精品蜜桃 一本一道久久综合狠狠老 | 国产免费久久精品99re丫y| 国产精品久久久久jk制服| 国产福利电影一区二区三区久久老子无码午夜伦不 | 亚洲AⅤ优女AV综合久久久| 久久人人妻人人爽人人爽| 精品无码久久久久久久久久| 久久精品国产亚洲AV不卡| 国产午夜精品久久久久九九电影 | 久久婷婷国产剧情内射白浆| 久久国产精品-久久精品| 久久婷婷午色综合夜啪|