Django에서 Stdout을 스트리밍 응답으로 리디렉션하는 방법
다음과 같은 기능이 주어집니다. 브라우저에서 실시간으로 stdout을 보는 방법은 무엇입니까?
import time
def job(times):
for i in range(times):
print(f'Task #{i}')
time.sleep(1)
print('Done')
time.sleep(0.5)
스트리밍 응답
일반적으로 응답은 모든 데이터가 수집된 후에 전송됩니다. 그러나 때로는 데이터가 준비될 때까지 기다릴 수 없습니다. 이 경우 스트리밍 응답을 사용합니다. Django에서는 StreamingHttpResponse입니다. 다음 문서에서는 SHR이라고 하겠습니다. StreamingHttpResponse는 반복자를 입력으로 받아들입니다. iterator에서 새 값을 가져올 때마다 값을 보냅니다. 이를 사용하려면 반복자 함수만 구현하면 됩니다.
yield
의 값을 실시간으로 사용자의 브라우저로 전송합니다.# Example of StreamingHttpResponse
from django.http.response import StreamingHttpResponse
def example():
for i in range(5):
# Add <br> to break line in browser
yield f'{i}<br>'
def stream(request):
return StreamingHttpResponse(example())
출력(브라우저에서):
0
1
2
3
4
스레딩
작업과 스트림 출력을 동시에 실행하므로 동시성이 필요합니다. Python에는 스레딩, 다중 처리 등과 같은 여러 선택 사항이 있습니다. 이 기사에서는 스레딩이 더 쉽기 때문에 스레딩을 사용하겠습니다.
# Example of threading
from threading import Thread
import time
def example(times):
for i in range(times):
print(i)
time.sleep(1)
# Create Thread
thread = Thread(target=example, args=(5,))
# Start Thread
thread.start()
time.sleep(2)
print("This is printed in the main thread")
# Waiting thread to be done
thread.join()
산출:
0
1
This is printed in the main thread
2
3
4
표준 출력 리디렉션
Python이 인쇄하는 위치를 변경하려면 변경해야 합니다
sys. stdout
. 모든 파일류 객체를 허용합니다. 특히 write
메서드로 개체를 정의해야 합니다.# Example of redirect stdout
import sys
class Printer:
def __init__(self):
self.contents = []
def write(self, value):
self.contents.append(value)
printer = Printer()
sys.stdout = printer
print('This should be saved in printer')
sys.stdout = sys.__stdout__
print('This should be printed to stdout')
print(printer.contents)
산출:
This should be printed to stdout
['This should be saved in printer', '\n']
스트리밍 응답으로 리디렉션 Stdout 구현
환경
파이썬 3.8.5
장고 3.2
먼저 Django 프로젝트를 생성합니다.
pip install django
django-admin startproject console_streaming
cd console_streaming
python manage.py startapp web
웹 설치
# console_streaming/settings.py
INSTALLED_APPS = [
...
# Add web
'web',
]
보기 만들기
# web/views.py
def stream(request):
# Implement later
pass
URL에 바인딩
# console_streaming/urls.py
from django.urls import path
from web import views
urlpatterns = [
path('stream/', views.stream),
]
테스트 작업
이것은 우리가 사용할 테스트 기능입니다. 한 줄을 인쇄하고 1초 동안 n번 기다린 다음 "Done"을 인쇄합니다.
# web/views.py
import time
def job(times):
for i in range(times):
print(f'Task #{i}')
time.sleep(1)
print('Done')
time.sleep(0.5)
프린터 등급
우리는 stdout을 처리하기 위해 Printer 클래스를 구현하고 Whale 프로그램 수명 주기에서 하나의 인스턴스만 사용할 것입니다.
sys.stdout
는 스레드에 특정하지 않기 때문에 다른 요청에서 다른 stdout을 사용하면 다른 요청에서 stdout을 가져옵니다. 그래서 사전을 사용하여 다른 스레드에 대한 대기열을 저장하고 current_thread()
를 사용하여 올바른 대기열을 식별하고 선택합니다. 현재 스레드가 Printer에 등록되지 않은 경우 기본 stdout을 사용합니다.# web/views.py
from queue import Queue
from threading import current_thread
import sys
class Printer:
def __init__(self):
self.queues = {}
def write(self, value):
'''handle stdout'''
queue = self.queues.get(current_thread().name)
if queue:
queue.put(value)
else:
sys.__stdout__.write(value)
def flush(self):
'''Django would crash without this'''
pass
def register(self, thread):
'''register a Thread'''
queue = Queue()
self.queues[thread.name] = queue
return queue
def clean(self, thread):
'''delete a Thread'''
del self.queues[thread.name]
# Initialize a Printer instance
printer = Printer()
sys.stdout = printer
### 스트리머 클래스
다음으로 Streamer 클래스에 의한 동시 실행 및 스트리밍 응답을 구현하겠습니다. 스레드를 초기화하고 대기열을 얻기 위해 프린터에 등록합니다. 그런 다음 대기열에서 값을 읽고 스레드가 끝날 때까지 응답에 양보하는 것을 반복합니다.
from threading import Thread
class Steamer:
def __init__(self, target, args):
self.thread = Thread(target=target, args=args)
self.queue = printer.register(self.thread)
def start(self):
self.thread.start()
print('This should be stdout')
while self.thread.is_alive():
try:
item = self.queue.get_nowait()
yield f'{item}<br>'
except Empty:
pass
yield 'End'
printer.clean(self.thread)
def stream(request):
streamer = Steamer(job, (10,))
return StreamingHttpResponse(streamer.start())
Django 서버 실행
$ python manage.py runserver
오픈http://localhost:8000/stream/
그럼 당신은 볼 수 있습니다
요청할 때마다 터미널에서 하나의 출력을 볼 수 있습니다.
This should be stdout
전체 조회수.py
from django.http.response import StreamingHttpResponse
from queue import Queue, Empty
from threading import Thread, current_thread
import time
import sys
class Printer:
def __init__(self):
self.queues = {}
def write(self, value):
'''handle stdout'''
queue = self.queues.get(current_thread().name)
if queue:
queue.put(value)
else:
sys.__stdout__.write(value)
def flush(self):
'''Django would crash without this'''
pass
def register(self, thread):
'''register a Thread'''
queue = Queue()
self.queues[thread.name] = queue
return queue
def clean(self, thread):
'''delete a Thread'''
del self.queues[thread.name]
printer = Printer()
sys.stdout = printer
class Steamer:
def __init__(self, target, args):
self.thread = Thread(target=target, args=args)
self.queue = printer.register(self.thread)
def start(self):
self.thread.start()
print('This should be stdout')
while self.thread.is_alive():
try:
item = self.queue.get_nowait()
yield f'{item}<br>'
except Empty:
pass
yield 'End'
printer.clean(self.thread)
def job(times):
for i in range(times):
print(f'Task #{i}')
time.sleep(1)
print('Done')
time.sleep(0.5)
def stream(request):
streamer = Steamer(job, (10,))
return StreamingHttpResponse(streamer.start())
코드 완료GitHub
wancat.cc에 게시된 원본
참조
Reference
이 문제에 관하여(Django에서 Stdout을 스트리밍 응답으로 리디렉션하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/wancat/how-to-redirect-stdout-to-streaming-response-in-django-59m3텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)