Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
xiaoshizijiayou opened this issue Aug 22, 2021 · 26 comments
Closed

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

xiaoshizijiayou opened this issue Aug 22, 2021 · 26 comments

Comments

@xiaoshizijiayou
Copy link

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

@Barenboim
Copy link
Contributor

你好。
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表示无连接。

@xiaoshizijiayou
Copy link
Author

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

@Barenboim
Copy link
Contributor

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()指定请求的方法。

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

@xiaoshizijiayou
Copy link
Author

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

@Barenboim
Copy link
Contributor

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

@xiaoshizijiayou
Copy link
Author

xiaoshizijiayou commented Aug 23, 2021

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

@Barenboim
Copy link
Contributor

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

@xiaoshizijiayou
Copy link
Author

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

@Barenboim
Copy link
Contributor

在这个地方:

new Series(this);

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

@xiaoshizijiayou
Copy link
Author

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

@Barenboim
Copy link
Contributor

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

@xiaoshizijiayou
Copy link
Author

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

@Barenboim
Copy link
Contributor

Barenboim commented Aug 23, 2021

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

@Barenboim
Copy link
Contributor

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

@xiaoshizijiayou
Copy link
Author

好的,我再研究下

@Barenboim
Copy link
Contributor

再给你一个延迟一秒返回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里等待。

@xiaoshizijiayou
Copy link
Author

嗯呢,更清楚一些了

@Barenboim
Copy link
Contributor

可以的话点个星吧:)

@xiaoshizijiayou
Copy link
Author

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

@Barenboim
Copy link
Contributor

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

@xiaoshizijiayou
Copy link
Author

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

@Barenboim
Copy link
Contributor

对,如果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);
直接给个文件名就可以了,文件都会帮你创建。

@xiaoshizijiayou
Copy link
Author

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

@Barenboim
Copy link
Contributor

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啊。

@xiaoshizijiayou
Copy link
Author

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

@Barenboim
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants