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