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',
|
503: 'Service Unavailable',
|
||||||
504: 'Gateway Timeout',
|
504: 'Gateway Timeout',
|
||||||
}
|
}
|
||||||
|
|
||||||
class HTTPResponse:
|
class HTTPResponse:
|
||||||
__slots__ = ('body', 'status', 'content_type')
|
__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.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
|
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):
|
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([
|
return b''.join([
|
||||||
'HTTP/{} {} {}\r\n'.format(version, self.status, STATUS_CODES.get(self.status, 'FAIL')).encode('latin-1'),
|
'HTTP/{} {} {}\r\n'.format(version, self.status, STATUS_CODES.get(self.status, 'FAIL')).encode(),
|
||||||
'Content-Type: {}\r\n'.format(self.content_type).encode('latin-1'),
|
b'Content-Type: ', self.content_type.encode(), b'\r\n',
|
||||||
'Content-Length: {}\r\n'.format(len(body)).encode('latin-1'),
|
b'Content-Length: ', str(len(self.body)).encode(), b'\r\n',
|
||||||
'Connection: {}\r\n'.format('keep-alive' if keep_alive else 'close').encode('latin-1'),
|
b'Connection: ', ('keep-alive' if keep_alive else 'close').encode(), b'\r\n',
|
||||||
b'\r\n',
|
b'\r\n',
|
||||||
body,
|
self.body,
|
||||||
#b'\r\n'
|
|
||||||
])
|
])
|
||||||
|
|
||||||
def json(body, status=200):
|
def json(body, status=200):
|
||||||
|
|
|
@ -95,19 +95,19 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
def on_body(self, body):
|
def on_body(self, body):
|
||||||
self.request.body = body
|
self.request.body = body
|
||||||
def on_message_complete(self):
|
def on_message_complete(self):
|
||||||
self.loop.create_task(self.get_response(self.request))
|
self.loop.create_task(self.get_response())
|
||||||
|
|
||||||
# -------------------------------------------- #
|
# -------------------------------------------- #
|
||||||
# Responding
|
# Responding
|
||||||
# -------------------------------------------- #
|
# -------------------------------------------- #
|
||||||
|
|
||||||
async def get_response(self, request):
|
async def get_response(self):
|
||||||
try:
|
try:
|
||||||
handler = self.sanic.router.get(request)
|
handler = self.sanic.router.get(self.request)
|
||||||
if handler is None:
|
if handler is None:
|
||||||
raise ServerError("'None' was returned while requesting a handler from the router")
|
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
|
# Check if the handler is asynchronous
|
||||||
if isawaitable(response):
|
if isawaitable(response):
|
||||||
|
@ -115,20 +115,20 @@ class HttpProtocol(asyncio.Protocol):
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
try:
|
try:
|
||||||
response = self.sanic.error_handler.response(request, e)
|
response = self.sanic.error_handler.response(self.request, e)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if self.sanic.debug:
|
if self.sanic.debug:
|
||||||
response = HTTPResponse("Error while handling error: {}\nStack: {}".format(e, format_exc()))
|
response = HTTPResponse("Error while handling error: {}\nStack: {}".format(e, format_exc()))
|
||||||
else:
|
else:
|
||||||
response = HTTPResponse("An error occured while handling an error")
|
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))
|
#print("response - {} - {}".format(self.n, self.request))
|
||||||
try:
|
try:
|
||||||
keep_alive = self.parser.should_keep_alive()
|
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()))
|
#print("KA - {}".format(self.parser.should_keep_alive()))
|
||||||
if not keep_alive:
|
if not keep_alive:
|
||||||
self.transport.close()
|
self.transport.close()
|
||||||
|
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,5 +12,5 @@ func handler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
http.HandleFunc("/", handler)
|
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