django 실행(runserver) 커맨드 분석
python [manage.py](<http://manage.py>) runserver
manage.py에 등록된 main 함수 호출
execute_from_command_line에서 입력된 인자(”runserver”)를 받고 ManagementUtility에 전달
utility.execute() 실행
CommandParser: 실행 프로그램(manage.py), 옵션 등을 담은 파싱 객체
subcommand: manage.py 이후 실행하는 명령(ex: “runserver”)
self.fetch_command(subcommand).run_from_argv(self.argv) 실행: runserver 커맨드 실행

fetch_command(): load_command_class 함수를 이용해 커맨드 클래스를 반환
run_from_argv(): self.argv를 바탕으로 BaseCommand.execute() 메소드 실행
# runserver.py
class Command(BaseCommand):
def execute(self, *args, **options):
if options['no_color']:
# We rely on the environment because it's currently the only
# way to reach WSGIRequestHandler. This seems an acceptable
# compromise considering `runserver` runs indefinitely.
os.environ["DJANGO_COLORS"] = "nocolor"
super().execute(*args, **options)
# base.py
class BaseCommand:
def execute(self, *args, **options):
...
output = self.handle(*args, **options) # self: runserver.Command
if output:
if self.output_transaction:
connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
output = '%s\\n%s\\n%s' % (
self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()),
output,
self.style.SQL_KEYWORD(connection.ops.end_transaction_sql()),
)
self.stdout.write(output)
return output
Command.handle() 메소드 실행: socket, port 관련 유효성 검증을 거친 후 run() 메소드 호출
class Command(BaseCommand):
def run(self, **options):
"""Run the server, using the autoreloader if needed."""
use_reloader = options['use_reloader'] # use_reloader = True
if use_reloader: # 실행
# watchman을 통한 코드 변경 감지
autoreload.run_with_reloader(self.inner_run, **options)
else:
self.inner_run(None, **options)
run_with_reloader() → start_django() 함수 호출
# from django.utils.autoreload import start_django
# reloader: StatReloader 객체(장고 파일 변경 감지)
# main_func: Command.inner_run
def start_django(reloader, main_func, *args, **kwargs):
ensure_echo_on()
main_func = check_errors(main_func)
django_main_thread = threading.Thread(target=main_func, args=args, kwargs=kwargs, name='django-main-thread')
django_main_thread.daemon = True
# target 함수(inner_run)를 가진 django-main-thread 실행
django_main_thread.start()
# 리로딩 영역
while not reloader.should_stop:
try:
reloader.run(django_main_thread)
except WatchmanUnavailable as ex:
# It's possible that the watchman service shuts down or otherwise
# becomes unavailable. In that case, use the StatReloader.
reloader = StatReloader()
logger.error('Error connecting to Watchman: %s', ex)
logger.info('Watching for file changes with %s', reloader.__class__.__name__)
reloader.run(): django_main_thread에서 앱 실행이 가능한지 확인하고, 파일 변경 감시 루프를 실행Command.inner_run()
# from django.core.management.commands.runserver import Command
class Command(BaseCommand):
def inner_run(self, *args, **kwargs):
threading = options['use_threading']
...
self.check_migrations()
try:
handler = self.get_handler(*args, **options)
run(self.addr, int(self.port), handler,
ipv6=self.use_ipv6, threading=threading, server_cls=self.server_cls)
except OSError as e:
...
migration 체크
handler=self.get_handler(): StaticFilesHandler 객체 전달. 환경 및 옵션에 따라 WSGIHandler가 전달됨
# DEBUG = False + python manage.py runserver --insecure 일경우 default handler(WSGIHandler)가 전달됨
def get_handler(self, *args, **options):
"""
Return the static files serving handler wrapping the default handler,
if static files should be served. Otherwise return the default handler.
"""
handler = super().get_handler(*args, **options)
use_static_handler = options['use_static_handler']
insecure_serving = options['insecure_serving']
if use_static_handler and (settings.DEBUG or insecure_serving):
return StaticFilesHandler(handler)
return handler
run()함수 실행
# wsgi_handler: 위에서 전달된 핸들러(StaticFilesHandler or WSGIHandler)
def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
server_address = (addr, port)
if threading: # True
httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
else:
httpd_cls = server_cls
# httpd = WSGIServer(("localhost", 8000), WSGIRequstHandler, False)
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
if threading:
# ThreadingMixIn.daemon_threads indicates how threads will behave on an
# abrupt shutdown; like quitting the server by the user or restarting
# by the auto-reloader. True means the server will not wait for thread
# termination before it quits. This will make auto-reloader faster
# and will prevent the need to kill the server manually if a thread
# isn't terminating correctly.
httpd.daemon_threads = True
httpd.set_app(wsgi_handler)
httpd.serve_forever()
WSGIServer 메소드 및 초기화(TCPServer)
WSGIServer.serve_forever(): 요청 대기
# run 함수에서 동적으로 httpd_cls가 생성될 때 thread mixin을 상속받음
class WSGIServer(ThreadingMixIn, WSGIServer):
def serve_forever(self, poll_interval=0.5):
...
try:
...
with _ServerSelector() as selector:
# selector = selectors.Pollselector
# register(fileobj, events, data=None)
# -> fileobj: fileno() 메소드를 가진 객체. fileno 메소드는 socket file(fd) 번호를 반환함
selector.register(self, selectors.EVENT_READ)
while not self.__shutdown_request:
ready = selector.select(poll_interval)
if self.__shutdown_request:
break
if ready:
self._handle_request_noblock()
self.service_actions()
finally:
self.__shutdown_request = False
self.__is_shut_down.set()
fetch_command().run_from_argv(): runserver command 클래스의 handle 메소드 실행
Runserver.handle(): 주소 파싱 및 유효성 검증 후 run 메소드 실행
Runserver.run(): inner_run 메소드 실행 with 파일 와치 리로더
Runserver.inner_run()
함수 호출run 함수 실행
# wsgi_handler: StaticFilesHandler or WSGIHandler
def run(addr, port, wsgi_handler, ipv6=False, threading=False, server_cls=WSGIServer):
server_address = (addr, port)
if threading: # threading = True
# ---------- 다음 스텝 호출 ----------
httpd_cls = type('WSGIServer', (socketserver.ThreadingMixIn, server_cls), {})
else:
httpd_cls = server_cls
httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
if threading:
httpd.daemon_threads = True
httpd.set_app(wsgi_handler)
httpd.serve_forever()
init() 실행
class TCPServer(BaseServer):
def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
"""Constructor. May be extended, do not override."""
BaseServer.__init__(self, server_address, RequestHandlerClass)
self.socket = socket.socket(self.address_family,
self.socket_type)
if bind_and_activate:
try:
self.server_bind() # 소켓 바인딩(socket.bind(self.server_address))
self.server_activate() # socket.listen()
except:
self.server_close() # socket.close()
raise
serve_forever()
# BaseServer.__is_shut_down = threading.Event()
# BaseServer.__shutdown_request = False
class BaseServer:
def serve_forever(self, poll_interval=0.5):
self.__is_shut_down.clear()
try:
with _ServerSelector() as selector:
# selector에 read event를 감지할 수 있도록 등록
selector.register(self, selectors.EVENT_READ)
while not self.__shutdown_request:
# read event 발생 시 ready에 값 할당(리스트)
ready = selector.select(poll_interval)
if self.__shutdown_request:
break
if ready:
# selector 값 변경 감지되면 요청 처리 메소드 호출
self._handle_request_noblock()
self.service_actions()
finally:
self.__shutdown_request = False
self.__is_shut_down.set()
_handle_request_noblock(): 자식 클래스들에 정의된 메소드 호출
class BaseServer:
def _handle_request_noblock(self):
try:
# TCPServer.get_request()
request, client_address = self.get_request() # socket.accept()
except OSError:
return
if self.verify_request(request, client_address):
try:
# ThreadingMixIn.process_request()
self.process_request(request, client_address)
except Exception:
self.handle_error(request, client_address)
# TCPServer.shutdown_request()
self.shutdown_request(request)
except:
self.shutdown_request(request)
raise
else:
self.shutdown_request(request)
def finish_request(self, request, client_address):
# self.RequestHandlerClass = WSGIRequestHandler
# ---------- 다음 스텝 호출 ----------
self.RequestHandlerClass(request, client_address, self)
class TCPServer(BaseServer):
def shutdown_request(self, request):
try:
request.shutdown(socket.SHUT_WR)
except OSError:
pass #some platforms may raise ENOTCONN here
self.close_request(request)
def close_request(self, request):
request.close()
class ThreadingMixIn:
def process_request_thread(self, request, client_address):
try:
# BaseServer.finish_request
self.finish_request(request, client_address)
except Exception:
self.handle_error(request, client_address)
finally:
# TCPServer.shutdown_request
self.shutdown_request(request)
def process_request(self, request, client_address):
if self.block_on_close:
vars(self).setdefault('_threads', _Threads())
t = threading.Thread(target = self.process_request_thread,
args = (request, client_address))
t.daemon = self.daemon_threads
self._threads.append(t)
t.start()
# from django.core.servers.basehttp WSGIServer
# django level에서 사용
# class WSGIServer(socketserver.ThreadingMixin, simple_server.WSGIServer):
init() 호출
class BaseRequestHandler:
def __init__(self, request, client_address, server):
self.request = request
self.client_address = client_address
self.server = server
# StreamRequestHandler.setup()
self.setup()
try:
# WSGIRequestHandler.handle()
self.handle()
finally:
# StreamRequestHandler.finish()
self.finish()
class StreamRequestHandler(BaseRequestHandler):
def setup(self):
self.connection = self.request # socket obj
if self.timeout is not None:
self.connection.settimeout(self.timeout)
if self.disable_nagle_algorithm:
self.connection.setsockopt(socket.IPPROTO_TCP,
socket.TCP_NODELAY, True)
self.rfile = self.connection.makefile('rb', self.rbufsize)
if self.wbufsize == 0:
# class _SocketWriter(BufferedIOBase)
self.wfile = _SocketWriter(self.connection)
else:
self.wfile = self.connection.makefile('wb', self.wbufsize)
def finish(self):
if not self.wfile.closed:
try:
self.wfile.flush()
except socket.error:
pass
self.wfile.close()
self.rfile.close()
class WSGIRequestHandler(simple_server.WSGIRequestHandler):
def handle(self):
self.close_connection = True
self.handle_one_request()
while not self.close_connection:
self.handle_one_request()
try:
self.connection.shutdown(socket.SHUT_WR)
except (AttributeError, OSError):
pass
def handle_one_request(self):
self.raw_requestline = self.rfile.readline(65537)
if len(self.raw_requestline) > 65536:
self.requestline = ''
self.request_version = ''
self.command = ''
self.send_error(414)
return
if not self.parse_request(): # An error code has been sent, just exit
return
# self.rfile = io.BufferedReader
# self.wfile = socketserver._SocketWriter
# ---------- 다음 스텝 호출 ----------
handler = ServerHandler(
self.rfile, self.wfile, self.get_stderr(), self.get_environ()
)
handler.request_handler = self # backpointer for logging & connection closing
handler.run(self.server.get_app())