-
-
Notifications
You must be signed in to change notification settings - Fork 9.3k
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
100% processor usage during GET have to wait 60s for response #1910
Comments
Do you have a publicly-accessible URL that I can test against? |
More details: GET is fired by sub-process (multiprocessing) - GET is calling API exposed by GSM operator - by this API I am receiving SMS and MMS. Other tasks fired by sub-proceses are using CPU below 1% (server with 2 Xeons). |
Hmm. In that case, can you show me your code? Feel free to remove identifying information (like URLs, header values etc.) |
Here You are - direct call of requests.get from my library p_par = { p_uri_part = 'getsms.aspx' def __request_api2(self, p_par, p_uri_part=None): ,verify='{0}ca_root_certificates.pem'.format(self.__PATH_RES_SEC) ca_root_certificates.pem - is grabbed from Mozilla - CA bundle Best regards, |
GitHub's email formatting made that pretty difficult to read, so I copy-pasted it into a code block here: p_par = {
'login': 'xxx',
'password': 'xxx',
'serviceId': '0000',
'timeout': 60000, # wait 60s for response
'deleteContent': 'true',
'manualconfirm': 'true'
}
p_uri_part = 'getsms.aspx'
def __request_api2(self, p_par, p_uri_part=None):
try:
# GSM
if self.__channel == '0':
v_url = '{0}/{1}'.format(self.__API_0_URL, p_uri_part)
v_crt = '{0}smsapi.crt'.format(self.__PATH_RES_SEC)
v_key = '{0}smsapi.key'.format(self.__PATH_RES_SEC)
try:
v_rsp = requests.get(
v_url, params=p_par
,cert=(v_crt, v_key)
,timeout=120
,verify='{0}ca_root_certificates.pem'.format(self.__PATH_RES_SEC)
)
v_res = v_rsp.text
except Exception as e:
self.__add_error('Error when invoking API: {0}'.format(str(e)))
v_res = 0
finally:
return v_res |
Hmm, there's nothing obviously wrong with that. When I get a moment I'll try to reproduce something similar on my machine. |
I am in my phone, so I'll be brief. I suspect this is pyopenssl interacting with tje timeout. Especially the makefile function in urllib3.contrib.pyopenssl busylooping when no data is being recieved |
Wait, does it do that? That's pretty awful. |
This is a guess (I think I stumbled upon this previously). I'll check it later. Maybe gevent is also an issue. Gevent and pyopenssl don't really work together. |
Hm, I can't reproduce this at the moment. @e-manuel Could you give more information about your environment:
|
|
Do you also have |
Yes, I have:
|
So I managed to reproduce it: Terminal 1: # netcat from the nmap project, should work with all ssl capable netcats
# We complete the SSL handshake but don't send a HTTP response line!
$ ncat --ssl -l 0.0.0.0 4000 Code: request.get('https://127.0.0.1:4000', verify=False, timeout=120) It seems this only happens when using PyOpenSSL, timeouts and FreeBSD. @e-manuel Pick your poison :-) |
Manually waiting for the buffer to be ready seems to fix it: diff --git a/requests/packages/urllib3/contrib/pyopenssl.py b/requests/packages/urllib3/contrib/pyopenssl.py
index d9bda15..67eeee4 100644
--- a/requests/packages/urllib3/contrib/pyopenssl.py
+++ b/requests/packages/urllib3/contrib/pyopenssl.py
@@ -156,6 +156,7 @@ class fileobject(_fileobject):
try:
data = self._sock.recv(rbufsize)
except OpenSSL.SSL.WantReadError:
+ select.select([self._sock], [], [])
continue
if not data:
break
@@ -183,6 +184,7 @@ class fileobject(_fileobject):
try:
data = self._sock.recv(left)
except OpenSSL.SSL.WantReadError:
+ select.select([self._sock], [], [])
continue
if not data:
break
@@ -234,6 +236,7 @@ class fileobject(_fileobject):
break
buffers.append(data)
except OpenSSL.SSL.WantReadError:
+ select.select([self._sock], [], [])
continue
break
return "".join(buffers)
@@ -244,6 +247,7 @@ class fileobject(_fileobject):
try:
data = self._sock.recv(self._rbufsize)
except OpenSSL.SSL.WantReadError:
+ select.select([self._sock], [], [])
continue
if not data:
break
@@ -271,7 +275,8 @@ class fileobject(_fileobject):
try:
data = self._sock.recv(self._rbufsize)
except OpenSSL.SSL.WantReadError:
- continue
+ select.select([self._sock], [], [])
+ continue
if not data:
break
left = size - buf_len (Insert lengthy rant about platform differences, especially concerning openssl here) |
Yeah, platforms stink. Let's all develop on Windows. =P |
I ranted about this to my housemate last night, by the by. I don't understand why PyOpenSSL busy-waits on this socket instead of calling select like a good person. @t-8ch are you making changes in the urllib3 version, or are you going to submit your patch upstream? |
I would like to ask what next - there will be new release of requests ? I would not go back to pycurl :( |
@e-manuel all of the changes are in PyOpenSSL. Since you're using the packages from your distro, you'll need to bother the maintainer of that package when it gets released. @Lukasa your question is especially relevant since FreeBSD seems to strip out the vendored packages so it should be sent upstream if possible. |
Yeah, I think we want an answer on what PyOpenSSL is going to do. @e-manuel In the meantime, you can patch your copy so this stops happening. |
@t-8ch thank You very much - Your patch works great, now when GET starts it uses below 2% of CPU and after few seconds less :) |
Hurrah! Let's leave this open until we get an idea of what's happening upstream. @t-8ch saves the day again! I should buy him a boat. |
But you loose the timeouts I fear (althoug I'm not sure, if timeouts ever worked with pyopenssl). |
Timeouts works now in this manner:
|
Hmm. Can we pass the timeout to the select call? |
Yep. |
@pasha-r That's a very neat solution, it didn't even occur to me. That, plus checking the return code from select, should add timeouts back. |
I think we should indeed go with our calling This should work: diff --git a/requests/packages/urllib3/contrib/pyopenssl.py b/requests/packages/urllib3/contrib/pyopenssl.py
index d9bda15..da44a29 100644
--- a/requests/packages/urllib3/contrib/pyopenssl.py
+++ b/requests/packages/urllib3/contrib/pyopenssl.py
@@ -43,7 +43,7 @@ from ndg.httpsclient.subj_alt_name import SubjectAltName as BaseSubjectAltName
import OpenSSL.SSL
from pyasn1.codec.der import decoder as der_decoder
from pyasn1.type import univ, constraint
-from socket import _fileobject
+from socket import _fileobject, timeout
import ssl
import select
from cStringIO import StringIO
@@ -139,6 +139,13 @@ def get_subj_alt_name(peer_cert):
class fileobject(_fileobject):
+ def _wait_for_sock(self):
+ rd, wd, ed = select.select([self._sock], [], [],
+ self._sock.gettimeout())
+ if not rd:
+ raise timeout()
+
+
def read(self, size=-1):
# Use max, disallow tiny reads in a loop as they are very inefficient.
# We never leave read() with any leftover data from a new recv() call
@@ -156,6 +163,7 @@ class fileobject(_fileobject):
try:
data = self._sock.recv(rbufsize)
except OpenSSL.SSL.WantReadError:
+ self._wait_for_sock()
continue
if not data:
break
@@ -183,6 +191,7 @@ class fileobject(_fileobject):
try:
data = self._sock.recv(left)
except OpenSSL.SSL.WantReadError:
+ self._wait_for_sock()
continue
if not data:
break
@@ -234,6 +243,7 @@ class fileobject(_fileobject):
break
buffers.append(data)
except OpenSSL.SSL.WantReadError:
+ self._wait_for_sock()
continue
break
return "".join(buffers)
@@ -244,6 +254,7 @@ class fileobject(_fileobject):
try:
data = self._sock.recv(self._rbufsize)
except OpenSSL.SSL.WantReadError:
+ self._wait_for_sock()
continue
if not data:
break
@@ -271,7 +282,8 @@ class fileobject(_fileobject):
try:
data = self._sock.recv(self._rbufsize)
except OpenSSL.SSL.WantReadError:
- continue
+ self._wait_for_sock()
+ continue
if not data:
break
left = size - buf_len The code seems suboptimal but I'd like to try keeping this close to the version from the stdlib (where 99% of the fileobject are taken from) PS: It seems I was totally confused when not being able to reproduce this on Linux. |
Suits me. =) |
@t-8ch You are great - now timeout works fine in every case - and may CPUs can "sleep" calmly ;) |
This will be fixed in the next release of requests, so I'm closing this. Thanks for your help all! |
Hi there, sorry to jump into this discussion, but I'm switching some legacy url resolver mechanism from urllib2 to requests 2.2.1 . Although requests works much better, the server's CPU started to scale in an erratic manner, whereas in the past it was kind of constant under 10% usage. We resolve, |
It's possible that this is your bug, if you're accessing HTTPS urls and have |
Nope, I don't use |
There's definitely overhead when using You should consider having a single Otherwise, we're not aware of any high CPU issues and it'd be interesting to get diags if you still hit it. |
I have a similar issue. Basically there are cpu peaks after switching from urllib2 to request to make get and post petitions. Basically Im seeeing a lot of I'm a bit confused with respect of the default behaviour of the connection pool.
Thank you in advance |
|
Basically in the cpu peaks Im getting lots of |
There is some locking around the connection pool and the cookies dictionary, so it's certainly possible. |
Should use sock instead of its file descriptor. See more at https://github.com/kennethreitz/requests/issues/1910.
Should use sock instead of its file descriptor. See more at https://github.com/kennethreitz/requests/issues/1910.
When GET request have to wait 60s for remote service response, processor usage increases to 100% - version 1 of "requests" worked in this case better.
GET is configured with "cert" data and "timeout=120" over SSL connection.
The text was updated successfully, but these errors were encountered: