Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit599dab2

Browse files
committed
Add process monitor
1 parent1f84a17 commit599dab2

File tree

6 files changed

+161
-30
lines changed

6 files changed

+161
-30
lines changed

‎backend/book/settings.py‎

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,11 @@
1818
importdj_database_url
1919
importsnoop
2020
fromdjango.contrib.messagesimportconstantsasmessages
21-
frommainimportsimple_settings
21+
frommain.simple_settingsimport*
2222

2323
BASE_DIR=Path(__file__).parent.parent
2424

25-
SECRET_KEY=os.environ.get(
26-
'SECRET_KEY',
27-
'kt1+4_u=ga%3v3@fy0@7c(&lq%)6tt=c+f-(ihd32@t$)i6gjm',
28-
)
29-
30-
DEBUG=os.environ.get('DEBUG','True')[0].upper()=='T'
31-
32-
SAVE_CODE_ENTRIES=os.environ.get('SAVE_CODE_ENTRIES','True')[0].upper()=='T'
33-
34-
snoop.install(enabled=DEBUG,out=sys.__stderr__,columns=['thread'])
35-
36-
GITHUB_TOKEN=os.environ.get('GITHUB_TOKEN')
25+
snoop.install(enabled=Root.DEBUG,out=sys.__stderr__,columns=['thread'])
3726

3827
ALLOWED_HOSTS= [
3928
'alexmojaki.pythonanywhere.com',

‎backend/main/simple_settings.py‎

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,36 @@
1-
importos
2-
31
importsentry_sdk
2+
fromdryenvimportDryEnv,populate_globals
43
fromlittleutilsimportsetup_quick_console_logging
54
fromsentry_sdk.integrations.djangoimportDjangoIntegration
65

76
setup_quick_console_logging()
87

8+
9+
classRoot(DryEnv):
10+
DEBUG=True
11+
12+
SEPARATE_WORKER_PROCESS=False
13+
MASTER_URL="http://localhost:5000/"
14+
15+
SAVE_CODE_ENTRIES=True
16+
17+
SENTRY_DSN=""
18+
SECRET_KEY='kt1+4_u=ga%3v3@fy0@7c(&lq%)6tt=c+f-(ihd32@t$)i6gjm'
19+
GITHUB_TOKEN=""
20+
21+
22+
classMONITOR(DryEnv):
23+
ACTIVE=False
24+
THRESHOLD=90
25+
MIN_PROCESSES=1
26+
NUM_MEASUREMENTS=3
27+
SLEEP_TIME=5
28+
29+
930
sentry_sdk.init(
10-
dsn=os.environ.get("SENTRY_DSN"),
31+
dsn=Root.SENTRY_DSN,
1132
integrations=[DjangoIntegration()],
1233
send_default_pii=True
1334
)
1435

15-
SEPARATE_WORKER_PROCESS=os.environ.get('SEPARATE_WORKER_PROCESS','False')[0].upper()=='T'
16-
17-
MASTER_URL=os.environ.get('MASTER_URL',"http://localhost:5000/")
36+
populate_globals()

‎backend/main/workers/master.py‎

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
importatexit
22
importmultiprocessing
33
importqueue
4-
fromcollectionsimportdefaultdict
4+
fromcollectionsimportdefaultdict,deque
55
fromfunctoolsimportlru_cache
66
frommultiprocessingimportQueue,Process
77
fromthreadingimportThread
8-
fromtimeimportsleep
8+
fromtimeimportsleep,time
99

1010
importflask
11+
importpsutil
1112

1213
frommainimportsimple_settings
14+
frommain.simple_settingsimportMONITOR
1315
frommain.workers.utilsimportinternal_error_result,make_result
1416
frommain.workers.workerimportworker_loop_in_thread
1517

@@ -18,18 +20,31 @@
1820

1921
classUserProcess:
2022
def__init__(self):
23+
self.user_id=None
2124
self.task_queue=Queue()
2225
self.input_queue=Queue()
2326
self.result_queue=Queue()
2427
self.awaiting_input=False
2528
self.process=None
2629
self.fresh_process=True
30+
self.last_used=float('inf')
2731
self.start_process()
2832

29-
@atexit.register
30-
defcleanup():
31-
ifself.process:
32-
self.process.terminate()
33+
atexit.register(self.atexit_cleanup)
34+
35+
defatexit_cleanup(self):
36+
ifself.process:
37+
self.process.terminate()
38+
39+
defclose(self):
40+
atexit.unregister(self.atexit_cleanup)
41+
forqin [self.task_queue,self.input_queue,self.result_queue]:
42+
q.close()
43+
self.process.terminate()
44+
45+
@property
46+
defps(self):
47+
returnpsutil.Process(self.process.pid)
3348

3449
defstart_process(self):
3550
self.fresh_process=True
@@ -41,6 +56,7 @@ def start_process(self):
4156
self.process.start()
4257

4358
defhandle_entry(self,entry):
59+
self.last_used=time()
4460
ifentry["source"]=="shell":
4561
ifself.awaiting_input:
4662
self.input_queue.put(entry["input"])
@@ -98,11 +114,44 @@ def _await_result(self):
98114
assertmultiprocessing.get_start_method()=="spawn"
99115

100116

117+
defmonitor_processes():
118+
history=deque([],MONITOR.NUM_MEASUREMENTS)
119+
whileTrue:
120+
sleep(MONITOR.SLEEP_TIME)
121+
percent=psutil.virtual_memory().percent
122+
history.append(percent)
123+
print(f"Recent memory usage:{history}")
124+
print(f"Number of user processes:{len(user_processes)}")
125+
if (
126+
len(history)==history.maxlen
127+
andmin(history)>MONITOR.THRESHOLD
128+
andlen(user_processes)>MONITOR.MIN_PROCESSES
129+
):
130+
oldest=min(user_processes.values(),key=lambdap:p.last_used)
131+
print(f"Terminating process last used{int(time()-oldest.last_used)} seconds ago")
132+
deluser_processes[oldest.user_id]
133+
oldest.close()
134+
history.clear()
135+
136+
137+
@lru_cache()
138+
defstart_monitor():
139+
ifMONITOR.ACTIVE:
140+
Thread(
141+
target=monitor_processes,
142+
name=monitor_processes.__name__,
143+
daemon=True,
144+
).start()
145+
146+
101147
@app.route("/run",methods=["POST"])
102148
defrun():
149+
start_monitor()
103150
try:
104151
entry=flask.request.json
105-
user_process=user_processes[entry["user_id"]]
152+
user_id=entry["user_id"]
153+
user_process=user_processes[user_id]
154+
user_process.user_id=user_id
106155
user_process.handle_entry(entry)
107156
returnuser_process.await_result()
108157
exceptException:
@@ -123,7 +172,7 @@ def master_session():
123172
importrequests
124173
session=requests.Session()
125174

126-
ifnotsimple_settings.SEPARATE_WORKER_PROCESS:
175+
ifnotsimple_settings.Root.SEPARATE_WORKER_PROCESS:
127176
Thread(
128177
target=run_server,
129178
daemon=True,
@@ -133,7 +182,7 @@ def master_session():
133182
# Wait until alive
134183
whileTrue:
135184
try:
136-
session.get(simple_settings.MASTER_URL+"health")
185+
session.get(simple_settings.Root.MASTER_URL+"health")
137186
break
138187
exceptrequests.exceptions.ConnectionError:
139188
sleep(1)
@@ -143,7 +192,7 @@ def master_session():
143192

144193
defworker_result(entry):
145194
session=master_session()
146-
returnsession.post(simple_settings.MASTER_URL+"run",json=entry).json()
195+
returnsession.post(simple_settings.Root.MASTER_URL+"run",json=entry).json()
147196

148197

149198
if__name__=='__main__':

‎docker-compose.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ services:
2121
image:python-init
2222
build:.
2323
env_file:.env
24+
environment:
25+
MONITOR_ACTIVE:'True'
2426
stdin_open:true
2527
tty:true
2628
entrypoint:./master_server.sh

‎poetry.lock‎

Lines changed: 71 additions & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎pyproject.toml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ dj-database-url = "^0.5.0"
2626
django-crispy-forms ="^1.9.0"
2727
sentry-sdk ="^0.14.3"
2828
requests ="^2.23.0"
29+
psutil ="^5.7.0"
30+
dryenv ="^0.0.1"
2931

3032
[tool.poetry.extras]
3133
production = ["gevent","gunicorn","psycopg2"]

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp