14
14
from locust import HttpLocust , TaskSet , task , events , runners
15
15
import requests
16
16
import statsd
17
+ import pandas as pd
17
18
18
19
from loadgenerator import project , csrf
19
20
import metrics
21
+ import logparser
20
22
21
23
host = os .environ .get ("LOCUST_STATSD_HOST" , "localhost" )
22
24
port = os .environ .get ("LOCUST_STATSD_PORT" , "8125" )
25
27
MEASUREMENT_NAME = os .environ .get ("LOCUST_MEASUREMENT_NAME" , "measurement" )
26
28
MEASUREMENT_DESCRIPTION = os .environ .get ("LOCUST_MEASUREMENT_DESCRIPTION" , "linear increase" )
27
29
DURATION = int (os .environ .get ("LOCUST_DURATION" , "20" ))
30
+ print (DURATION )
28
31
USERS = int (os .environ .get ("LOCUST_USERS" , '10' ))
29
32
HATCH_RATE = float (os .environ .get ("LOCUST_HATCH_RATE" , "1" ))
30
- LOAD_TYPE = os .environ .get ("LOCUST_LOAD_TYPE" , "constant" ) # linear, constant, random
33
+ LOAD_TYPE = os .environ .get ("LOCUST_LOAD_TYPE" , "constant" ) # linear, constant, random, nasa, worldcup
31
34
SPAWN_WAIT_MEAN = int (os .environ .get ("LOCUST_SPAWN_WAIT_MEAN" , "10" ))
32
35
SPAWN_WAIT_STD = int (os .environ .get ("LOCUST_SPAWN_WAIT_STD" , "4" ))
33
36
USER_MEAN = int (os .environ .get ("LOCUST_USER_MEAN" , "40" ))
34
37
USER_STD = int (os .environ .get ("LOCUST_USER_STD" , "5" ))
35
38
WAIT_MEAN = int (os .environ .get ("LOCUST_WAIT_MEAN" , "10" ))
36
39
WAIT_STD = int (os .environ .get ("LOCUST_WAIT_STD" , "4" ))
40
+ WEB_LOGS_PATH = os .environ .get ("LOCUST_LOG_PATH" , "logs" ) # path to nasa/worldcup logs
37
41
38
42
def wait (self ):
39
43
gevent .sleep (random .normalvariate (WAIT_MEAN , WAIT_STD ))
40
44
TaskSet .wait = wait
41
45
42
46
def login (l ):
43
47
resp = l .client .get ("/login" )
48
+ print (resp )
44
49
l .csrf_token = csrf .find_in_page (resp .content )
45
50
data = {
46
51
"_csrf" : l .csrf_token ,
47
52
"email" : l .email ,
48
53
"password" : "password"
49
54
}
50
55
r = l .client .post ("/login" , data )
56
+ print (r )
51
57
assert r .json ().get ("redir" , None ) == "/project"
52
58
53
59
def create_delete_project (l ):
@@ -140,6 +146,32 @@ def print_color(text):
140
146
reset = CSI + "m"
141
147
print ((CSI + "31;40m%s" + CSI + "0m" ) % text )
142
148
149
+
150
+ def replay_log_measure (df ):
151
+ def wait (self ):
152
+ from remote_pdb import RemotePdb
153
+ RemotePdb ('127.0.0.1' , 4444 ).set_trace ()
154
+ gevent .sleep (random .normalvariate (WAIT_MEAN , WAIT_STD ))
155
+ TaskSet .wait = wait
156
+
157
+ runner = runners .locust_runner
158
+ locust = runner .locust_classes [0 ]
159
+ started_at = datetime .utcnow ()
160
+ for client in df .groupby (["client_id" , "session_id" ]):
161
+ pass
162
+ while True :
163
+ if (datetime .utcnow () - started_at ).seconds > DURATION :
164
+ break
165
+ def start_locust (_ ):
166
+ try :
167
+ l = locust ()
168
+ #l.requests =
169
+ l .run ()
170
+ except gevent .GreenletExit :
171
+ pass
172
+ runner .locusts .spawn (start_locust , locust )
173
+
174
+
143
175
def random_measure ():
144
176
runner = runners .locust_runner
145
177
locust = runner .locust_classes [0 ]
@@ -158,8 +190,10 @@ def start_locust(_):
158
190
started_at = datetime .utcnow ()
159
191
160
192
while True :
161
- if (datetime .utcnow () - started_at ).seconds > DURATION :
193
+ seconds = (datetime .utcnow () - started_at ).seconds
194
+ if seconds > DURATION :
162
195
break
196
+ print ("%d seconds left!" % (DURATION - seconds ))
163
197
new_user = - 1
164
198
while new_user < 0 :
165
199
new_user = int (random .normalvariate (USER_MEAN , USER_STD ))
@@ -176,14 +210,48 @@ def start_locust(_):
176
210
if diff > 0 :
177
211
for l in random .sample (locusts , diff ):
178
212
if new_user >= len (runner .locusts ): break
179
- runner .locusts .killone (l )
213
+ try :
214
+ runner .locusts .killone (l )
215
+ except Exception as e :
216
+ print ("failed to kill locust: %s" % e )
180
217
print ("stop user: now: %d" % len (runner .locusts ))
181
218
STATSD .gauge ("user" , len (runner .locusts ))
182
219
wait = random .normalvariate (SPAWN_WAIT_MEAN , SPAWN_WAIT_STD )
183
220
print_color ("cooldown for %f" % wait )
184
221
time .sleep (wait )
185
222
stop_measure (started_at )
186
223
224
+ def read_log (type ):
225
+ if type == "nasa" :
226
+ read_log = logparser .read_nasa
227
+ else : # "worldcup"
228
+ read_log = logparser .read_worldcup
229
+ df = read_log (WEB_LOGS_PATH , limit = 1000 )
230
+ filter = df ["type" ].isin (["HTML" , "DYNAMIC" , "DIRECTORY" ])
231
+ if type == "worldcup" :
232
+ filter = filter & (df .region == "Paris" ) & (df .server == 4 )
233
+ return df [filter ]
234
+
235
+ def session_number (v ):
236
+ diff = v .timestamp .diff (1 )
237
+ diff .fillna (0 , inplace = True )
238
+ sessions = (diff > pd .Timedelta (minutes = 10 )).cumsum ()
239
+ data = dict (client_id = v .client_id , timestamp = v .timestamp ,
240
+ session_id = sessions .values )
241
+ return pd .DataFrame (data )
242
+
243
+ def started_at (v ):
244
+ data = dict (client_id = v .client_id , timestamp = v .timestamp , session_id = v .session_id ,
245
+ started_at = [v .timestamp .iloc [0 ]] * len (v .timestamp ))
246
+ return pd .DataFrame (data )
247
+
248
+ def group_log_by_sessions (df ):
249
+ df = df .sort_values ("timestamp" )
250
+ per_client = df .groupby (df .client_id , sort = False )
251
+ with_session = per_client .apply (session_number )
252
+ by = [with_session .client_id , with_session .session_id ]
253
+ return with_session .groupby (by ).apply (started_at )
254
+
187
255
def measure ():
188
256
RequestStats ()
189
257
time .sleep (5 )
@@ -196,7 +264,13 @@ def measure():
196
264
def linear_measure (* args , ** kw ):
197
265
stop_measure (started_at )
198
266
events .hatch_complete += linear_measure
199
- else : # "random"
267
+ elif LOAD_TYPE == "random" :
200
268
random_measure ()
269
+ elif LOAD_TYPE == "nasa" or LOAD_TYPE == "worldcup" :
270
+ df = read_log (LOAD_TYPE ).head (10000 )
271
+ replay_log_measure (group_log_by_sessions (df ))
272
+ else :
273
+ sys .stderr .write ("unsupported load type: %s" % LOAD_TYPE )
274
+ sys .exit (1 )
201
275
202
276
Thread (target = measure ).start ()
0 commit comments