sogou/workflow

对框架提供的http server机制很疑惑

xiaoshizijiayou opened this issue · 26 comments

你好,我看了提供的3个关于http server的例子,对于服务如何回应请求还是没看明白:1.接收到http请求后,设置好response,框架会自动发送回复吗,还是需要创建一个http请求,然后手动发送回复吗?2.如何区分并处理不同的请求地址,通过上发上来地址吗?

你好。
1、是自动回复的,回复时机是server_task所在的series里没有其他任务了。这个我在FAQ关于server回复时机的问题里有说明。如果你没有向series里插入任务,那么process函数结束时就回复了。
2、任何网络task都可以调用:

class WFNetworkTask<REQ, RESP>
{
    int get_peer_addr(struct sockaddr *addr, socklen_t *addrlen) const;
};

这个方法获得对方ip地址。无论是client还是server任务,这个都是可以用的。我在echo server的示例里,使用这个函数打印client地址。server任务无需判断返回值,因为对server任务来讲,一定有一个成功的连接。而对于client任务,如果是DNS错误或者URL解析错误,这个函数会返回-1表示无连接。

1.如果按照series里无任务是回复,框架的机制是异步的,当任务高频且连续,series里会一直有任务,那前面的回应不就永远发不出去了吗?
2.比如现在两种类型的请求,a是注册请求,b是查询请求,二者访问地址相同,到路径不同,服务端如何区分两种请求,可以分别绑定不同处理函数吗,还是只能通过上发的地址+路径来区分处理?
3.框架是如何区分get和post请求的?

1、每个http请求对应一个server_task和一个series,高频连续的请求,并不是一个server_task,也不在同一个series里。所以没有你所说的这个问题。
2、这个很简单,通过request_uri作进一步区分就好。

int process(WFHttpTask *server_task)
{
    protocol::HttpRequest *req = server_task->get_req();
    const char *request_uri = req->get_request_uri();
    ...
}

request_uri就是请求的路径了。
3、HttpRequest可以通过get_method()方法获得是GET还是POST。也可以通过set_method()指定请求的方法。

很多东西可以进一步的封装,但非必要功能,我们都留给用户自己做。可能做法和一下别的框架有所区别。

关于问题1,给的3个http server例子,不都是创建了一个server,然后start一下,series创建一个,创建多个httptask然后push到series里处理,你说的:每个http请求对应一个server_task和一个series,例子里我没有看到这样的,而且,作为http server,一个http请求对应一个server task这样合理吗?

server内部每接受到一个完整request,就创建一个server task,并创建一个series处理(process)这个task。并不是一个server对应一个series,而是一个server task对应一个series。可以把这个series理解成server task的处理流程。
server任务和client任务的区别是,server任务是框架创建,client任务是用户主动创建的。每个http请求对应一个server task其实是合理的。

那按照上面说的:server内部每接受到一个完整request,就创建一个server task,并创建一个series处理(process)这个task,这里的series也是由框架自己创建的吗,之前其他问题里有说封装了一个blockserices来处理高频连续请求,就是创建一个blockserices单例,统一管理和处理请求,这个在htppserver里不适用,是吗?

之前的block series和这个没有关系,那个是user自己创建的series,用来处理连续的client或计算任务,和server没关系。 server task的series是由框架创建的。
当然,你也可以通过类似的方式,block住server的series,方法是一样的,产生一个counter task,push_back到这个series里,需要打开的时候,手动count一下。

server task的series是由框架创建的,server task和series我在哪里能看到并管理呢?

在这个地方:

new Series(this);

其实你没有必要关心这个的创建,在使用上是你自己创建的series是没有区别的。

嗯,我的需求是这样的:1.http请求响应速度要快2.服务端在收到请求后,需要经过一系列操作完成后,才能发送response,目前对于咱们的框架还在学习中,不知道如何使用才能达到高性能的要求,请指教下!多谢!

你这个就是我们最典型的应用啊。一系列操作每个都是一个task,push_back到series里,然后这些task都执行完,你把response填好,就自动回复了……

处理一系列操作这个series是自己创建的吧,series里的任务完成后,如何通知到process去添加response呢?

不是自己创建的啊,就是server task的那个series。
proxy或file_server不都是这种用法吗?就是往这个series里串任务。
SeriesWork *series = series_of(server_task);
明白?
我们回复的时机,不是process函数结束。回复的时机,是server task所在的series结束。我在�FAQ里写了的。

要不然server_task的那个series有什么用?

好的,我再研究下

再给你一个延迟一秒返回hello world的server实现,看看是不是能理解了:

#include <stdio.h>
#include "workflow/WFTaskFactory.h"
#include "workflow/WFHttpServer.h"

int main()
{
    WFHttpServer server([](WFHttpTask *task) {
        task->get_resp()->append_output_body("<html>Hello World!</html>");
        series_of(task)->push_back(WFTaskFactory::create_timer_task(1000000, nullptr));
    });

    if (server.start(8888) == 0) {  // start server on port 8888
        getchar(); // press "Enter" to end.
        server.stop();
    }

    return 0;
}

series结束回复,一定是记得这事,不要在process里等待。

嗯呢,更清楚一些了

可以的话点个星吧:)

点了,如此耐心专业的解答必须点赞

感谢使用!有什么问题随时交流。

服务端收到一个请求,base64编码的图片,收到之后decode一下,然后存储为文件,这样的操作,创建什么任务合适?go_task吗?

对,如果decode过程占cpu还比较多,简单的可以用可go_task来decode。如果decode不占多少cpu,process里直接算也可以。结束了之后用一个pwrite任务写磁盘。
现在pwrite任务已经可以这么创建了:
WFFileIOTask *create_pwrite_task(const std::string& filepath, const void *buf, size_t size, off_t offset, fio_callback_t callback);
直接给个文件名就可以了,文件都会帮你创建。

如何从请求里拿到消息体内容呢

void process(WFHttpTask *task)
{
    protocol::HttpRequest *req = task->get_req();
    const void *body;
    size_t size;

    if (req->get_parsed_body(&body, &size))
    {
    }
}

对于response也同理。这个body不会自动解chunk编码。解chunk编码可以用chunk cursor。
多看一看文档和FAQ啊。

void process(WFHttpTask *task)是线程安全的吗?实际使用中出现了一个崩溃,不确定是否和这有关系,请回答下
image

process是并发运行的。没有什么线程安全或不安全的概念。如果你在process里访问你自己需要互斥的资源,是需要加锁的。