Performance improvements to response and moved tests around
This commit is contained in:
parent
74b0cbae1b
commit
6041e7cfa6
|
@ -17,36 +17,30 @@ STATUS_CODES = {
|
|||
503: 'Service Unavailable',
|
||||
504: 'Gateway Timeout',
|
||||
}
|
||||
|
||||
class HTTPResponse:
|
||||
__slots__ = ('body', 'status', 'content_type')
|
||||
|
||||
def __init__(self, body='', status=200, content_type='text/plain'):
|
||||
def __init__(self, body=None, status=200, content_type='text/plain', body_bytes=b''):
|
||||
self.content_type = content_type
|
||||
self.body = body
|
||||
|
||||
if not body is None:
|
||||
self.body = body.encode('utf-8')
|
||||
else:
|
||||
self.body = body_bytes
|
||||
|
||||
self.status = status
|
||||
|
||||
@property
|
||||
def body_bytes(self):
|
||||
body_type = type(self.body)
|
||||
if body_type is str:
|
||||
body = self.body.encode('utf-8')
|
||||
elif body_type is bytes:
|
||||
body = self.body
|
||||
else:
|
||||
body = b'Unable to interpret body'
|
||||
|
||||
return body
|
||||
|
||||
def output(self, version="1.1", keep_alive=False):
|
||||
body = self.body_bytes
|
||||
# This is all returned in a kind-of funky way
|
||||
# We tried to make this as fast as possible in pure python
|
||||
return b''.join([
|
||||
'HTTP/{} {} {}\r\n'.format(version, self.status, STATUS_CODES.get(self.status, 'FAIL')).encode('latin-1'),
|
||||
'Content-Type: {}\r\n'.format(self.content_type).encode('latin-1'),
|
||||
'Content-Length: {}\r\n'.format(len(body)).encode('latin-1'),
|
||||
'Connection: {}\r\n'.format('keep-alive' if keep_alive else 'close').encode('latin-1'),
|
||||
'HTTP/{} {} {}\r\n'.format(version, self.status, STATUS_CODES.get(self.status, 'FAIL')).encode(),
|
||||
b'Content-Type: ', self.content_type.encode(), b'\r\n',
|
||||
b'Content-Length: ', str(len(self.body)).encode(), b'\r\n',
|
||||
b'Connection: ', ('keep-alive' if keep_alive else 'close').encode(), b'\r\n',
|
||||
b'\r\n',
|
||||
body,
|
||||
#b'\r\n'
|
||||
self.body,
|
||||
])
|
||||
|
||||
def json(body, status=200):
|
||||
|
|
|
@ -95,19 +95,19 @@ class HttpProtocol(asyncio.Protocol):
|
|||
def on_body(self, body):
|
||||
self.request.body = body
|
||||
def on_message_complete(self):
|
||||
self.loop.create_task(self.get_response(self.request))
|
||||
self.loop.create_task(self.get_response())
|
||||
|
||||
# -------------------------------------------- #
|
||||
# Responding
|
||||
# -------------------------------------------- #
|
||||
|
||||
async def get_response(self, request):
|
||||
async def get_response(self):
|
||||
try:
|
||||
handler = self.sanic.router.get(request)
|
||||
handler = self.sanic.router.get(self.request)
|
||||
if handler is None:
|
||||
raise ServerError("'None' was returned while requesting a handler from the router")
|
||||
|
||||
response = handler(request)
|
||||
response = handler(self.request)
|
||||
|
||||
# Check if the handler is asynchronous
|
||||
if isawaitable(response):
|
||||
|
@ -115,20 +115,20 @@ class HttpProtocol(asyncio.Protocol):
|
|||
|
||||
except Exception as e:
|
||||
try:
|
||||
response = self.sanic.error_handler.response(request, e)
|
||||
response = self.sanic.error_handler.response(self.request, e)
|
||||
except Exception as e:
|
||||
if self.sanic.debug:
|
||||
response = HTTPResponse("Error while handling error: {}\nStack: {}".format(e, format_exc()))
|
||||
else:
|
||||
response = HTTPResponse("An error occured while handling an error")
|
||||
|
||||
self.write_response(request, response)
|
||||
self.write_response(response)
|
||||
|
||||
def write_response(self, request, response):
|
||||
def write_response(self, response):
|
||||
#print("response - {} - {}".format(self.n, self.request))
|
||||
try:
|
||||
keep_alive = self.parser.should_keep_alive()
|
||||
self.transport.write(response.output(request.version, keep_alive))
|
||||
self.transport.write(response.output(self.request.version, keep_alive))
|
||||
#print("KA - {}".format(self.parser.should_keep_alive()))
|
||||
if not keep_alive:
|
||||
self.transport.close()
|
||||
|
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
|
@ -11,5 +12,5 @@ func handler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func main() {
|
||||
http.HandleFunc("/", handler)
|
||||
http.ListenAndServe(":8000", nil)
|
||||
http.ListenAndServe(":" + os.Args[1], nil)
|
||||
}
|
33
tests/performance/sanic/http_response.py
Normal file
33
tests/performance/sanic/http_response.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
import asyncpg
|
||||
import sys
|
||||
import os
|
||||
import inspect
|
||||
|
||||
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
|
||||
sys.path.insert(0,currentdir + '/../../../')
|
||||
|
||||
import timeit
|
||||
|
||||
from sanic.response import json
|
||||
|
||||
print(json({ "test":True }).output())
|
||||
|
||||
print("Running New 100,000 times")
|
||||
times = 0
|
||||
total_time = 0
|
||||
for n in range(6):
|
||||
time = timeit.timeit('json({ "test":True }).output()', setup='from sanic.response import json', number=100000)
|
||||
print("Took {} seconds".format(time))
|
||||
total_time += time
|
||||
times += 1
|
||||
print("Average: {}".format(total_time/times))
|
||||
|
||||
print("Running Old 100,000 times")
|
||||
times = 0
|
||||
total_time = 0
|
||||
for n in range(6):
|
||||
time = timeit.timeit('json({ "test":True }).output_old()', setup='from sanic.response import json', number=100000)
|
||||
print("Took {} seconds".format(time))
|
||||
total_time += time
|
||||
times += 1
|
||||
print("Average: {}".format(total_time/times))
|
85
tests/performance/sanic/simple_server.py
Normal file
85
tests/performance/sanic/simple_server.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
import sys
|
||||
import os
|
||||
import inspect
|
||||
|
||||
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
|
||||
sys.path.insert(0,currentdir + '/../../../')
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import json, text
|
||||
from sanic.exceptions import ServerError
|
||||
|
||||
app = Sanic("test")
|
||||
|
||||
@app.route("/")
|
||||
async def test(request):
|
||||
return json({ "test": True })
|
||||
|
||||
@app.route("/sync")
|
||||
def test(request):
|
||||
return json({ "test": True })
|
||||
|
||||
|
||||
|
||||
|
||||
@app.route("/text")
|
||||
def rtext(request):
|
||||
return text("yeehaww")
|
||||
|
||||
@app.route("/exception")
|
||||
def exception(request):
|
||||
raise ServerError("yep")
|
||||
|
||||
@app.route("/exception/async")
|
||||
async def test(request):
|
||||
raise ServerError("asunk")
|
||||
|
||||
@app.route("/post_json")
|
||||
def post_json(request):
|
||||
return json({ "received": True, "message": request.json })
|
||||
|
||||
@app.route("/query_string")
|
||||
def query_string(request):
|
||||
return json({ "parsed": True, "args": request.args, "url": request.url, "query_string": request.query_string })
|
||||
|
||||
import sys
|
||||
app.run(host="0.0.0.0", port=sys.argv[1],)#, on_start=setup)
|
||||
|
||||
|
||||
|
||||
# import asyncio_redis
|
||||
# import asyncpg
|
||||
# async def setup(sanic, loop):
|
||||
# sanic.conn = []
|
||||
# sanic.redis = []
|
||||
# for x in range(10):
|
||||
# sanic.conn.append(await asyncpg.connect(user='postgres', password='zomgdev', database='postgres', host='192.168.99.100'))
|
||||
# for n in range(30):
|
||||
# connection = await asyncio_redis.Connection.create(host='192.168.99.100', port=6379)
|
||||
# sanic.redis.append(connection)
|
||||
|
||||
|
||||
# c=0
|
||||
# @app.route("/postgres")
|
||||
# async def postgres(request):
|
||||
# global c
|
||||
# values = await app.conn[c].fetch('''SELECT * FROM players''')
|
||||
# c += 1
|
||||
# if c == 10:
|
||||
# c = 0
|
||||
# return text("yep")
|
||||
|
||||
# r=0
|
||||
# @app.route("/redis")
|
||||
# async def redis(request):
|
||||
# global r
|
||||
# try:
|
||||
# values = await app.redis[r].get('my_key')
|
||||
# except asyncio_redis.exceptions.ConnectionLostError:
|
||||
# app.redis[r] = await asyncio_redis.Connection.create(host='127.0.0.1', port=6379)
|
||||
# values = await app.redis[r].get('my_key')
|
||||
|
||||
# r += 1
|
||||
# if r == 30:
|
||||
# r = 0
|
||||
# return text(values)
|
|
@ -1,83 +0,0 @@
|
|||
import asyncpg
|
||||
import sys
|
||||
import os
|
||||
import inspect
|
||||
|
||||
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
|
||||
parentdir = os.path.dirname(currentdir)
|
||||
sys.path.insert(0,parentdir)
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import json, text
|
||||
from sanic.exceptions import ServerError
|
||||
|
||||
app = Sanic("test")
|
||||
|
||||
@app.route("/")
|
||||
async def test(request):
|
||||
return json({ "test": True })
|
||||
|
||||
|
||||
|
||||
import asyncio_redis
|
||||
import asyncpg
|
||||
async def setup(sanic, loop):
|
||||
sanic.conn = []
|
||||
sanic.redis = []
|
||||
for x in range(10):
|
||||
sanic.conn.append(await asyncpg.connect(user='postgres', password='zomgdev', database='postgres', host='192.168.99.100'))
|
||||
for n in range(30):
|
||||
connection = await asyncio_redis.Connection.create(host='192.168.99.100', port=6379)
|
||||
sanic.redis.append(connection)
|
||||
|
||||
|
||||
c=0
|
||||
@app.route("/postgres")
|
||||
async def postgres(request):
|
||||
global c
|
||||
values = await app.conn[c].fetch('''SELECT * FROM players''')
|
||||
c += 1
|
||||
if c == 10:
|
||||
c = 0
|
||||
return text("yep")
|
||||
|
||||
r=0
|
||||
@app.route("/redis")
|
||||
async def redis(request):
|
||||
global r
|
||||
try:
|
||||
values = await app.redis[r].get('my_key')
|
||||
except asyncio_redis.exceptions.ConnectionLostError:
|
||||
app.redis[r] = await asyncio_redis.Connection.create(host='127.0.0.1', port=6379)
|
||||
values = await app.redis[r].get('my_key')
|
||||
|
||||
r += 1
|
||||
if r == 30:
|
||||
r = 0
|
||||
return text(values)
|
||||
|
||||
|
||||
|
||||
|
||||
@app.route("/text")
|
||||
def rtext(request):
|
||||
return text("yeehaww")
|
||||
|
||||
@app.route("/exception")
|
||||
def exception(request):
|
||||
raise ServerError("yep")
|
||||
|
||||
@app.route("/exception/async")
|
||||
async def test(request):
|
||||
raise ServerError("asunk")
|
||||
|
||||
@app.route("/post_json")
|
||||
def post_json(request):
|
||||
return json({ "received": True, "message": request.json })
|
||||
|
||||
@app.route("/query_string")
|
||||
def query_string(request):
|
||||
return json({ "parsed": True, "args": request.args, "url": request.url, "query_string": request.query_string })
|
||||
|
||||
import sys
|
||||
app.run(host="0.0.0.0", port=sys.argv[1])#, on_start=setup)
|
Loading…
Reference in New Issue
Block a user