简体   繁体   English

如何在Flask的回调中返回HTTP响应,或者甚至是否重要?

[英]How do I return an HTTP response in a callback in Flask, or does it even matter?

I am familiar with evented servers but not threaded ones. 我熟悉事件服务器但不熟悉线程服务器。 A common feature of REST APIs implemented in evented systems like node.js+express or tornado is, in an handler, to do some I/O work asynchronously in a callback, then return the actual HTTP response in the callback . 的REST API的一个共同特征的事件触发系统,如Node.js的+明示或龙卷风是,在处理程序,异步做回调某些I / O工作,然后返回在回调的实际HTTP响应来实现。 In Express, we have things like: 在Express中,我们有类似的东西:

app.post('/products', function (req, res) {
  Product.create(req.body, function (err, product) {
    if (err) return res.json(400, err);
    res.send(201, product);
  });
});

where Post.create hits the database and calls the callback after the product is persisted. 其中Post.create命中数据库并在产品持久化后调用回调。 The response, whether 201 or 400 is sent in the callback. 无论是201还是400,都会在回调中发送响应。 This keeps the server freed up to do other things while the database is working, but from the point of view of the client, the request seems to take a while. 这使得服务器在数据库工作时可以自由地执行其他操作,但从客户端的角度来看,请求似乎需要一段时间。

Suppose I wanted to do the same thing in Flask (which is not an evented server). 假设我想在Flask中做同样的事情(这不是一个事件服务器)。 If I have a POST handler to create several objects that needs to make several database writes that could take several seconds to complete, it seems I have two choices: 如果我有一个POST处理程序来创建几个需要进行多次数据库写入的对象,可能需要几秒钟才能完成,我似乎有两个选择:

  1. I could immediately return a 202 ACCEPTED but then this burdens the client with having to check back to see whether all the writes were committed. 我可以立即返回202 ACCEPTED,但这会给客户带来负担,不得不检查是否所有写入都已提交。

  2. I could just implement all the database writes directly inside the handler. 我可以直接在处理程序中实现所有数据库写入。 The client will have to wait the few seconds for the reply, but it's synchronous and simple from the client perspective. 客户端将不得不等待几秒钟的回复,但从客户端的角度来看,它是同步和简单的。

My question is whether if I do #2, is Flask smart enough to block the current request thread so that other requests can be handled during the database writes? 我的问题是,如果我做#2,Flask是否足够智能阻止当前请求线程,以便在数据库写入期间可以处理其他请求? I would hope the server doesn't block here. 我希望服务器不会阻止这里。

BTW I have done long polling, but this is for a public REST API where clients expect simple requests and responses, so I think either approach 1 or 2 is best. 顺便说一下,我做了很长时间的轮询,但是这是一个公共REST API,客户期望简单的请求和响应,所以我认为方法1或2是最好的。 Option 1 seems rare to me, but I am worried about #2 blocking the server? 选项1对我来说似乎很少见,但我担心#2会阻塞服务器吗? Am I right to be concerned, or is Flask (and threaded servers) just smart so I need not worry? 我是对的,或者Flask(和线程服务器)是否聪明,所以我不用担心?

Blocking vs. non-blocking 阻止与非阻塞

Flask itself (much like express) is not inherently blocking or non-blocking - it relies on the underlying container to provide the features necessary for operation (reading data from the user and writing responses to the user). Flask本身(非常像express)本身并不是阻塞或非阻塞 - 它依赖于底层容器来提供操作所需的功能(从用户读取数据并向用户写入响应)。 If the server does not provide an event loop (eg mod_wsgi) then Flask will block. 如果服务器没有提供事件循环(例如mod_wsgi),那么Flask将阻止。 If the server is a non-blocking one (eg gunicorn) then Flask will not block. 如果服务器是非阻塞服务器(例如gunicorn),那么Flask将不会阻止。

On the other end of things, if the code that you write in your handlers is blocking Flask will block, even if it is run on a non-blocking container . 另一方面,如果您在处理程序中编写的代码阻止Flask将阻止, 即使它是在非阻塞容器上运行

Consider the following: 考虑以下:

app.post('/products', function (req, res) {
  var response = Product.createSync(req.body);
  // Event loop is blocked until Product is created
  if (response.isError) return res.json(400, err);
  res.send(201, product);
});

If you run that on a node server you will quickly bring everything to a screeching halt. 如果您在节点服务器上运行它,您将很快将所有内容都戛然而止。 Even though node itself is non-blocking your code is not and it blocks the event loop preventing you from handling any other request from this node until the loop is yielded at res.json or res.send . 即使节点本身是非阻塞的,您的代码也不会阻塞事件循环, 阻止您处理来自此节点的任何其他请求,直到在res.jsonres.send处产生循环 Node's ecosystem makes it easy to find non-blocking IO libraries - in most other common environments you have to make a conscious choice to use non-blocking libraries for the IO you need to do. Node的生态系统可以很容易地找到非阻塞IO库 - 在大多数其他常见环境中,您必须有意识地选择将非阻塞库用于您需要执行的IO。

Threaded servers and how they work 线程服务器及其工作原理

Most non-evented containers use multiple threads to manage the workload of a concurrent system. 大多数非事件容器使用多个线程来管理并发系统的工作负载。 The container accepts requests in the main thread and then farms off the handling of the request and the serving of the response to one of its worker threads. 容器接受主线程中的请求,然后处理请求的处理以及对其一个工作线程的响应服务。 The worker thread executes the (most often blocking) code necessary to handle the request and generate a response. 工作线程执行处理请求所需的(最常阻塞的)代码并生成响应。 While the handling code is running that thread is blocked and cannot take on any other work. 处理代码正在运行时,该线程被阻止,无法承担任何其他工作。 If the request rate exceeds the total thread pool count then clients start backing up, waiting for a thread to complete. 如果请求率超过总线程池数,则客户端开始备份,等待线程完成。

What's the best thing to do with a long-running request in a threaded environment? 在线程环境中长时间运行的请求最好的做法是什么?

Knowing that blocking IO blocks one of your workers, the question now is "how many concurrent users are you expecting to have?" 知道阻止IO阻止你的一个工人,现在的问题是“你期望有多少并发用户?” (Where concurrent means "occur over the span of time it takes to accept and process one request") If the answer is "less than the total number of threads in my worker thread pool" then you are golden - your server can handle the load and it's non-blocking nature is in no way a threat to stability. (并发意味着“在接受和处理一个请求所花费的时间跨度内发生”)如果答案是“小于我的工作线程池中的线程总数”那么你就是黄金 - 你的服务器可以处理负载它的非阻塞性质决不会对稳定性构成威胁。 Choosing between #1 and #2 is largely a matter of taste. 在#1和#2之间选择主要是品味问题。

On the other hand, if the answer to the above question is "more than the total number of works in my thread pool" then you will need to handle the requests by passing off the user's data to another worker pool (generally via a queue of some kind) and responding to the request with a 202 (Option #1 in your list). 另一方面,如果上述问题的答案是“超过我的线程池中的工作总数”,那么您将需要通过将用户的数据传递给另一个工作池来处理请求(通常通过队列某种)并用202响应请求(列表中的选项#1)。 That will enable you to lower the response time, which will, in turn, enable you to handle more users. 这将使您能够缩短响应时间,从而使您能够处理更多用户。

TL;DR TL; DR

  • Flask is not blocking or non-blocking as it does no direct IO Flask没有阻塞或没有阻塞,因为它没有直接的 IO
  • Threaded servers block on the request / response handling thread, not the accept request thread 线程服务器阻塞请求/响应处理线程,而不是接受请求线程
  • Depending on the expected traffic you will almost certainly want to go go with option #1 (return a 202 and push the work into a queue to be handled by a different thread pool / evented solution). 根据预期的流量,您几乎肯定希望使用选项#1(返回202并将工作推送到队列以由不同的线程池/事件解决方案处理)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM