Asynchronous Multiplexing HTTP2/DBO Requests
pip install aquests==0.30.2
Aquests is generating asynchronous requests and fetching data from HTTP2, REST API, Websocket, RPCs and several Database engines. This project was originally started for testing Skitai App Engine and seperated for efficient developing eaches on Jan 2017.
Supported requests are:
Table of Contents
For fetching single web page:
import aquests
aquests.get ("http://127.0.0.1:5000/")
aquests.fetchall ()
Its result is:
user$ REQID 0. HTTP/2.0 200 OK 4210 bytes received
Let's add more pages:
for i in range (3):
aquests.get ("http://127.0.0.1:5000/")
aquests.fetchall ()
Its result is:
REQID 0. HTTP/2.0 200 OK 4210 bytes received
REQID 1. HTTP/2.0 200 OK 4210 bytes received
REQID 2. HTTP/2.0 200 OK 4210 bytes received
Now increase fetching workers,
aquests.configure (3) # 3 workers
for i in range (3):
aquests.get ("http://127.0.0.1:5000/")
aquests.fetchall ()
Result is same as above but somewhat faster and REQID is not ordered.
Now increase workers and pages for making load,
aquests.configure (100) # 100 workers
for i in range (10000):
aquests.get ("http://127.0.0.1:5000/")
aquests.fetchall ()
Now result is,
REQID 3635. HTTP/2.0 200 OK 4210 bytes received
REQID 3627. HTTP/2.0 200 OK 4210 bytes received
REQID 3594. HTTP/2.0 200 OK 4210 bytes received
REQID 3702. HTTP/2.0 200 OK 4210 bytes received
REQID 3685. HTTP/2.0 200 OK 4210 bytes received
REQID 3637. HTTP/2.0 200 OK 4210 bytes received
REQID 3591. HTTP/2.0 200 OK 4210 bytes received
REQID 3586. HTTP/2.0 200 OK 4210 bytes received
(and scrolled fast...)
pip install aquests
At command line,
python3 -m aquests.load -n 100 -c 2 http://myserver.com
# for viewing help
python3 -m aquests.load --help
Note that is is similar to h2load HTTP2 bench marking tool, but aquests is not optimized for benchmarking.
def finish_request (response):
print (response.status_code)
print (response.content)
aquests.configure (workers = 10, callback = finish_request)
for i in range (10):
aquests.get ("http://127.0.0.1:5000/")
aquests.fetchall ()
numreq = 0
limit = 1000000
workers = 100
def finish_request (response):
global numreq, limit
if numreq < limit:
aquests.get ("http://127.0.0.1:5000/")
numreq += 1
aquests.configure (workers, callback = finish_request)
for i in range (workers):
aquests.get ("http://127.0.0.1:5000/")
numreq += 1
aquests.fetchall ()
def finish_request (response):
print (response.meta ['req_id'])
print (response.meta ['req_method'])
print (response.meta ['job_name'])
aquests.configure (workers = 10, callback = finish_request)
aquests.get ("http://127.0.0.1:5000/", meta = {'job_name': 'test1'})
aquests.get ("http://127.0.0.1:5000/", meta = {'job_name': 'test2'})
Note: meta ['req_id'], meta ['req_method'] and meta ['req_callback'] are reserved keys and automatically added by aquests. You SHOULDN'T use theses keys and actually it is better do not use key starts with 'req_'.
aquests.configure (20, timeout = 10) # 10 seconds
aquests.get ("https://www.google.co.kr/?gfe_rd=cr&ei=3y14WPCTG4XR8gfSjoK4DQ")
aquests.fetchall ()
If timeout occured, response status_code will be 702. Also note above 700 codes mostly indicates network related error.
Caution
You can set response validators using headers.
aquests.configure (20, timeout = 10) # 10 seconds
aquests.get (
"https://www.google.co.kr/?gfe_rd=cr&ei=3y14WPCTG4XR8gfSjoK4DQ",
headers = {
"Accept": 'text/html',
"Accept-Content-Length": 100000, # max 100Kb
}
)
aquests.fetchall ()
Accept-Content-Length is not standard HTTP header but used by aquests. aquests returns status code 718 for unaccpetable content and code 719 for too large content.
dbo = aquests.mongodb ("127.0.0.1:27017", "test_database")
aquests.configure (20)
for i in range (1000):
aquests.get ("http://127.0.0.1:5000/")
dbo.findone ("posts", {"author": "James Milton"})
aquests.fetchall ()
For requesting with basic/digest authorization:
stub = aquests.rpc (url, auth = (username, password))
stub.get_prime_number_gt (10000)
aquests.fetchall ()
If you provide both (username, password), aquests try basic/digest authorization. But if just (username,) aquests handle username as bearer token like API Key.
For automatically redireting by http status 301, 302, 307, 308:
def finish_request (response):
print (response.history)
aquests.configure (callback = finish_request)
aquests.get ('http://pypi.python.org')
aquests.fetchall ()
response.history is like,
[<Response [301]>, <Response [302]>]
Also for disabling redirect,
aquests.configure (callback = finish_request, allow_redirects = False)
def finish_request (response):
print (response.cookies)
aquests.configure (20, callback = finish_request, cookie = True)
aquests.get ("https://www.google.co.kr/?gfe_rd=cr&ei=3y14WPCTG4XR8gfSjoK4DQ")
aquests.fetchall ()
Caution
This cookie feature shouldn't handle as different sessions per worker. All workers (connections) of aquests share same cookie values per domain. It means a worker sign in a website, so are the others. Imagine lots of FireFox windows on a desktop computer. If you really need session control, use requests.
from aquests.lib import logger
aquests.configure (
workers = 10,
logger = logger.file_logger ('/tmp/logs', 'aquests')
)
I make similar naming with requests' attribute and method names as possible.
Response has these attributes and method:
Below thing is available only on Websocket response.
Below things are available only on DBO responses.
Below things aren't available on DBO and Websocket responses.
import aquests
aquests.configure (
workers = 1,
logger = None,
callback = None,
timeout = 10,
cookie = False,
force_http1 = False,
http2_constreams = 1,
allow_redirects = True,
qrandom = False,
use_pool = True,
dns = [],
tracking = False
)
aquests.get ("http://127.0.0.1:5000/")
aquests.delete ("http://127.0.0.1:5000/models/ak-40")
aquests.get ("https://www.google.co.kr/search?q=aquests")
Also aquests.head (), options () and trace () are available.
aquests.post (
"http://127.0.0.1:5000/",
{'author': 'James Milton'},
{'Content-Type': 'application/x-www-form-urlencoded'}
)
Put example,
aquest.put (
"http://127.0.0.1:5000/users/jamesmilton",
{'fullnamer': 'James Milton'},
{'Content-Type': 'application/json'}
)
)
# is equal to:
aquests.putjson (
"http://127.0.0.1:5000/users/jamesmilton",
{'fullnamer': 'James Milton'}
)
There're some shorter ways ratehr than specifing content type:
And putform (), putjson ()... is also available.
aquests.upload (
"http://127.0.0.1:5000/",
{
'author': 'James Milton',
'file': open ('/tmp/mycar.jpg', 'rb')
}
)
You should open file with 'rb' mode.
aquests.ws ("ws://127.0.0.1:5000/websocket/echo", "Hello World")
# secure websocket channel, use wss
aquests.ws ("wss://127.0.0.1:5000/websocket/echo", "Hello World")
aquests.fetchall ()
Response is like this,
Note: Sometimes status_code is 200, opcode is -1. It is NOT official websocket spec. but means websocket is successfully connected but disconnected before receving a message by some reasons.
If you want to send specify message type.
from aquests.protocols.ws import OPCODE_TEXT, OPCODE_BINARY
aquests.ws ("ws://127.0.0.1:5000/websocket/echo", (OPCODE_BINARY, b"Hello World"))
aquests.fetchall ()
Note: This method can only single message per request.
stub = aquests.rpc ("https://pypi.python.org/pypi")
stub.package_releases('roundup')
stub.prelease_urls('roundup', '1.4.10')
aquests.fetchall ()
Returns,
['1.5.1']
<class 'xmlrpc.client.Fault'> <Fault 1:...>
import route_guide_pb2
stub = aquests.grpc ("http://127.0.0.1:5000/routeguide.RouteGuide")
point = route_guide_pb2.Point (latitude=409146138, longitude=-746188906)
for i in range (3):
stub.GetFeature (point)
aquests.fetchall ()
Returns,
name: "Berkshire Valley Management Area Trail, Jefferson, NJ, USA"
location {
latitude: 409146138
longitude: -746188906
}
For more about gRPC and route_guide_pb2, go to gRPC Basics - Python.
def finish_request (response):
print (response.data)
aquests.configure (3, callback = finish_request)
dbo = aquests.postgresql ("127.0.0.1:5432", "mydb", ("test", "1111"))
for i in range (10):
dbo.execute ("SELECT city, prcp, temp_hi, temp_low FROM weather;")
Returns,
[
{'prcp': 0.25, 'temp_hi': 50, 'city': 'San Francisco', 'temp_lo': 46},
{'prcp': 0.0, 'temp_hi': 54, 'city': 'Hayward', 'temp_lo': 37}
]
dbo = aquests.mongodb ("127.0.0.1:27017", "test_database")
for i in range (3):
dbo.findone ("posts", {"author": "Steve Newman"})
dbo.findall ("posts", {"author": "Hans Roh"})
aquests.fetchall ()
Returns,
{
'starting_from': 0,
'number_returned': 1,
'cursor_id': 0,
'data': [
{
'_id': ObjectId('586a11f80d23915c7ec76f01'),
'author': 'Steve Newman',
'title': 'How to swim'
}
]
}
Available Functions
Note: User authorization is not supported yet.
dbo = aquests.redis ("127.0.0.1:6379")
dbo.get ("session-5ae675bc")
dbo.lrange ("user-saved-docs", 0, 3)
aquests.fetchall ()
Returns,
response-of-session-5ae675bc
[32534, 3453, 6786]
Possibly you can use all Redis commands.
Note: User authorization is not supported yet.
Usage is almost same with PostgreSQL. This service IS NOT asynchronous BUT just emulating.
dbo = aquests.sqlite3 ("sqlite3.db")
dbo.execute ("""
drop table if exists people;
create table people (name_last, age);
insert into people values ('Cho', 42);
""")
aquests.fetchall ()
For get, post*, put*, upload, delete, options, trace parameters are the same.
aquests.get (url, params = None, headers = None, auth = None, meta = {})
For Websocket,
aquests.ws (url, params = None, headers = None, auth = None, meta = {})
url: request url string, should start with 'ws://' or 'wss://'(SSL Websocket)
params: string, bytes or tuple. if messages is not string you specify message type code using tuple like (ws.OPCODE_PING, b"hello"), you can find OPCODE list, 'from aquests.protocols import ws'. CAUTION. if your params type is bytes type, opcode will automatically be OPCODE_BINARY and string type, be OPCODE_TEXT. and opcode is inffluent to receiver. if you avoid auto opcode, specify opcode with tuple.
- ws.OPCODE_TEXT
- ws.OPCODE_BINARY
- ws.OPCODE_CONTINUATION
- ws.OPCODE_PING
- ws.OPCODE_PONG
- ws.OPCODE_CLOSE
headers: None or dictionary
auth: None or tuple (username, password)
callback: substitude defaul callback
meta: dictionary
For rpc, grpc stub creation:
stub = aquests.rpc (url, headers = None, auth = None, meta = {})
stub = aquests.grpc (url, headers = None, auth = None, meta = {})
Note: stub's methods and parameters are defined by RPC service providers
For postgresql, mongodb, redis dbo creation:
dbo = aquests.postgresql (server, dbname = None, auth = None, meta = {})
dbo = aquests.mongodb (server, dbname = None, auth = None, meta = {})
dbo = aquests.redis (server, dbname = None, auth = None, meta = {})
Note: stub's methods and parameters are defined by database engines. Please read above related chapters But SQL based postgresql has only 1 method and parameters - execute(sql) or do(sql) just for your convinience.
0.7.19
0.7.18
0.7.16
0.7.15
0.7.13
0.7.12
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
- re-engineer await_fifo, http2_fifo
- add lib.evbus
- retry once if database is disconnected by keep-live timeout
- change screen_logger for easy to read traceback information
0.7.4:
0.7.3:
0.7.2:
0.7.1: fix dns cache case sensitivity
0.7:
0.6.14: add qrandom option for aquests.configure
0.6.13: fix response.json ()
0.6.11
0.6.10: add response.lxml
0.6.8: add protocols.__init__.py
0.6.7: change socket closing log message
0.6.6: fix asyncon active
0.6.4.2: license changed from BSD to MIT
0.6.4.1: fix await_fifo bug
0.6.3: fix lifetime, tmap
0.6.2: change queue list -> deque
0.6.1: fix websocket text data encoding
0.6:
0.5.2: remove ready_producer_fifo, this will be used only serverside
0.5.1: change from list to deque on producer_fifo
0.4.33: force_http1 applied to https
0.4.32: fix http.buffer.list_buffer class
0.4.30: add websocket message type detection
0.4.28: remove aquests.wss, use aquests.ws with url wss://...
0.4.25: fix select.select () divide and conquer
0.4.22: fix http2_constreams
0.4.21: fix http2 flow control window
0.4.20: add configure options: force_http1, http2_constreams
0.4.18: url / trailing
0.4.17: fix finding end of data on http2
0.4.16: fix http2 disconnecting behavior
0.4.10: fix xmlrpc stub url / trailing
0.4.9: changed response properties - request.method -> method, request.server -> server, request.dbname -> dbname and request.params -> params
0.4.4: add lib.athreads
0.4.2: fix http2 large content download
0.4.1: add a few examples
0.4: add timeout feature
0.3.10: fix http2 frame length validation, add cookie feature
0.3.8: fix dbo request shutdown behavior
0.3.1: add HEAD, OPTIONS, TRACE
0.3: fix installation error
0.2.13: change default display callback
0.2.10: fix xmlrpc