1+ import multiprocessing
12import os
2- import ujson
33import asyncpg
4- import multiprocessing
54import random
5+ import asyncio
6+ from operator import itemgetter
67import blacksheep as bs
78import jinja2
8- from email .utils import formatdate
9-
10- try :
11- from ujson import dumps as jsonify
12- except :
13- from json import dumps as jsonify
14-
15-
16- _is_travis = os .environ .get ('TRAVIS' ) == 'true'
17-
18- _is_gunicorn = "gunicorn" in os .environ .get ("SERVER_SOFTWARE" , "" )
19-
20- _cpu_count = multiprocessing .cpu_count ()
21- if _is_travis :
22- _cpu_count = 2
23-
24-
25- #from blacksheep.settings.json import json_settings
26- #json_settings.use(dumps=jsonify)
27-
28- DBDRV = "postgres"
29- DBHOST = "tfb-database"
30- DBUSER = "benchmarkdbuser"
31- DBPSWD = "benchmarkdbpass"
9+ import msgspec
10+ from pathlib import Path
3211
3312READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1'
3413WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2'
3514ADDITIONAL_ROW = [0 , "Additional fortune added at request time." ]
3615MAX_POOL_SIZE = 1000 // multiprocessing .cpu_count ()
3716MIN_POOL_SIZE = max (int (MAX_POOL_SIZE / 2 ), 1 )
38-
3917db_pool = None
18+ key = itemgetter (1 )
4019
41- g_response_server = None
42- g_response_add_date = False
43-
20+ try :
21+ import uvloop
22+ asyncio .set_event_loop_policy (uvloop .EventLoopPolicy ())
23+ except Exception :
24+ ...
4425
4526async def setup_db (app ):
4627 global db_pool
4728 db_pool = await asyncpg .create_pool (
48- user = os .getenv ('PGUSER' , DBUSER ),
49- password = os .getenv ('PGPASS' , DBPSWD ),
29+ user = os .getenv ('PGUSER' , "benchmarkdbuser" ),
30+ password = os .getenv ('PGPASS' , "benchmarkdbpass" ),
5031 database = 'hello_world' ,
51- host = DBHOST ,
32+ host = "tfb-database" ,
5233 port = 5432 ,
5334 min_size = MIN_POOL_SIZE ,
54- max_size = MAX_POOL_SIZE ,
35+ max_size = MAX_POOL_SIZE ,
5536 )
5637
5738
5839def load_fortunes_template ():
59- path = os .path .join ('templates' , 'fortune.html' )
60- with open (path , 'r' ) as template_file :
61- template_text = template_file .read ()
62- return jinja2 .Template (template_text )
40+ with Path ("templates/fortune.html" ).open ("r" ) as f :
41+ return jinja2 .Template (f .read ())
6342
6443
6544fortune_template = load_fortunes_template ()
@@ -82,139 +61,92 @@ def get_num_queries(request):
8261 return 500
8362 return query_count
8463
64+ ENCODER = msgspec .json .Encoder ()
65+ DECODER = msgspec .json .Decoder ()
66+ JSON_CONTENT_TYPE = b"application/json"
67+ def jsonify (
68+ data ,
69+ status = 200 ,
70+ headers = None ,
71+ ):
72+ """
73+ Returns a response with application/json content,
74+ and given status (default HTTP 200 OK).
75+ """
76+ return bs .Response (
77+ status = status ,
78+ headers = headers ,
79+ content = bs .Content (content_type = JSON_CONTENT_TYPE , data = ENCODER .encode (data )),
80+ )
8581
86- # ------------------------------------------------------------------------------------------
87-
88- async def bs_middleware (request , handler ):
89- global g_response_server , g_response_add_date
90- response = await handler (request )
91- if g_response_server :
92- response .headers [b'Server' ] = g_response_server
93- if g_response_add_date :
94- response .headers [b'Date' ] = formatdate (timeval = None , localtime = False , usegmt = True )
95- return response
82+ class Result (msgspec .Struct ):
83+ id : int
84+ randomNumber : int
9685
86+ # ------------------------------------------------------------------------------------------
9787
98- @app . route ('/json' )
88+ @bs . get ('/json' )
9989async def json_test (request ):
100- return bs .json ( {'message' : 'Hello, world!' } )
101-
90+ return jsonify ( {'message' : 'Hello, world!' } )
10291
103- @app . route ('/db' )
92+ @bs . get ('/db' )
10493async def single_db_query_test (request ):
10594 row_id = random .randint (1 , 10000 )
10695
10796 async with db_pool .acquire () as db_conn :
10897 number = await db_conn .fetchval (READ_ROW_SQL , row_id )
10998
110- world = { 'id' : row_id , ' randomNumber' : number }
111- return bs . json ( world )
99+ return jsonify ( Result ( id = row_id , randomNumber = number ))
100+ # return ({'id': row_id, 'randomNumber': number} )
112101
113102
114- @app . route ('/queries' )
103+ @bs . get ('/queries' )
115104async def multiple_db_queries_test (request ):
116105 num_queries = get_num_queries (request )
117106 row_ids = random .sample (range (1 , 10000 ), num_queries )
118- worlds = [ ]
107+ worlds = []
119108
120109 async with db_pool .acquire () as db_conn :
121110 statement = await db_conn .prepare (READ_ROW_SQL )
122111 for row_id in row_ids :
123112 number = await statement .fetchval (row_id )
124- worlds .append ( {"id" : row_id , "randomNumber" : number } )
113+ # worlds.append( {"id": row_id, "randomNumber": number} )
114+ worlds .append (Result (id = row_id , randomNumber = number ))
125115
126- return bs . json (worlds )
116+ return jsonify (worlds )
127117
128118
129- @app . route ('/fortunes' )
119+ @bs . get ('/fortunes' )
130120async def fortunes_test (request ):
131121 async with db_pool .acquire () as db_conn :
132122 fortunes = await db_conn .fetch ("SELECT * FROM Fortune" )
133123
134124 fortunes .append (ADDITIONAL_ROW )
135- fortunes .sort (key = lambda row : row [ 1 ] )
125+ fortunes .sort (key = key )
136126 data = fortune_template .render (fortunes = fortunes )
137127 return bs .html (data )
138128
139129
140- @app . route ('/updates' )
130+ @bs . get ('/updates' )
141131async def db_updates_test (request ):
142132 num_queries = get_num_queries (request )
143133 ids = sorted (random .sample (range (1 , 10000 + 1 ), num_queries ))
144134 numbers = sorted (random .sample (range (1 , 10000 ), num_queries ))
145135 updates = list (zip (ids , numbers ))
146136
147- worlds = [ {"id" : row_id , "randomNumber" : number } for row_id , number in updates ]
148-
137+ # worlds = [ {"id": row_id, "randomNumber": number} for row_id, number in updates ]
138+ worlds = [ Result ( id = row_id , randomNumber = number ) for row_id , number in updates ]
149139 async with db_pool .acquire () as db_conn :
150140 statement = await db_conn .prepare (READ_ROW_SQL )
151141 for row_id , _ in updates :
152142 await statement .fetchval (row_id )
153143 await db_conn .executemany (WRITE_ROW_SQL , updates )
154144
155- return bs . json (worlds )
145+ return jsonify (worlds )
156146
157147
158- @app . route ('/plaintext' )
148+ @bs . get ('/plaintext' )
159149async def plaintext_test (request ):
160150 return bs .Response (200 , content = bs .Content (b"text/plain" , b'Hello, World!' ))
161151 #return bs.text('Hello, World!')
162152
163-
164- # -----------------------------------------------------------------------------------
165-
166- if __name__ == "__main__" :
167- import optparse
168- import logging
169- import re
170-
171- parser = optparse .OptionParser ("usage: %prog [options]" , add_help_option = False )
172- parser .add_option ("-h" , "--host" , dest = "host" , default = '0.0.0.0' , type = "string" )
173- parser .add_option ("-p" , "--port" , dest = "port" , default = 8080 , type = "int" )
174- parser .add_option ("-s" , "--server" , dest = "server" , default = "uvicorn" , type = "string" )
175- parser .add_option ("-w" , "--workers" , dest = "workers" , default = 0 , type = "int" )
176- parser .add_option ("-k" , "--keepalive" , dest = "keepalive" , default = 60 , type = "int" )
177- parser .add_option ("-v" , "--verbose" , dest = "verbose" , default = 0 , type = "int" )
178- (opt , args ) = parser .parse_args ()
179-
180- workers = _cpu_count
181- if workers > 0 :
182- workers = opt .workers
183-
184- if _is_travis :
185- workers = 2
186-
187- def run_app ():
188- global g_response_server , g_response_add_date
189-
190- if opt .gateway == "uvicorn" :
191- import uvicorn
192- log_level = logging .ERROR
193- uvicorn .run (app , host = opt .host , port = opt .port , workers = 1 , loop = "uvloop" , log_level = log_level , access_log = False )
194-
195- if opt .server == 'fastwsgi' :
196- import fastwsgi
197- from blacksheep .utils .aio import get_running_loop
198- g_response_server = b'FastWSGI'
199- app .middlewares .append (bs_middleware )
200- loop = get_running_loop ()
201- loop .run_until_complete (app .start ())
202- fastwsgi .run (app , host = opt .host , port = opt .port , loglevel = opt .verbose )
203-
204- if opt .server == 'socketify' :
205- import socketify
206- msg = "Listening on http://0.0.0.0:{port} now\n " .format (port = opt .port )
207- socketify .WSGI (app ).listen (opt .port , lambda config : logging .info (msg )).run ()
208-
209- def create_fork ():
210- n = os .fork ()
211- # n greater than 0 means parent process
212- if not n > 0 :
213- run_app ()
214-
215- # fork limiting the cpu count - 1
216- for i in range (1 , workers ):
217- create_fork ()
218-
219- run_app () # run app on the main process too :)
220-
0 commit comments