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

ASGI support? #2902

Closed
sirex opened this issue Apr 2, 2018 · 44 comments
Closed

ASGI support? #2902

sirex opened this issue Apr 2, 2018 · 44 comments
Labels

Comments

@sirex
Copy link

sirex commented Apr 2, 2018

Long story short

Currently, most of asyncio based web frameworks embeds http server into the web framework. If http/ws servers would use ASGI standard, then it would be possible to run your app using different http/ws servers.

That is why I think it is important for aiohttp to add ASGI support.

Expected behaviour

I would like to write aiohttp app:

from aiohttp import web

async def hello(request):
    return web.Response(text="Hello world!")

app = web.Application()
app.add_routes([web.get('/', hello)])

And run it with any ASGI compatible server:

> daphne app:app
> http -b get :8080/
Hello world!

Actual behaviour

> daphne app:app
2018-04-02 12:48:51,097 ERROR    Traceback (most recent call last):
  File "daphne/http_protocol.py", line 158, in process
    "server": self.server_addr,
  File "daphne/server.py", line 184, in create_application
    application_instance = self.application(scope=scope)
TypeError: __call__() got an unexpected keyword argument 'scope'

127.0.0.1:41828 - - [02/Apr/2018:12:48:51] "GET /" 500 452
> http -b get :8080/
<html>
  <head>
    <title>500 Internal Server Error</title>
  </head>
  <body>
    <h1>500 Internal Server Error</h1>
    <p>Daphne HTTP processing error</p>
    <footer>Daphne</footer>
  </body>
</html>

ASGI resources

https://github.com/django/asgiref/blob/master/specs/asgi.rst - ASGI specification.

https://github.com/django/asgiref/blob/master/specs/www.rst - ASGI-HTTP and ASGI-WebSocket protocol specifications.

Example ASGI app:

import json

def app(scope):
    async def channel(receive, send):
        message = await receive()

        if scope['method'] == 'POST':
            response = message
        else:
            response = scope

        await send({
            'type': 'http.response.start',
            'status': 200,
            'headers': [
                [b'Content-Type', b'application/json'],
            ],
        })
        await send({
            'type': 'http.response.body',
            'body': json.dumps(response, default=bytes.decode).encode(),
        })
        await send({
            'type': 'http.disconnect',
        })
    return channel
> daphne app:app
2018-03-31 22:28:10,823 INFO     Starting server at tcp:port=8000:interface=127.0.0.1
2018-03-31 22:28:10,824 INFO     HTTP/2 support enabled
2018-03-31 22:28:10,824 INFO     Configuring endpoint tcp:port=8000:interface=127.0.0.1
2018-03-31 22:28:10,825 INFO     Listening on TCP address 127.0.0.1:8000
127.0.0.1:43436 - - [31/Mar/2018:22:28:17] "GET /" 200 347
127.0.0.1:43440 - - [31/Mar/2018:22:28:22] "POST /" 200 43
127.0.0.1:43446 - - [31/Mar/2018:22:28:42] "POST /" 200 54
> http -b get :8000/
{
    "type": "http"
    "http_version": "1.1",
    "method": "GET",
    "path": "/",
    "query_string": "",
    "root_path": "",
    "scheme": "http",
    "headers": [
        ["host", "localhost:8000"],
        ["user-agent", "HTTPie/0.9.9"],
        ["accept-encoding", "gzip, deflate"],
        ["accept", "*/*"],
        ["connection", "keep-alive"]
    ],
    "client": ["127.0.0.1", 43360],
    "server": ["127.0.0.1", 8000],
}

> http -b -f post :8000/ foo=bar
{
    "body": "foo=bar",
    "type": "http.request"
}

> http -b -j post :8000/ foo=bar
{
    "body": "{\"foo\": \"bar\"}",
    "type": "http.request"
}
@thomaszdxsn
Copy link
Contributor

thomaszdxsn commented Apr 2, 2018

has any discussion about ASGI in Python community?

@asvetlov
Copy link
Member

asvetlov commented Apr 2, 2018

I recall #2035

Still don't see strong reasons for new code/standard maintenance burden.
If ASGI even stable enough? ASGI 2 is not compatible with ASGI 1 for example.

@asvetlov
Copy link
Member

asvetlov commented Apr 2, 2018

Another question is performance.
I expect very low numbers for ASGI in comparison with aiohttp

@sirex
Copy link
Author

sirex commented Apr 2, 2018

@andrewgodwin confirmed, that ASGI 2 is stable.

benoitc/gunicorn#1380 (comment)

@fafhrd91
Copy link
Member

fafhrd91 commented Apr 2, 2018

What is the point of ASGI? I understand reasons for wsgi, especially regarding time when it was introduced. But asgi seems like just maintenance burden for framework developers

@samuelcolvin
Copy link
Member

Agree spending time on asgi would seem like a waist of time given aiohttp has/is a robust http server itself.

Why not use http as the protocol to connect components rather than conceiving of a whole new spec.? Genuine question, I'm not saying there isn't a case for asgi, I just don't see what it is.

@andrewgodwin
Copy link

From my perspective, it is because WSGI's abstraction is useful but not good with long-running requests (long poll/websockets), which is why it's basically just WSGI-with-async these days, albeit with all the unicode edge cases tidied up and more than just a request event.

Much like when WSGI was first introduced, there's no real reason for any single server to support it - as you said, you already have a good server and application abstraction - but it's that it allows frameworks and servers to be swapped out rather than being tied to each other, which is why I'm pushing it mostly from the framework side of things.

@sirex
Copy link
Author

sirex commented Apr 2, 2018

If both, aiohttp and for example Sanic would support ASGI, I could run my Sanic app on aiohttp server. If Gunicorn would support ASGI, then I could run my aiohttp app on Gunicorn server. If there would be mod_asgi for Apache, then I could run aiohttp ap on mod_asgi. ASGI is needed for the same purposes as WSGI.

WSGI does not support and can't support WebSockets, while ASGI can support both, HTTP and WebSockets. Also ASGI integrates well with asyncio. For example you can easily implement streaming, without blocking other requests.

@fafhrd91
Copy link
Member

fafhrd91 commented Apr 2, 2018

does anyone care about running sanic app in aiohttp server? is this what users miss at the moment? or it is just idea of infinite flexibility of everything

@sirex
Copy link
Author

sirex commented Apr 2, 2018

@fafhrd91 I think it is more about the idea of infinite flexibility of everything. 🙂

@fafhrd91
Copy link
Member

fafhrd91 commented Apr 2, 2018

I am not against it :)
someone just need to pay for it, either it is time of maintainer or employer money

@hubo1016
Copy link
Contributor

hubo1016 commented Apr 2, 2018

It could be developed as a separated project - a bridge between aiohttp and ASGI. Take an app and create simulated ASGI interfaces or vice versa.

@samuelcolvin
Copy link
Member

http-asgi-http?

Sounds like a mess to me.

@fafhrd91
Copy link
Member

fafhrd91 commented Apr 2, 2018

@samuelcolvin for the sake of infinite flexibility you should embrace it :)

@samuelcolvin
Copy link
Member

Humm, ok. 🙃

@asvetlov
Copy link
Member

asvetlov commented Apr 3, 2018

I cannot buy the idea of "more work for sake of flexibility" without other visible benefits.
Please provide at least one concrete example where aiohttp can win from ASGI support -- or let's close the conversation.

@asvetlov
Copy link
Member

asvetlov commented Apr 3, 2018

Now you can run both sanic and aiohttp (and any other asyncio-compatible) servers in the single process, every service on own tcp port.
asyncio supports it out of the box, no need other abstractions.
No need for something special for websocket or http2 support -- just use nonblocking socket and build everything on top of it using asyncio abstractions, existing libraries for parsing/building stream data etc.
Why should I support yet another abstraction level? Keep in mind that every abstraction adds own limitations and reduces a speed -- it is true for ASGI as well.
I understand why Django needs ASGI but don't understand why native asynchronous framework needs it too.

@sirex
Copy link
Author

sirex commented Apr 3, 2018

Benefits are exactly the same as with WSGI.

Currently, while ASGI is not yet adopted by many frameworks and servers, there is no benefit for aiohttp, it is the other way around, ASGI would benefit from aiohttp if aiohttp would be an early adopter and would help to understand if ASGI is good enough and can become a PEP, as a successor to WSGI.

But if ASGI will be adopted by many frameworks and servers, aiohttp, without ASGI support would be simply out of game, because no one would want to use a web framework, that can only work with it's own build-in server. And no one would want to use an async HTTP/WebSockets server, that can only be used with it's own build in web framework.

But of course if you don't care about standards, and don't see ASGI benefits, then let's close the conversation.

@asvetlov
Copy link
Member

asvetlov commented Apr 3, 2018

ASGI is not a standard.
I don't care about the support of all existing standard candidates in the world.
Maybe ASGI will prove the vitality and benefits, maybe its fate is dying young -- let's see.

@asvetlov asvetlov closed this as completed Apr 3, 2018
@hubo1016
Copy link
Contributor

hubo1016 commented Apr 3, 2018

@sirex WSGI is invented when FastCGI is still heavily used, so it is invented to make web apps be compatible to both FastCGI and more advanced technologies.

Asyncio is already an advanced technology. And currently there is no way to let the asyncio interfaces work with legacy servers.

@samuelcolvin
Copy link
Member

I'm not sure what you mean by

And currently there is no way to let the asyncio interfaces work with legacy servers.

@andrewgodwin
Copy link

To everyone who keeps asking for ASGI support: please don't bug people about adding it like this. If it gets more adoption, and thus more people willing to work on it, maybe the picture will change, but demanding features from a project that brings them nothing is not great (I would do the same if you asked me for something random on Channels).

The first step for anything like this is writing a compatibility shim that would present it as an aiohttp application - that's something that can be done outside of either of our projects (channels or aiohttp) and then released separately. If someone is really keen enough to want to work on that, let me know and I would be happy to advise.

@asvetlov
Copy link
Member

asvetlov commented Apr 3, 2018

@andrewgodwin thanks for understanding.
Maybe later aiohttp will get ASGI support but now the task is not in a priority list.

@hubo1016
Copy link
Contributor

hubo1016 commented Apr 4, 2018

@samuelcolvin I mean a thread-based web server cannot drive an asyncio interface (like ASGI, an interface is some method which is called by the web server). Gunicorn and uwsgi replaces their workers to support them. Reverse proxies need a separated HTTP server. They do not work like WSGI which only needs a simple wrapper.

@jordaneremieff
Copy link

jordaneremieff commented May 13, 2018

I've started working on an example ASGI adapter for aiohttp based on the work in #2035 (updated for the current ASGI specification).

It can be viewed in the example app here: https://github.com/erm/asgi-examples/

@imbolc
Copy link
Contributor

imbolc commented Sep 9, 2018

Please provide at least one concrete example where aiohttp can win from ASGI support

@asvetlov hope you meant if users of aiohttp can win? :)

Here's one major reason - the more common layers would we have - the less fragmented people effords can be. Imagine that maintainers of japronto or vibora would spent time on writing some underlying layer, rather than build whole framework. The layer that can be used by aiohttp, sanic, apistar, maybe even django users. So here's win-win: their projects would get much more attention and aiohttp users would get benefits (speedup in this case) just for free.

One more reason - common services / middlewares - not sure how important is it, haven't seen much of them in WSGI world.

But I'm here for another reason, just read your (@asvetlov) post on "Architectural problems of Sanic framework". Do you see any problems with asgi approach?

@asvetlov
Copy link
Member

I very doubt if ASGI is a useful abstraction.
It uses dicts and lists as an interface data structure, any library should wrap them into own classes.
It is an obvious slow-down, especially for vibora which tries to use C (Cython actually) data structures for such things.

Common services / middlewares doesn't exist in WSGI world, why they will appear in ASGI?
Django middleware is not compatible with any another WSGI framework.

Regarding to flow control in ASGI -- I didn't dig too deep. Perhaps it is fine, don't know.

@imbolc
Copy link
Contributor

imbolc commented Sep 10, 2018

I very doubt if ASGI is a useful abstraction.

Why not? Here's already at least two ASGI servers (uvicorn and daphne). And couple of frameworks (apistar looks pretty mature) on top of them.

It is an obvious slow-down

The question is how big is it, just tested on my laptop simple hello world:

  • uvicorn: Requests/sec: 12891
  • apistar (on uvicorn): 8700
  • starlette (on uvicorn): 9534
  • aiohttp: 6068
  • sanic: 7026
  • vibora: (single worker): 128713

The first three - are ASGI frameworks. About viobra, agreed it's barely would use ASGI. But all the other frameworks are showing comparable speed, why do you think that dicts / lists layer will slowdown aiohttp or sanic any significantly?

@fafhrd91
Copy link
Member

Helo world example doesn’t matter. All modern python frameworks uses c-optimized extensions for basic operations. But if you add actual business logic written in python, there would be no difference.

Reusable wsgii middleware are magical creatures. I guess it would be the same with asgi

@imbolc
Copy link
Contributor

imbolc commented Sep 10, 2018

What world be better than hello-world If we're measuring dicts-lists-layer overhead?

@asvetlov
Copy link
Member

I have different numbers for aiohttp and sanic on my laptop :)

You are comparing apples with oranges.
The proper benchmark should implement for example sanic (or aiohttp) on top of uvicorn and measure the difference.

For example aiohttp uses multidict library for HTTP headers, uvicorn operates on list of pairs, apistar uses another structure. A part of apistar performance degradation in comparison with uvicorn itself is headers conversion.
The same for request's body etc.

@fafhrd91
Copy link
Member

I don’t think performance matter. I think asgi does not matter in 2018 in general. Usability and complexity matters. Python is not very good choice for high performance system in any case.

@imbolc
Copy link
Contributor

imbolc commented Sep 13, 2018

@asvetlov It uses dicts and lists as an interface data structure

Could you tell a few words about this when you have a minute? Why is it bad and what's the better option? Some lazy loaded structures?

@jordaneremieff
Copy link

jordaneremieff commented Sep 22, 2018

@asvetlov

I very doubt if ASGI is a useful abstraction.

I've found ASGI to be a very useful abstraction for development at the application level, much more than alternatives. I have generally been less concerned with performance and "hello world" benchmark hype that tend to dominate these discussions - which isn't to say ASGI servers and applications are not performant.

Common services / middlewares doesn't exist in WSGI world, why they will appear in ASGI?

There are common middlewares that exist for WSGI, though I'd say ASGI presents an opportunity to achieve a reusable middleware ecosystem in a way that WSGI did not.

Regardless, ASGI is not WSGI.

Django middleware is not compatible with any another WSGI framework.

Correct, but this is a particular framework implementation unrelated to the WSGI specification itself and is even less related to ASGI discussion. The composability of ASGI applications is much more flexible than WSGI for the purpose of creating framework-independent middleware. It is much easier to reuse ASGI applications across multiple framework implementations.

An example of this would be how Django Channels middlewares can easily be reused with an ASGI library such as Starlette with no complications.

@imbolc
Copy link
Contributor

imbolc commented Sep 22, 2018

@ERM how much useful middlewares you can imagine outside of application context?

@jordaneremieff
Copy link

jordaneremieff commented Sep 23, 2018

@imbolc Hard for me to say, I referenced the application-level in particular because that is where I am most familiar, but it looks like Uvicorn now provides some middlewares that could help answer that question. cc @tomchristie may be able to speak to this better than I.

Also, in regards to my previous comment, I would like to make clear my intention is only to present an individual perspective on ASGI to assist in evaluating it as a specification. If this project would not find it useful to support then it should do what is in the best interest of its maintainers/users - ASGI is just an option to pool developer knowledge and effort and help facilitate greater cooperation across Python frameworks (difficult to demonstrate currently, but things are progressing).

@tomchristie
Copy link

how much useful middlewares you can imagine outside of application context?

Examples would be:

  • DebugMiddleware (e.g. Werkzeug)
  • StaticFile serving (e.g. Whitenoise)
  • Error monitoring (e.g. Sentry)
  • CORS, CSP, CSRF, HTTPS-only, Same origin etc. policies.
  • JWT (Since it's stateless)
  • Signed cookie sessions.

There are also other sorts of composition it allows, eg. HTTP or WebSocket testing libraries that plug into any ASGI application.

Being able to switch out different servers means it's possible to isolate thorny issues to either being an application issue or a server issue. Plus if even faster cythonised implementations come along you can just seamlessly start using those.

Most critically tho is that it lowers the ecosystem complexity as a whole. There's a single clearly defined interface to work against. I can understand the application framework code in isolation, and be confident that it can run against multiple server implementations. (Or write an async "hello world" application without having to tie myself to an application framework.)

Regarding to flow control in ASGI -- I didn't dig too deep. Perhaps it is fine, don't know.

Pretty confident in saying that I believe all three of daphne, uvicorn, and hypercorn deal with flow control in both directions and high/low watermarks, yup.

The proper benchmark should implement for example sanic (or aiohttp) on top of uvicorn and measure the difference.

Starlette is essentially the same set of functionality as sanic or aiohttp. The TechEmpower benchmarks there demonstrate that it runs at least with equal performance characteristics. More context on those results here.

Obviously it'd be a pain for aiohttp to retrospectively adopt and I respect that decision, tho I think if it's simple a question of "would this benefit the community as a whole" then the answer is an unambiguous "yes"

@imbolc
Copy link
Contributor

imbolc commented Sep 24, 2018

Thanks, @tomchristie
Could you tell you rough estimation how much can cytonization improve uvicorn performance and how would it affect apistar speed? I'm wondering if something like Vibora can potentially adopt it?
And as you're dealing a lot with this, do you found any flaws of ASGI?

@tomchristie
Copy link

tomchristie commented Sep 24, 2018

Could you tell you rough estimation how much can cytonization improve uvicorn performance

There was some work on it here. encode/uvicorn#92 I don't think there's any good answer to that without implementing it and looking at various benchmarks.

Main point there would be...

  • aiohttp/sanic-like ASGI server implementations already have aiohttp/sanic-like performance.
  • vibora-like ASGI server implementations would likely have roughly vibora-like performance.

I'm wondering if something like Vibora can potentially adopt it?

If you mean "could Vibora be an ASGI framework?" Then sure, yes.

And as you're dealing a lot with this, do you found any flaws of ASGI?

Nothing significant, no. There's a little light refinement to some edges of the spec, but more from a point of view of strictly clarifying any under-specified aspects of behavior.

@fafhrd91
Copy link
Member

I still have same opinion as before. There is no need for one more middleware api, end users don’t care until framework does not interfere with their work. I believe this new api is just huge burden on maintainers. For me high performance python is a fantasy, but i don’t do aiohttp/python anymore. In the end it is up to @asvetlov

@imbolc
Copy link
Contributor

imbolc commented Sep 24, 2018

high performance python is a fantasy

In general its probably so, but for io-bound things like web apis I don't see why rust would be any faster. I've recently compared actix with vibora on a few db queries with json serialization and got the same speed.

@asvetlov
Copy link
Member

The issue is closed, isn't it?
Personally, I don't want to be an early adopter or maintainer of experimental standard integration.

@andrewgodwin
Copy link

Just to let you all know that I plan to bring ASGI to the PEP process soon - since we now have three separate implementations - so if you have any concrete concerns, that will be the place to raise them properly, rather than here.

@lock
Copy link

lock bot commented Oct 28, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a [new issue] for related bugs.
If you feel like there's important points made in this discussion, please include those exceprts into that [new issue].
[new issue]: https://github.com/aio-libs/aiohttp/issues/new

@lock lock bot added the outdated label Oct 28, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Oct 28, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

10 participants