skynet框架应用 (十八) http协议的服务

18 http协议的服务

​ http协议的服务分为http服务端与http客户端,skynet服务作为http服务端的时候,可以像其他的web服务器一样,接收http请求并给与应答。skynet的服务作为http客户端的时候,可以通过http协议像远端发送请求并等待得到应答。

18.1http服务端

​ skynet 从 v0.5.0 开始提供了简单的 http 服务器的支持。skynet.httpd 是一个独立于 skynet 的,用于 http 协议解析的库,它本身依赖 socket api 的注入。使用它,你需要把读写 socket 的 API 封装好,注入到里面就可以工作。

​ skynet.sockethelper 模块将 skynet 的 Socket API 封装成 skynet.httpd 可以接受的形式:阻塞读写指定的字节数、网络错误以异常形式抛出。

下面是一个简单的范例:testhttpd.lua


local skynet = require "skynet"
local socket = require "skynet.socket"

skynet.start(function()
    local agent = {}
    for i= 1, 20 do
        -- 启动 20 个代理服务用于处理 http 请求
        agent[i] = skynet.newservice("testhttpagent")  
    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)

http代理服务代码:testagenthttp.lua

-- 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 string = string


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 (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 做出回应。
                if header.host then
                    skynet.error("header host", header.host)
                end

                local path, query = urllib.parse(url)
                skynet.error(string.format("path: %s", path))
                local color,text = "red", "hello"
                if query then
                    local q = urllib.parse_query(query) --获取请求的参数
                    for k, v in pairs(q) do
                        skynet.error(string.format("query: %s= %s", k,v))
                        if(k == "color") then
                            color = v
                        elseif (k == "text") then
                            text = v
                        end
                    end
                end
                local resphtml = "<body bgcolor=\""..color.."\">"..text.."</body>\n" --返回一张网页
                response(id, code, resphtml) --返回状态码200,并且跟上内容
            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)

​ 这个 httpd 模块最初是用于服务器内部管理,以及和其它平台对接。所以只提供了最简单的功能。如果是重度的业务要使用,可以考虑再其上做进一步的开发。

运行httpd服务,然后在浏览器地址栏上输入http://127.0.0.1:8001/?color=blue&text=abc,httpd服务输出如下:

$ ./skynet examples/conf
testhttpd
[:01000010] LAUNCH snlua testhttpd
[:01000012] LAUNCH snlua testhttpagent
[:01000019] LAUNCH snlua testhttpagent
[:0100001a] LAUNCH snlua testhttpagent
[:0100001b] LAUNCH snlua testhttpagent
[:0100001c] LAUNCH snlua testhttpagent
[:0100001d] LAUNCH snlua testhttpagent
[:0100001e] LAUNCH snlua testhttpagent
[:0100001f] LAUNCH snlua testhttpagent
[:01000020] LAUNCH snlua testhttpagent
[:01000022] LAUNCH snlua testhttpagent
[:01000029] LAUNCH snlua testhttpagent
[:01000031] LAUNCH snlua testhttpagent
[:01000033] LAUNCH snlua testhttpagent
[:01000034] LAUNCH snlua testhttpagent
[:01000035] LAUNCH snlua testhttpagent
[:01000036] LAUNCH snlua testhttpagent
[:01000037] LAUNCH snlua testhttpagent
[:01000038] LAUNCH snlua testhttpagent
[:01000039] LAUNCH snlua testhttpagent
[:0100003a] LAUNCH snlua testhttpagent
[:01000010] 127.0.0.1:50614 connected, pass it to agent :01000012
[:01000012] header host 127.0.0.1:8001
[:01000012] path: /
[:01000012] query: color= blue
[:01000012] query: text= abc

18.2 http客户端

​ 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 服务)。

示例代码:httpclient.lua

local skynet = require "skynet"
local httpc = require "http.httpc"
local dns = require "skynet.dns"

local function main()
    httpc.dns() -- set dns server
    httpc.timeout = 100 -- set timeout 1 second
    print("GET baidu.com")
    local respheader = {}
    local status, body = httpc.request("GET", "baidu.com", "/", respheader, { host = "baidu.com" })
    --local status, body = httpc.get("baidu.com", "/", respheader, { host = "baidu.com" })
    print("[header] =====>")
    for k,v in pairs(respheader) do
        print(k,v)
    end
    print("[body] =====>", status)
    print(body)
end

skynet.start(function()
    print(pcall(main))
    skynet.exit()
end)

运行结果:

$ ./skynet examples/conf
testhttpclient
[:01000010] LAUNCH snlua testhttpclient
GET baidu.com
[header] =====> #百度返回的响应头部分
connection  Keep-Alive
date    Fri, 09 Feb 2018 16:49:34 GMT
last-modified   Tue, 12 Jan 2010 13:48:00 GMT
expires Sat, 10 Feb 2018 16:49:34 GMT
cache-control   max-age=86400
accept-ranges   bytes
content-type    text/html
content-length  81
etag    "51-47cf7e6ee8400"
server  Apache
[body] =====>   200  #状态码,以及响应体
<html>
<meta http-equiv="refresh" content="0;url=http://www.baidu.com/">
</html>

true #执行成功
[:01000010] KILL self

尝试连接一下上一节的http服务,修改testhttpclient.lua:

local skynet = require "skynet"
local httpc = require "http.httpc"
local dns = require "skynet.dns"

local function main()
    httpc.dns() -- set dns server
    httpc.timeout = 100 -- set timeout 1 second
    print("GET 127.0.0.1:8001")
    local respheader = {}
    local status, body = httpc.request("GET", "127.0.0.1:8001", "/?color=blue&text=abc", respheader)
    print("[header] =====>")
    for k,v in pairs(respheader) do
        print(k,v)
    end
    print("[body] =====>", status)
    print(body)
end

skynet.start(function()
    print(pcall(main))
    skynet.exit()
end)

先运行testhttpd再运行testhttpclient:

$ ./skynet examples/conf
testhttpd
[:01000010] LAUNCH snlua testhttpd
[:01000012] LAUNCH snlua testhttpagent
[:01000019] LAUNCH snlua testhttpagent
[:0100001a] LAUNCH snlua testhttpagent
[:0100001b] LAUNCH snlua testhttpagent
[:0100001c] LAUNCH snlua testhttpagent
[:0100001d] LAUNCH snlua testhttpagent
[:0100001e] LAUNCH snlua testhttpagent
[:0100001f] LAUNCH snlua testhttpagent
[:01000020] LAUNCH snlua testhttpagent
[:01000022] LAUNCH snlua testhttpagent
[:01000029] LAUNCH snlua testhttpagent
[:01000031] LAUNCH snlua testhttpagent
[:01000033] LAUNCH snlua testhttpagent
[:01000034] LAUNCH snlua testhttpagent
[:01000035] LAUNCH snlua testhttpagent
[:01000036] LAUNCH snlua testhttpagent
[:01000037] LAUNCH snlua testhttpagent
[:01000038] LAUNCH snlua testhttpagent
[:01000039] LAUNCH snlua testhttpagent
[:0100003a] LAUNCH snlua testhttpagent
testhttpclient
[:0100003b] LAUNCH snlua testhttpclient
GET 127.0.0.1:8001
[:01000010] 127.0.0.1:50646 connected, pass it to agent :01000012
[:01000012] header host 127.0.0.1:8001 #testhttpd服务获取GET请求
[:01000012] path: /
[:01000012] query: color= blue  
[:01000012] query: text= abc
[header] =====>      #testhttpclient得到响应
content-length  32
[body] =====>   200
<body bgcolor="blue">abc</body>

true
[:0100003b] KILL self

### 回答1: 《Skynet框架教程-非常详细.pdf》是一本关于Skynet框架的教程,它提供了关于Skynet框架的全面和详细的介绍。 该教程首先介绍了Skynet框架的背景和起源,以及它的特点和优势。Skynet是一个高度可扩展的分布式服务框架,具有高性能和低延迟的特点。它采用了基于actor模型的并发编程模型,并提供了丰富的工具和库来帮助开发者构建和管理分布式应用。 教程的下一部分介绍了Skynet框架的基本概念和架构。它解释了Skynet节点、服务和消息传递等核心概念,并提供了示例代码来说明这些概念的使用方法。读者可以通过这一部分了解Skynet框架的基本原理和用法。 接下来的章节详细介绍了Skynet框架的各个组件和功能。其中包括服务注册与发现、负载均衡、容错机制、监控和调试等方面。每个组件和功能都有详细的说明和示例代码,读者可以通过实践来学习和理解。 教程的最后一部分是一些实际应用案例的介绍。这些案例涵盖了不同领域和规模的应用,包括游戏服务器、在线教育平台、电子商务网站等。每个案例都详细介绍了Skynet框架在该应用中的具体应用和实现过程,对于读者来说是一个很好的参考和借鉴资料。 总之,《Skynet框架教程-非常详细.pdf》是一本很好的Skynet框架学习资料,它提供了全面而详细的内容,涵盖了Skynet框架的各个方面。无论是初学者还是有一定经验的开发者,都可以通过这本教程来学习和掌握Skynet框架的使用和开发技巧。 ### 回答2: 《Skynet框架教程-非常详细.pdf》是一本关于Skynet框架的详细教程文档,其中包含了关于Skynet框架的基本概念、核心功能、使用方法等内容。 Skynet框架是一个高性能、轻量级的分布式服务框架,适用于开发网络游戏、实时通信等高并发场景。该框架基于事件驱动模型,通过异步消息传递和多线程技术实现高并发处理能力。 在《Skynet框架教程-非常详细.pdf》中,首先介绍了Skynet框架的背景与发展历程,帮助读者了解该框架的起源和应用领域。接着详细介绍了Skynet框架的核心架构,包括节点管理、服务管理、消息传递等模块的设计与实现原理。 教程还详细介绍了Skynet框架的安装和配置,包括环境准备、编译与安装等步骤。然后,通过一系列实际案例演示了如何使用Skynet框架进行开发,包括创建节点、注册服务、消息处理、资源管理等方面的内容。 此外,教程还介绍了Skynet框架的调试和优化技巧,包括日志查看、性能测试工具的使用等方面的内容。最后,给出了一些常见问题的解答,帮助读者更好地解决在使用Skynet框架过程中遇到的困惑。 总的来说,《Skynet框架教程-非常详细.pdf》是一本适合初学者和有一定经验的开发人员的教程,通过阅读该教程可以深入了解Skynet框架的原理和使用方法,从而更好地应用于实际项目中。 ### 回答3: 《Skynet框架教程-非常详细.pdf》是一本非常详细的Skynet框架教程。Skynet框架是一个高性能、高可靠性的分布式服务框架,用于构建可扩展的游戏服务器、物联网平台等分布式应用。 这本教程从Skynet框架的基础知识讲起,介绍了Skynet框架的特点、架构和设计理念。然后详细介绍了Skynet框架的安装和配置,包括环境准备、编译安装和启动流程等。 接下来,教程深入讲解了Skynet框架的核心概念和基本用法,包括服务、消息、协议等。这些内容帮助读者理解Skynet框架的工作原理,并能够快速上手开发。 教程还介绍了Skynet框架的高级特性和扩展功能,如集群部署、负载均衡、动态扩容等。这些内容使读者能够在实际应用中解决复杂的问题,并提升系统的性能和可扩展性。 此外,教程还提供了大量的示例和实战案例,帮助读者将理论应用到实际项目中。通过这些实例,读者可以学习到如何使用Skynet框架构建真实的分布式应用,同时也能够了解到Skynet框架的一些最佳实践和常见错误。 综上所述,《Skynet框架教程-非常详细.pdf》是一本非常全面的Skynet框架教程,适合初学者和有一定经验的开发者阅读,对于了解Skynet框架的原理和使用方法都有很大帮助。无论是想要学习Skynet框架的基础知识,还是进一步提升Skynet框架应用技巧,这本教程都是一个不错的选择。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值