Discussion:
[uWSGI] Routing based on POST request body
Alex Railean
2018-08-06 21:33:38 UTC
Permalink
Hello,

I have a Python web-application that provides a REST-like API, which takes
commands and arguments in the form of JSON payloads POSTed to the server. It
runs as N worker processes (N=4 for now).

The application has several methods, some take longer than others to handle. I
am looking for a way to route requests to specific workers, such that some of
them only handle the quick requests, while others - only the "long" ones. Thus,
the system should stay responsive, as a couple of long-running requests (which
consumers expect to take a while) won't block everything else.


The documentation provides examples of routing, by host, uri, and other
parameters, but there's nothing to explain whether the router can look into the
request body and make a decision based on that.


Is this feasible? If so, what are the adequate uwsgi mechanisms for
accomplishing that?

If not, what is the best alternative way to make it happen?


Looking forward to your feedback,
Alex
Curtis Maloney
2018-08-06 23:59:48 UTC
Permalink
Post by Alex Railean
Hello,
I have a Python web-application that provides a REST-like API, which takes
commands and arguments in the form of JSON payloads POSTed to the server. It
runs as N worker processes (N=4 for now).
The application has several methods, some take longer than others to handle. I
am looking for a way to route requests to specific workers, such that some of
them only handle the quick requests, while others - only the "long" ones. Thus,
the system should stay responsive, as a couple of long-running requests (which
consumers expect to take a while) won't block everything else.
The documentation provides examples of routing, by host, uri, and other
parameters, but there's nothing to explain whether the router can look into the
request body and make a decision based on that.
Sounds to me like you should be routing based on the Content-Length
header...

Perhaps try routing based on ${HTTP_CONTENT_LENGTH} ?

--
Curtis
Alex
2018-08-07 11:15:00 UTC
Permalink
Hi,
Post by Curtis Maloney
Sounds to me like you should be routing based on the Content-Length
header...
Perhaps try routing based on ${HTTP_CONTENT_LENGTH} ?
Thank you for the tip, this is a clever workaround, but unfortunately
I cannot rely on it. The size of the input variables can be different,
and some methods take a variable number of input arguments. Thus, the
length of the request is not a certain predictor of the method
invoked.

The method itself is the in the very beginning of the message, so if I
could peek the first few bytes, that would be sufficient:
{'GetTelemetry': {'data': {'type': 'user', 'id':
u'25b2f369-3ca5-4cdb-ab47-abc12343231'}}}
{'TransferEnergy': {'data': {'amount': 1945, 'target':
[u'25b2f369-3ca5-4cdb-ab47-abc12343231']}}}
{'TransferEnergy': {'data': {'amount': 1945, 'target':
[u'25b2f369-3ca5-4cdb-ab47-abc12343231',
u'11111-2222-3333-4444-abc12343231']}}}

The total number of methods is less than 10, so one can tell them
apart by looking at the first 4 characters in the body.

What other options could apply?
Curtis Maloney
2018-08-07 12:04:08 UTC
Permalink
Post by Alex
Hi,
Post by Curtis Maloney
Sounds to me like you should be routing based on the Content-Length
header...
Perhaps try routing based on ${HTTP_CONTENT_LENGTH} ?
Thank you for the tip, this is a clever workaround, but unfortunately
I cannot rely on it. The size of the input variables can be different,
and some methods take a variable number of input arguments. Thus, the
length of the request is not a certain predictor of the method
invoked.
I don't think this is going to work... I don't think routing can read a
_bit_ of the body only... and you'd lose all the benefits

When I do this sort of RPC, I put the method in a X-RPC-Action header
instead... this would work well for you, too

--
C
Alex
2018-08-07 13:19:06 UTC
Permalink
Post by Curtis Maloney
When I do this sort of RPC, I put the method in a X-RPC-Action header
This choice is enforced by the framework used in the project, so I
cannot change that. However, I can adjust the client-side, such that
the HTTP request headers also contain the method name. The problem is
that I didn't see how uwsgi can route based on request headers.

The doc lists the following criteria for routing:
- host (check HTTP_HOST)
- uri (check REQUEST_URI)
- qs (check QUERY_STRING)
- remote-addr (check REMOTE_ADDR)
- remote-user (check REMOTE_USER)
- referer (check HTTP_REFERER)
- user-agent (check HTTP_USER_AGENT)
- status (check HTTP response status code, not available in the request chain)
- default (default subject, maps to PATH_INFO)

My only option is to put the method into one of the existing fields,
for example the user-agent. This would work, though one could argue
that it is ideologically "ugly". Is there a way to look for a specific
header?




And second, I want to ask for some guidance about the routing itself -
the examples show how I can change the URL to which the request will
be sent. However, my objective is to route the request to a specific
worker process. Is this something that uwsgi can do, in principle?
Curtis Maloney
2018-08-07 13:30:51 UTC
Permalink
Post by Alex
Post by Curtis Maloney
When I do this sort of RPC, I put the method in a X-RPC-Action header
This choice is enforced by the framework used in the project, so I
cannot change that. However, I can adjust the client-side, such that
the HTTP request headers also contain the method name. The problem is
that I didn't see how uwsgi can route based on request headers.
I just tested locally:

./uwsgi --http :8000 --route-run 'log:${HTTP_X_RPC_ACTION}' --route-if
'equal:${HTTP_X_RPC_ACTION};test log:HERE' -M

Then ran:

curl -H "X-RPC-Action: test" http://localhost:8000/


And in the logs I got:

spawned uWSGI http 1 (pid: 25698)
test
HERE


So... you can test _any_ CGI var you expect to be there... any header.

As for routing to a specific worker... no, I'm not aware of how you
could do this.

--
Curtis

Continue reading on narkive:
Loading...