Performance improvements to response and moved tests around

This commit is contained in:
Channel Cat 2016-10-08 15:21:40 -07:00
parent 74b0cbae1b
commit 6041e7cfa6
7 changed files with 143 additions and 113 deletions

View File

@ -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):

View File

@ -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()

View File

@ -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)
} }

View 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))

View 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)

View File

@ -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)