HTTP (伺服器內部管理和其他平臺的對接)

lzb991435344發表於2017-08-15

這裡寫圖片描述
2.1 HTTP
skynet.httpd 是一個獨立於 skynet 的,用於 http 協議解析的庫,它本身依賴 socket api 的注入。使用它,你需要把讀寫 socket 的 API 封裝好,注入到裡面就可以工作。
skynet.sockethelper 模組將 skynet 的 Socket API 封裝成 skynet.httpd 可以接受的形式:阻塞讀寫指定的位元組數、網路錯誤以異常形式丟擲。

-- examples/simpleweb.lua

local skynet = require "skynet"
local socket = require "skynet.socket"
local httpd = require "http.httpd"
local sockethelper = require "http.sockethelper"
local urllib = require "http.url"
local table = table
local string = string

local mode = ...
if mode == "agent" then

local function response(id, ...)
    local ok, err = httpd.write_response(sockethelper.writefunc(id), ...)
    if not ok then
        -- if err == sockethelper.socket_error , that means socket closed.
        skynet.error(string.format("fd = %d, %s", id, err))
    end
end

skynet.start(function()
    skynet.dispatch("lua", function (_,_,id)
        socket.start(id)  -- 開始接收一個 socket
        -- limit request body size to 8192(8*1024) (you can pass nil to unlimit)
        -- 一般的業務不需要處理大量上行資料,為了防止攻擊,做了一個 8K 限制。這個限制可以去掉。


        local  code,url,method,header,body= httpd.read_request(
sockethelper.readfunc(id), 8192)
        if code then
            if code ~= 200 then  -- 如果協議解析有問題,就回應一個錯誤碼 code 。
                response(id, code)
            else
        -- 這是一個示範的迴應過程,你可以根據你的實際需要,解析 url, method 和 header 做出迴應。
                local tmp = {}
                if  header.host  then
                    table.insert(tmp, string.format("host: %s", header.host))
                end
                local path, query = urllib.parse(url)
                table.insert(tmp, string.format("path: %s", path))
                if query then
                    local q = urllib.parse_query(query)
                    for k, v in pairs(q) do
                        table.insert(tmp, string.format("query: %s= %s", k,v))
                    end
                end
                response(id, code, table.concat(tmp,"\n"))
            end
        else
            -- 如果丟擲的異常是 sockethelper.socket_error 表示是客戶端網路斷開了。
            if url == sockethelper.socket_error then
                skynet.error("socket closed")
            else
                skynet.error(url)
            end
        end
        socket.close(id)
    end)
end)

else

skynet.start(function()
    local agent = {}
    for i= 1, 20 do
        -- 啟動 20 個代理服務用於處理 http 請求
        agent[i] = skynet.newservice(SERVICE_NAME, "agent")  
    end
    local balance = 1
    -- 監聽一個 web 埠
    local id = socket.listen("0.0.0.0", 8001)  
    socket.start(id , function(id, addr)  
        -- 當一個 http 請求到達的時候, 把 socket id 分發到事先準備好的代理中去處理。
        skynet.error(string.format("%s connected, pass it to agent :%08x", addr, agent[balance]))
        skynet.send(agent[balance], "lua", id)
        balance = balance + 1
        if balance > #agent then
            balance = 1
        end
    end)
end)
end

2.2 httpc–客戶端模組
skynet 提供了一個非常簡單的 http 客戶端模組。你可以用:

httpc.request(method, host, uri, recvheader, header, content)

來提交一個 http 請求,其中

•method 是 “GET” “POST” 等。
•host 為目標機的地址
•uri 為請求的 URI
•recvheader 可以是 nil 或一張空表,用於接收回應的 http 協議頭。
•header 是自定義的 http 請求頭。
注:如果 header 中沒有給出 host ,那麼將用前面的 host 引數自動補上。
•content 為請求的內容。
它返回狀態碼和內容。如果網路出錯,則丟擲 error 。

httpc.dns(server, port)

可以用來設定一個非同步查詢 dns 的伺服器地址。如果你不給出地址,那麼將從 /etc/resolv.conf 查詢地址。如果你沒有呼叫它設定非同步 dns 查詢,那麼 skynet
將在網路底層做同步查詢。這很有可能阻塞住整個 skynet 的網路訊息處理(不僅僅阻塞單個 skynet 服務)。
另外,httpc 還提供了簡單的 httpc.get 以及 httpc.post 的封裝
如果有https的需求,可使用 lua-webclient 它是libcurl multi interface的簡單封裝,支援單執行緒,非阻塞的大量http、https請求。

httpc 可以通過設定 httpc.timeout 的值來控制超時時間。時間單位為 0.01秒。

相關文章