본문 바로가기
Others

Process, Port, Debuging

by S.T.Lee 2022. 7. 17.

Process = 운영체제에 의해 실행중인 프로그램

  • 오래전에는 "컴퓨터 한대 = 하나의 프로그램"이였다. 계산기처럼 계산 프로그램 하나만 실행할 수 있었다.
  • 요즘 컴퓨터는 여러 개의 게임, 크롬 등의 웹사이트를 동시에 켜 놓을 수 있다.
  • 이게 가능한 것은 운영체제 덕분이다.
  • 운영체제는 프로그램을 실행 시켜주고 종료 시켜준다.

 

Port

2대의 컴퓨터 A와 B가 있다.

  • 그림에 대한 설명을 하면 
    • 컴퓨터에는 네트워크 인터페이스가 달려 있고, 이 인터페이스에 ip가 할당된다.
    • 컴퓨터마다 운영체제가 있으며 각 프로세스 1, 3을 실행중에 있다.
    • 컴퓨터 A의 경우 프로세스 1이 80 포트를 점유하고 있는데 이를 '컴퓨터 A가 80번 포트를 listen'하고 있다고 한다.
    • 컴퓨터 A의 프로세스 3가 외부 프로세스랑 통신하고 있는 상태이다.
      • 이때 사용자는 운영체제에게 ip 111.111.111.111에서 30000번 포트를 listen한다고 요청한다.
    • 111.111.111.111의 포트 30000 프로세스와 222.222.222.222의 포트 80 프로세스가 이어져 있고 이 TCP 연결을 (111.111.111.1111, 30000, 222.222.222.222, 80, TCP)로 표현할 수 있다.
      • TCP란 Transmission Control Protocol으로 IP 프로토콜 위에서 연결형 서비스를 지원하는 전송계층 프로토콜로, 인터넷 환경에서 기본으로 사용한다.
      • TCP의 특징으로 연결형 서비스를 제공하며, 전이중(Full Duplex) 방식의 양방향 가상 회선을 제공한다.
      • 신뢰성 있는 데이터 전송을 보존한다.
      • 출처 - https://dany-it.tistory.com/50
  • 포트(PORT)는 운영체제가 미리 만들어두는 "가상의 전화단자"이다. 실행되고 있는 수 많은 프로세스의 혼선을 없애기 위해 하나의 프로세스가 하나의 포트만 사용할 수 있도록 운영체제가 관리된다.
  • 인터넷으로 부터 특정 포트 조합으로 패킷이 들어올때, listen 중인 프로세스가 있다면 해당 프로세스로 패킷이 전달된다.
    • 패킷(packet)이란 네트워크를 통해 전송하기 쉽도록 자른 데이터의 전송 단위이다.
  • 만약 프로세스가 포트가 할당되어 있지 않고, 포트 번호가 사용되고 있지 않다면 운영체제는 프로세스에 해당 포트 번호를 할당한다.
  • HTTP의 기본 포트는 80, HTTPS는 443이다.(이유는 없다. 약속이다.)
    • 그림에서 각 컴퓨터의 1번 프로세스가 80번 포트를 사용한다. 이를 통해, 1번 프로세스가 HTTP web을 담당하는것을 유추할 수 있다.
    • 또한 컴퓨터 A의 프로세스 3가 B의 프로세스 1(HTTP)에 무언가 요청하고 있다. 이를 통해 A가 클라이언트, B가 서버임을 유추할 수 있다.

 

 

gunicorn/waitress 실습

  • Django의 기본적인 worker는 하나의 요청만 받아들일 수 있다.
  • 새로운 Django 프로젝트를 생성
  • allowed host에 0.0.0.0 추가
  • urls.py 수정
from time import sleep

from django.contrib import admin
from django.http import HttpResponse
from django.urls import path


def hi(request):
    print(f"processing: {request.GET.get('hi')}")
    sleep(5)
    return HttpResponse('hihi')


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', hi)
]
  • 요청 한 번 해보기
#mac
curl "0.0.0.0:8000?hi=1"
#window
curl http://localhost:8000?hi=1
curl -v http://localhost:8000?hi=1

? 뒤에는 전달할 데이터를 의미하고 이경우 검색어 hi의 값이 1임을 의미한다.
Pycharm Run 부분에서 processing:1이 나오는걸 확인할 수 있다.
? 뒤에 없으면 none, hi=12이면 processing:12가 나온다.
  • gunicorn 설치
pip install gunicorn
  • 여기서 잠깐, gunicorn은 기본 window환경에서는 돌아가지 않는다.
  • 따라서 waitress라는 다른 패키지 사용을 추천한다.
pip install waitress
  • 0.0.0.0은 모든 주소를 의미한다. *도 동일하다.
gunicorn guni_test.wsgi:application --bind 0.0.0.0:8000 --workers 1

waitress-serve --listen=*:8000 guni.wsgi:application
  • 위의 코드를 입력하면 새로운 서버가 열린다.
  • 8000번으로 켜졌음으로 원래 하고 있던 파이참 runserver는 종료해준다.

  • 이후 새로운 터미널을 켜서 $ curl http://localhost:8000?hi=1를 실행하면 정상적으로 작동하고 있음을 알 수 있다.

  • 하지만 실전에서는 저렇게 한번에 한 요청만 들어오지 않는다.
  • 두개의 요청을 보내보자
curl "0.0.0.0:8000?hi=1" & curl "0.0.0.0:8000?hi=2" & echo "start"

  • 재미있는건 분명 동기 데이터 전송인데 총 5.3초 정도의 시간 후 완료가 되었다.
    • thread로 인해 비동기식으로 데이터 처리가 되었다.
  • window의 문제인지는 모르겠으나..... 아래처럼 작성하면 동기식으로 데이터가 들어가진다.
  • 아마도 waitress의 공식문서를 보면 threads의 default가 4라고 되어 있어서 그런거 같다.
    • 참고로 threads는 process를 여러 개로 나눈 조각이다. worker를 나눈다고 생각하면 편할거 같다.
    • 이를 통해 비동기 데이터 처리가 가능하다.
    • 출처 - https://donghoson.tistory.com/8
$ curl http://localhost:8000?hi=1  http://localhost:8000?hi=2

$ curl http://localhost:8000?hi=1 & curl http://localhost:8000?hi=2 & curl http://localhost:8000?hi=3 & curl http://localhost:8000?hi=4 & curl http://localhost:8000?hi=5 &

왜인지 모르겠지만 자꾸 마지막 애들은 씹힌다. 정확히 3개부터 저런 현상이 발생했다. - 맨 뒤에 &를 안붙여서 발생한 일이다.

$ waitress-serve --listen=*:8000 --thread=8  guni.wsgi:application

  • 그림과 같이 warning이 더 이상 안뜨는 것을 확인할 수 있다. 확실하게 thread수와 관련된 경고문이였다.
  • 사실 이는 waitress 코드에서 확인할 수 있는데 request를 줄때 idle_threads, queue_size등을 print 해보면 된다.
def add_task(self, task):
    with self.lock:
        self.queue.append(task)
        self.queue_cv.notify()
        queue_size = len(self.queue)
        idle_threads = len(self.threads) - self.stop_count - self.active_count
        if queue_size > idle_threads:
            self.queue_logger.warning(
                "Task queue depth is %d", queue_size - idle_threads
            )

 

 

Background Process

  • 프로세스는 다음과 같이 나눌 수 있다.
    • foreground에서 돌아가는 프로그램(크롬, 파이참 등)
    • background에서 돌아가는 프로그램(Adobe Update 등)

 

 

Background Process에서 실행

  • 명령어 뒤에 & 을 붙이면 백그라운드에서 실행한다.
sleep 15
touch hi.txt
  • chmod 755 ./touch.sh를 통해 권한을 설정해준다.

  • 위와 같이 프로세스를 하는 중에 echo가 실행되는 비동기식 처리가 보여진다.
  • 백그라운에 돌려놓고 ssh 연결을 종료하면 백그라운드에서 실행되는 프로세스가 종료된다. 이를 막기 위해,
    • nohup 사용
    • systemctl service 사용
  • 2가지 방법이 있다.
  • 후자를 추천하는 바이다.

 

Thread

  • 하나의 Process는 한 개 이상의 Threads를 가지고 있다.
  • 하지만 파이썬에서는 왠만해서는 Multi Threads를 지양하고 있다. 이유는 GIL과 연결되어 있다.
    • GIL = Global Interpreter Lock 으로 파이썬 인터프리터가 한 Thread가 한 Bitecode를 실행 시킬 수 있도록 해준다.
    • 즉, 하나의 Thread에 모든 자원을 허락하고 그 후에 Lock을 걸어 다른 Thread를 실행할 수 없게 막는다.
    • 그래서 보통은 Multi Thread가 성능이 안좋으나 sleep을 사용하여 다른 Thread로 context switching을 하여 효율이 개선된다.
    • 하지만 I/O작업이 많아 Thread가 대기해야 하는 경우 Multi Thread가 성능이 더 좋다.
    • 출처 - https://ssungkang.tistory.com/entry/python-GIL-Global-interpreter-Lock%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C

  • 병렬 처리를하고 싶다면 하단의 두 방법을 사용하는 것이 좋다.
    • Multi Process
    • event loop(async io)

 

Debugging

  • Pycharm에서 가능하다
  • 오른쪽을 누르면 해당 줄이 빨간색으로 변한다. 중단점에서 코드 실행을 멈출 수 있다.
  • 중단점에서 성공적으로 멈춰지면 파란색 줄로 바뀐다.

  • Evaluation을 사용하면 더 많은 정보를 알 수 있다.
  • 일종의 dir인데 dir의 결과까지 보여준다고 생각하면 쉽다.
  • 계산기 모양을 누르면 된다.(또는 alt + f8)

  • expression에 식을 적으면 실행해준다. 아래의 경우 GET만 빈칸이 아닌걸 보고 GET에 문제가 있음을 유추할 수 있다.

  • 또한 type도 즉시 확인할 수 있다.