[Serve] Migrate from Flask.Request to Starlette Request (#12852)

This commit is contained in:
architkulkarni
2020-12-21 15:34:15 -06:00
committed by GitHub
parent 5b48480e29
commit 8b4b4bf0a2
26 changed files with 140 additions and 174 deletions
+15 -22
View File
@@ -117,7 +117,7 @@ policies <serve-split-traffic>`, finding the next available replica, and
batching requests together.
When the request arrives in the model, you can access the data similarly to how
you would with HTTP request. Here are some examples how ServeRequest mirrors Flask.Request:
you would with HTTP request. Here are some examples how ServeRequest mirrors Starlette.Request:
.. list-table::
:header-rows: 1
@@ -125,25 +125,25 @@ you would with HTTP request. Here are some examples how ServeRequest mirrors Fla
* - HTTP
- ServeHandle
- | Request
| (Flask.Request and ServeRequest)
| (Starlette.Request and ServeRequest)
* - ``requests.get(..., headers={...})``
- ``handle.options(http_headers={...})``
- ``request.headers``
* - ``requests.post(...)``
- ``handle.options(http_method="POST")``
- ``requests.method``
* - ``request.get(..., json={...})``
- ``request.method``
* - ``requests.get(..., json={...})``
- ``handle.remote({...})``
- ``request.json``
* - ``request.get(..., form={...})``
- ``await request.json()``
* - ``requests.get(..., form={...})``
- ``handle.remote({...})``
- ``request.form``
* - ``request.get(..., params={"a":"b"})``
- ``await request.form()``
* - ``requests.get(..., params={"a":"b"})``
- ``handle.remote(a="b")``
- ``request.args``
* - ``request.get(..., data="long string")``
- ``request.query_params``
* - ``requests.get(..., data="long string")``
- ``handle.remote("long string")``
- ``request.data``
- ``await request.body()``
* - ``N/A``
- ``handle.remote(python_object)``
- ``request.data``
@@ -157,9 +157,9 @@ you would with HTTP request. Here are some examples how ServeRequest mirrors Fla
.. code-block:: python
import flask
import starlette.requests
if isinstance(request, flask.Request):
if isinstance(request, starlette.requests.Request):
print("Request coming from web!")
elif isinstance(request, ServeRequest):
print("Request coming from Python!")
@@ -170,10 +170,10 @@ you would with HTTP request. Here are some examples how ServeRequest mirrors Fla
.. code-block:: python
handle.remote(flask_request)
handle.remote(starlette_request)
In this case, Serve will `not` wrap it in ServeRequest. You can directly
process the request as a ``flask.Request``.
process the request as a ``starlette.requests.Request``.
How fast is Ray Serve?
----------------------
@@ -187,13 +187,6 @@ You can checkout our `microbenchmark instruction <https://github.com/ray-project
to benchmark on your hardware.
Does Ray Serve use Flask?
-------------------------
Flask is only used as a web request object for servable to consume the data.
We actually use the fastest Python web server: `Uvicorn <https://www.uvicorn.org/>`_ as our web server,
alongside with the power of Python asyncio.
**Flask is ONLY the request object that we are using, Uvicorn (not flask) provides the webserver.**
Can I use asyncio along with Ray Serve?
---------------------------------------
Yes! You can make your servable methods ``async def`` and Serve will run them
+3
View File
@@ -33,6 +33,9 @@ Since Serve is built on Ray, it also allows you to scale to many machines, in yo
If you want to try out Serve, join our `community slack <https://forms.gle/9TSdDYUgxYs8SA9e8>`_
and discuss in the #serve channel.
.. note::
Starting with Ray version 1.3.0, Ray Serve backends must take in a Starlette Request object instead of a Flask Request object.
See the `migration guide <https://docs.google.com/document/d/1CG4y5WTTc4G_MRQGyjnb_eZ7GK3G9dUX6TNLKLnKRAc/edit?usp=sharing>`_ for details.
Installation
============
+3 -5
View File
@@ -19,10 +19,8 @@ Backends
Backends define the implementation of your business logic or models that will handle requests when queries come in to :ref:`serve-endpoint`.
In order to support seamless scalability backends can have many replicas, which are individual processes running in the Ray cluster to handle requests.
To define a backend, first you must define the "handler" or the business logic you'd like to respond with.
The handler should take as input a `Flask Request object <https://flask.palletsprojects.com/en/1.1.x/api/?highlight=request#flask.Request>`_.
The handler should return any JSON-serializable object as output. For a more customizable response type, the handler may return a
The handler should take as input a `Starlette Request object <https://www.starlette.io/requests/>`_ and return any JSON-serializable object as output. For a more customizable response type, the handler may return a
`Starlette Response object <https://www.starlette.io/responses/>`_.
In the future, Ray Serve will support `Starlette Request objects <https://www.starlette.io/requests/>`_ as input as well.
A backend is defined using :mod:`client.create_backend <ray.serve.api.Client.create_backend>`, and the implementation can be defined as either a function or a class.
Use a function when your response is stateless and a class when you might need to maintain some state (like a model).
@@ -32,7 +30,7 @@ A backend consists of a number of *replicas*, which are individual copies of the
.. code-block:: python
def handle_request(flask_request):
def handle_request(starlette_request):
return "hello world"
class RequestHandler:
@@ -40,7 +38,7 @@ A backend consists of a number of *replicas*, which are individual copies of the
def __init__(self, msg):
self.msg = msg
def __call__(self, flask_request):
def __call__(self, starlette_request):
return self.msg
client.create_backend("simple_backend", handle_request)
+1 -1
View File
@@ -23,7 +23,7 @@ Handle API
:members: remote, options
When calling from Python, the backend implementation will receive ``ServeRequest``
objects instead of Flask requests.
objects instead of Starlette requests.
.. autoclass:: ray.serve.utils.ServeRequest
:members:
+5 -5
View File
@@ -30,13 +30,13 @@ You can use the ``@serve.accept_batch`` decorator to annotate a function or a cl
This annotation is needed because batched backends have different APIs compared
to single request backends. In a batched backend, the inputs are a list of values.
For single query backend, the input type is a single Flask request or
For single query backend, the input type is a single Starlette request or
:mod:`ServeRequest <ray.serve.utils.ServeRequest>`:
.. code-block:: python
def single_request(
request: Union[Flask.Request, ServeRequest],
request: Union[starlette.requests.Request, ServeRequest],
):
pass
@@ -47,7 +47,7 @@ types:
@serve.accept_batch
def batched_request(
request: List[Union[Flask.Request, ServeRequest]],
request: List[Union[starlette.requests.Request, ServeRequest]],
):
pass
@@ -84,8 +84,8 @@ Ray Serve was able to evaluate them in batches.
What if you want to evaluate a whole batch in Python? Ray Serve allows you to send
queries via the Python API. A batch of queries can either come from the web server
or the Python API. Requests coming from the Python API will have the similar API
as Flask.Request. See more on the API :ref:`here<serve-handle-explainer>`.
or the Python API. Requests coming from the Python API will have a similar API
to Starlette Request. See more on the API :ref:`here<serve-handle-explainer>`.
.. literalinclude:: ../../../../python/ray/serve/examples/doc/tutorial_batch.py
:start-after: __doc_define_servable_v1_begin__