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-socket does not support Keep-Alive #1097

Closed
parasyte opened this issue Nov 1, 2015 · 13 comments · Fixed by eduNEXT/drydock#126
Closed

http-socket does not support Keep-Alive #1097

parasyte opened this issue Nov 1, 2015 · 13 comments · Fixed by eduNEXT/drydock#126

Comments

@parasyte
Copy link
Contributor

parasyte commented Nov 1, 2015

The Native HTTP Support does not support HTTP Keep-Alive. It's easy to test with uwsgi --http-socket :8080 and using telnet to send a valid HTTP/1.1 request; uwsgi will close the socket after sending the response.

A well-behaved HTTP/1.1 server will keep the socket open unless the client requests Connection: Close.

@Darvame
Copy link
Contributor

Darvame commented Nov 2, 2015

uwsgi --http-socket :8080 --http-keepalive --add-header "Connection: keep-alive"

@parasyte
Copy link
Contributor Author

parasyte commented Nov 2, 2015

@Darvame Perhaps you could try this yourself, because it does not work:

# wsgi.py
import gevent.monkey
gevent.monkey.patch_all()

import bottle

app = bottle.Bottle()

@app.route('/ping')
def ping():
    return {
        'message' : "pong",
    }

if __name__ == '__main__':
    bottle.run(
        app,
        server=bottle.GeventServer,
    )

Run the server:

$ uwsgi --http-socket :8080 --add-header "Connection: Keep-Alive" --http-keepalive --so-keepalive --gevent 100 --virtualenv env --module wsgi:app

Test keepalive:

$ time telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /ping HTTP/1.1
Host: localhost

HTTP/1.1 200 OK
Content-Length: 19
Content-Type: application/json
Connection: Keep-Alive

{"message": "pong"}Connection closed by foreign host.

real    0m0.444s
user    0m0.014s
sys     0m0.009s

The following invocation works as expected:

$ uwsgi --http :8080 --http-keepalive --so-keepalive --gevent 100 --virtualenv env --module wsgi:app

Test keepalive:

$ time telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /ping HTTP/1.1
Host: localhost

HTTP/1.1 200 OK
Content-Length: 19
Content-Type: application/json

{"message": "pong"}Connection closed by foreign host.

real    1m0.223s
user    0m0.013s
sys     0m0.008s

Here are a few backtraces from lldb (plain ol' Python and gevent plugin) showing where the close is occurring unconditionally:

Python: https://github.com/unbit/uwsgi/blob/2.0.11.2/core/loop.c#L149

(lldb) b close
Breakpoint 1: 5 locations.
(lldb) c
Process 59371 resuming
Process 59371 stopped
* thread #1: tid = 0x3a67e2, 0x00007fff838e29d8 libsystem_kernel.dylib`close, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
    frame #0: 0x00007fff838e29d8 libsystem_kernel.dylib`close
libsystem_kernel.dylib`close:
->  0x7fff838e29d8 <+0>:  movl   $0x2000006, %eax
    0x7fff838e29dd <+5>:  movq   %rcx, %r10
    0x7fff838e29e0 <+8>:  syscall
    0x7fff838e29e2 <+10>: jae    0x7fff838e29ec            ; <+20>
(lldb) bt
* thread #1: tid = 0x3a67e2, 0x00007fff838e29d8 libsystem_kernel.dylib`close, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
  * frame #0: 0x00007fff838e29d8 libsystem_kernel.dylib`close
    frame #1: 0x0000000100002ee2 uwsgi`close_and_free_request(wsgi_req=0x00000001003fb078) + 34 at utils.c:1006
    frame #2: 0x00000001000031af uwsgi`uwsgi_close_request(wsgi_req=0x00000001003fb078) + 639 at utils.c:1105
    frame #3: 0x000000010004e886 uwsgi`simple_loop_run(arg1=<unavailable>) + 230 at loop.c:149
    frame #4: 0x0000000100055e0b uwsgi`uwsgi_ignition + 443 at uwsgi.c:3537
    frame #5: 0x0000000100055c03 uwsgi`uwsgi_worker_run + 883 at uwsgi.c:3465
    frame #6: 0x000000010005362e uwsgi`uwsgi_run + 478 at uwsgi.c:3375
    frame #7: 0x00000001000512ae uwsgi`main(argc=<unavailable>, argv=<unavailable>, envp=<unavailable>) + 14 at uwsgi.c:2002
    frame #8: 0x00007fff9314d5ad libdyld.dylib`start + 1
    frame #9: 0x00007fff9314d5ad libdyld.dylib`start + 1

gevent: https://github.com/unbit/uwsgi/blob/2.0.11.2/plugins/gevent/gevent.c#L325

(lldb) b close
Breakpoint 1: 5 locations.
(lldb) c
Process 59862 resuming
Process 59862 stopped
* thread #1: tid = 0x3a80a5, 0x00007fff838e29d8 libsystem_kernel.dylib`close, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
    frame #0: 0x00007fff838e29d8 libsystem_kernel.dylib`close
libsystem_kernel.dylib`close:
->  0x7fff838e29d8 <+0>:  movl   $0x2000006, %eax
    0x7fff838e29dd <+5>:  movq   %rcx, %r10
    0x7fff838e29e0 <+8>:  syscall
    0x7fff838e29e2 <+10>: jae    0x7fff838e29ec            ; <+20>
(lldb) bt
* thread #1: tid = 0x3a80a5, 0x00007fff838e29d8 libsystem_kernel.dylib`close, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
  * frame #0: 0x00007fff838e29d8 libsystem_kernel.dylib`close
    frame #1: 0x0000000100002ee2 uwsgi`close_and_free_request(wsgi_req=0x0000000101be1618) + 34 at utils.c:1006
    frame #2: 0x00000001000031af uwsgi`uwsgi_close_request(wsgi_req=0x0000000101be1618) + 639 at utils.c:1105
    frame #3: 0x00000001000743ed uwsgi`py_uwsgi_gevent_request(self=<unavailable>, args=<unavailable>) + 349 at gevent.c:325
    frame #4: 0x000000010022e2a5 Python`PyEval_EvalFrameEx + 12479
    frame #5: 0x000000010022afb4 Python`PyEval_EvalCodeEx + 1387
    frame #6: 0x00000001001cfbf5 Python`function_call + 352
    frame #7: 0x00000001001b1ad7 Python`PyObject_Call + 99
    frame #8: 0x00000001001bc962 Python`instancemethod_call + 174
    frame #9: 0x00000001001b1ad7 Python`PyObject_Call + 99
    frame #10: 0x0000000100230e2e Python`PyEval_CallObjectWithKeywords + 93
    frame #11: 0x0000000101ff1b2a greenlet.so`g_initialstub + 1114
    frame #12: 0x0000000101ff13ad greenlet.so`g_switch + 333
    frame #13: 0x00000001001bc8b4 Python`instancemethod_hash + 97
    frame #14: 0x000000010029da10 Python`instancemethod_getset + 80

These lines also execute with --http :8080 (i.e. in the working case), but I assume it's closing the socket on the backend?

@Darvame
Copy link
Contributor

Darvame commented Nov 2, 2015

Yes, does not work for me too :C

@unbit
Copy link
Owner

unbit commented Nov 2, 2015

You need uWSGI 2.1 (master branch) and the --http11-socket option. Once set the server will try to maintain the connection opened if a bunch of rules are respected. This is not a smart http 1.1 parser (to avoid parsing the whole response) but assumes the developer is generating the right headers. It has been added to support RTSP protocol for video streaming.

@depaolim
Copy link
Contributor

depaolim commented Nov 6, 2015

Does this new option replace completely the old ones?

I used

./uwsgi --http11-socket :8080 --gevent 100 --virtualenv env --module wsgi:app

and I obtained this:

$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /ping HTTP/1.1

HTTP/1.1 200 OK
Content-Length: 19
Content-Type: application/json

{"message": "pong"}

without any "Connection closed by foreign host."

So it seems to work as a "Keep-alive" without any need of the old options

--add-header "Connection: Keep-Alive" --http-keepalive --so-keepalive

Does it mean that they are no more necessary?

@xrmx
Copy link
Collaborator

xrmx commented Nov 13, 2015

@depaolim i think --http11-socket may replace the add-header and http-keepalive options but i don't see it touching tcp stuff as so-keepalive does.

@xrmx
Copy link
Collaborator

xrmx commented Nov 15, 2015

Documentation has been updated thanks to @depaolim, closing this.

@xrmx xrmx closed this as completed Nov 15, 2015
@marc1n
Copy link

marc1n commented Apr 1, 2017

I confirm (by testing it) that "http-keepalive" option works only with "http" (not "http-socket") in uWSGI 2.0.

Why it is "http-keepalive" not working with "http-socket"? Is it any reason behind this?

The "http11-socket" is a future option (in uWSGI 2.1 which for now is not released yet) so in reality I cannot use HTTP Keep-Alive in uWSGI not paying penalty of "http" option overhead.

Btw, "http-keepalive" option value is a number which specify timeout (in seconds) after which an inactive connection is closed by server. How can I configure this timeout when I will use "http11-socket"?

@ushuz
Copy link

ushuz commented Apr 30, 2018

Seconding @marc1n, we observed the same problem. http-keepalive doesn't work with http-socket but work with http. There's also a stackoverflow question reporting the same.

http11-socket does enable keepalive, but it doesn't work well with gevent.


UPDATE (14 Nov 2018): http11-socket doesn't work well with non-gevent either, half workers got jammed after a while.

@jf
Copy link

jf commented Sep 15, 2020

Btw, "http-keepalive" option value is a number which specify timeout (in seconds) after which an inactive connection is closed by server. How can I configure this timeout when I will use "http11-socket"?

@marc1n what is your source for this? I see #2018, which seems to indicate that this is a boolean instead.

@marc1n
Copy link

marc1n commented Sep 15, 2020

@jf
Copy link

jf commented Sep 15, 2020

@jf From the source code: https://github.com/unbit/uwsgi/blob/uwsgi-2.0/plugins/http/http.c#L658

Excellent, thanks! I see it now. On another note, if anybody has any recommendations for another server to check out that has proper http keep-alive support, I'd be glad to hear it.

@xqliang
Copy link

xqliang commented Nov 22, 2022

@depaolim i think --http11-socket may replace the add-header and http-keepalive options but i don't see it touching tcp stuff as so-keepalive does.

--http-socket + --http-keepalive + add-header not worked, and --http11-socket works like --http + --http-keepalive + add-header,but the add-header is still needed for some clients(e.g. ab -k) to keep connection open(alive). BTW, use --socket-timeout(defaults to 4s) to set the keepalive timeout in seconds.

NOTE: Currently(uWSGI==2.0.21 2022.10.25) only --http + --http-keepalive + add-header works well with Nginx keepalive, #2133.

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

Successfully merging a pull request may close this issue.

9 participants