<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>don&apos;s blog</title>
    <description>development study blog</description>
    <link>https://don2101.github.io//</link>
    <atom:link href="https://don2101.github.io//feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Sat, 09 Nov 2024 07:44:42 +0000</pubDate>
    <lastBuildDate>Sat, 09 Nov 2024 07:44:42 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>Python에서 Singleton 구현</title>
        <description>Singleton Pattern


  글로벌하게 접근 가능한 한 개의 객체만을 생성하는 패턴




사용 이유


  로깅, DB작업, 프린터 스풀러 등 어플리케이션 리소스에 대한 동시 요청 충돌을 막기 위해 한개의 인스턴스만 생성
    
      ex) 일관성을 위해 DB 접근 객체를 하나만 생성
    
  
  클래스에 대한 단일 전역 객체 제공
  공유된 리소스에 대한 동시 제어




1. 구현 방법


  Constructor를 private으로 선언
  객체를 초기화 하는 static함수, 객체를 return하는 static함수 구현




Python에서 구현

class Singleton(object):
    def __new__(cls):
        if not hasattr(cls, &apos;instance&apos;):
    	    cls.instance = super(Singleton, cls).__new__(cls)
        return cls.instance
      
s = Singleton()
print(&quot;Object created&quot;, s)

s1 = Singleton()
print(&quot;Object created&quot;, s1)



  super(Singleton, cls): Singleton 클래스 자신의 부모에 접근합니다ㅣ
  hasattr을 통해 클래스 자체에서 instance를 갖고 있는지 확인한 후 없으면 instance 객체를 생성합니다.
  cls: 클래스 그 자체를 의미하는 변수





  실행 결과





  매 요청마다 같은 객체를 반환





  __init__과 self를 사용한다면


class Singleton(object):
    def __init__(self):
        if not hasattr(self, &apos;instance&apos;):
            self.instance = super(Singleton, self).__init__()
        return self.instance
      
s = Singleton()
print(&quot;Object created&quot;, s)

s1 = Singleton()
print(&quot;Object created&quot;, s1)





  실행 결과





  매 요청마다 다른 객체를 반환




2. Lazy Initialization


  클래스를 생성하면서 필요하지 않은 시점에 객체가 미리 만들어 질 수 있다.
  객체가 꼭 필요한 시점에만 객체를 생성
  클래스를 초기화 한 후 함수를 통해 객체를 생성




Python에서 구현

class Singleton:
    __instance = None
    def __init__(self):
        if not Singleton.__instance:
            print(&quot;__init__ method called...&quot;)
        else:
            print(&quot;Instance already created:&quot;, self.getInstance())
    
    @classmethod
    def getInstance(cls):
        if not cls.__instance:
            cls.__instance = Singleton()
        return cls.__instance

s = Singleton() # 클래스 초기화, 객체 생성 X
Singleton.getInstance() # 객체 생성
s1 = Singleton().getInstance() # getInstance를 통해 싱글턴 객체에 접근
print(s1)

s2 = Singleton()
print(s2)



  __instance: 클래스 전역 객체
  classmethod 인 getInstance를 통해 객체 생성, 접근





  실행 결과






s2에 대해…


  위 코드에서 getInstance() 를 통해 반환 받은 객체의 주소는 0x103cf8208 입니다.
  s1을 print하면 동일한 주소에 대한 객체가 반환됩니다.
  반면 s2는 Singleton 생성자를 통해 반환받은 객체를 쥐고 있으며 s1이 담고 있는 객체의 주소와는 다릅니다.
  s2는 정확하게 말하면 getInstance 를 통해 생성된 즉, 싱글턴 패턴을 통해 생성된 객체가 아닌 이름이Singleton 인 클래스의 생성자를 통해 생성된 객체입니다.
  두 객체는 완전히 다른 객체이며, 싱글턴 패턴을 통해 생성된 객체에 접근하기 위해선 getInstance 메서드를 통해 접근해야 합니다.




Java에서 구현

 public class Singleton {
		private static Singleton instance = null;
		
  	private Singleton(){} // 외부에서 생성자에 접근 불가능

		public static Singleton getInstance(){ // Singleton instance 반환
				if(instance == null){
						instance = new Singleton();
				}

				return instance;
		}

    public void printInstance(){
      	System.out.println(&quot;Singletone instance&quot;);
    }

    public static void main(String[] args) {
        Singletone singleton = Singletone.getInstance();
      	singletone.printInstance();
    }
}



  생성자에 접근하지 못하게 한 후 getInstance를 통해서만 객체에 접근
  static 이기 때문에 매 번 같은 객체를 반환




Python vs Java


  python은 클래스 자체를 제어하는 것이 가능하므로 생성자를 실행한 후 classmethod를 통해 전역 객체 생성
  Java는 static 메서드를 통해 전역 객체를 생성




Module Singleton


  파이썬에서 import방식에 의해 모든 모듈은 싱글톤입니다.


작동 방식


  Python은 모듈이 import되었는지 확인
  됐다면, 해당 객체를 반환하고 아니면 import하여 instance화
  모듈은 import하는 순간 초기화. 하지만, 같은 모듈을 import하면 초기화 하지 않는다.




3. Monostate Singleton Pattern


  모든 객체가 같은 상태를 공유하는 패턴
  한 객체의 데이터의 유일성을 보장할 수 있는 방법
  객체를 파생해도 동일한 상태를 공유




단점


  객체가 사용되지 않더라도 메모리 공간을 차지한다
  생성과 소멸이 잦으며, 많은 비용이 소모된다.




구현 예시

class Borg:
    __shared_state = {&quot;1&quot;: &quot;2&quot;}
    
    def __init__(self):
        self.x = 1
        self.__dict__ = {}

b1 = Borg()
b2 = Borg()

b2.x = 4

print(&quot;b1 is :&quot;, b1)
print(&quot;b2 is :&quot;, b2)

print(&quot;b1 x: &quot;, b1.x)
print(&quot;b2 x: &quot;, b2.x)

print(&quot;b1 dict: &quot;, b1.__dict__)
print(&quot;b2 dict: &quot;, b2.__dict__)



  __dict__: 클래스 내부 속성 변수를 dictionary 관리하는 변수
    
      self.x 를 선언해도 self.__dict__ 를 사용하면 속성변수 x 에 접근할 수 없게된다.
      이후 b2.x = 4 를 통해 x 변수를 dictionary로 관리
    
  
  해당 코드에서 __shared_state 에 있는 데이터를 dictionary로 관리하게 되고 모든 인스턴스에서 공유하게 된다.





  실행 결과






__new__를 사용한 구현

class Book(object):
    _shared_state = {}

    def __new__(cls, *args, **kwargs):
        obj = super(Book, cls).__new__(cls, *args, **kwargs)
        obj.__dict__ = cls._shared_state
        print(obj)

        return obj

book1 = Book() # 다른 객체이지만 상태를 공유한다.
book2 = Book()

book1.x = 1
book2.y = 2

print(book1.__dict__)
print(book2.__dict__)





  실행 결과






4. Singleton and Meta class

Meta class


  클래스의 클래스
  클래스 그 자체는 메타 클래스의 인스턴스
  이미 정의된 클래스를 통해 새로운 형식의 클래스 생성 가능
    
      상속과 유사한 기능처럼 보인다
    
  




Meta class vs Inheritance


  보통 메타 클래스는 OOP의 제약을 벗어난 구현을 위해 사용
  한 객체가 메타 클래스로 부터 받은 method를 호출해도 메타 클래스의 해당 메서드를 찾지 않는다.
    
      다만 객체가 생성될 시에 메타 클래스가 미리 생성
      python이 인터프리터를 사용하기에 가능
    
  
  상속과 다르게 부모-자식관계로 묶여 있지 않으며, 서로 다른 객체
  OOP의 제약을 벗어난 극도로 dynamic한 프로그래밍 시에 사용 추천
    
      Stack overflow 답변
    
  





  예시





  python에서 모든 것은 객체이다
  type 클래스가 int 클래스의 메타클래스
    
      int가 type을 재정의
    
  




python에서 클래스


  기본적으로 python에서 class 를 사용해 정의한 클래스는 클래스 이면서 객체이다
  클래스 그 자체이기도 하지만 동시에 객체이기도 하다





  type을 통해 클래스의 자료형을 확인


class Car:
    pass

print(type(Car))

# 출력 결과

&amp;lt;class &apos;type&apos;&amp;gt;



  python에서 모든 클래스는 클래스 이며 type이라는 클래스의 객체이기도 하다




type


  type 은 python에서 자료형을 확인하는 함수이지만, 또 다른 기능으로 클래스를 생성하는 기능이 있다.
  인자를 보면 type(name, bases, dict)의 인자를 받는다
    
      name: 클래스 명
      base: 베이스 클래스
      dict: 속성값
    
  
  인자로 클래스를 이루는 정의를 받아 클래스를 반환
    
      그리고 이 클래스는 객체가 되기도 한다
    
  
  class Car 라는 코드는 사실상 type 이 실행되어 클래스(이면서 객체)를 반환하는 과정을 거친다.
  메타 클래스는 클래스의 클래스 이며 클래스를 생성하는 클래스이다.
    
      즉, 메타 클래스는 클래스 생성을 제어할 수 있다.
    
  
  type 은 메타 클래스이며, 클래스를 생성하는 메타클래스이다.




type을 통한 클래스 및 인스턴스 생성

# type을 통해 Wing 클래스를 생성하고 A에 저장

A = type(&quot;Wing&quot;, (), {&quot;x&quot;: 1})
print(A)
print(A.__dict__)

# A를 통해 Wing class의 인스턴스를 생성 후 a1에저장, 값을 확인

a1 = A()
print(a1.x)





  출력 결과






그래서…


  메타 클래스를 통해 클래스와 객체 생성을 제어할 수 있으며, 이는 싱글톤을 생성하는 용도로 사용할 수 있다는 것과 같다




메타 클래스를 통한 싱글턴 생성

class MetaSingleton(type):
    _instances = {}
		
    # 클래스를 함수처럼 사용할 때 호출되는 메서드
    # ex) a = A(), a()
    
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(MetaSingleton, cls).__call__(*args, **kwargs)

        return cls._instances[cls]


class Book(metaclass=MetaSingleton):
    pass


book1 = Book()
book2 = Book()

print(book1)
print(book2)





  출력 결과






5. Examples

데이터베이스


  여러 서비스가 한 개의 DB를 공유하는 구조
  안정된 서비스를 위해
    
      DB의 일관성을 보존해야 하며, 연산간 충돌이 없어야 한다.
      다수의 DB 연산을 처리하려면 메모리와 CPU를 효율적으로 사용해야 한다.
    
  





  싱글턴을 통해 하나의 DB 접속 객체 생성


import sqlite3

# 객체를 싱글턴으로 만드는 역할

class MetaSingleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(MetaSingleton, cls).__call__(*args, **kwargs)
        
        return cls._instances[cls]


# MetaSingleton으로 인해 1개의 Database 객체만 생성

class Database(metaclass=MetaSingleton):
    connection = None

    def connect(self):
        if self.connection is None:
            self.connection = sqlite3.connect(&quot;db.sqlite3&quot;)
            self.cursorobj = self.connection.cursor()

        return self.cursorobj


db1 = Database().connect()
db2 = Database().connect()

print(db1)
print(db2)




  MetaSingleton 메타 클래스에 의해 Database 객체는 싱글턴으로 생성
  웹 앱이 DB 요청을 할 때 마다 Database 클래스 객체를 한개만 생성하여 DB 동기화를 보장
    
      리소스를 하나만 사용하여 CPU, 메모리 효율적 사용
    
  




인프라 상태 확인

class StatusCheck:
    _instance = None
    
    # 싱글턴으로 StatueCheck 객체 생성
    # 클래스 메서드인 __new__를 사용
    
    def __new__(cls, *args, **kwargs):
        if not StatusCheck._instance:
            StatusCheck._instance = super(StatusCheck, cls).__new__(cls, *args, **kwargs)

        return StatusCheck._instance

    # 싱글턴에서 공동으로 공유하는 자원
    
    def __init__(self):
        self._servers = []
		
    def addServer(self):
        self._servers.append(&quot;Server 1&quot;)
        self._servers.append(&quot;Server 2&quot;)
        self._servers.append(&quot;Server 3&quot;)
        self._servers.append(&quot;Server 4&quot;)

    def changeServer(self):
        self._servers.pop()
        self._servers.append(&quot;Server 5&quot;)

# 동일한 두 객체

status_check1 = StatusCheck()
status_check2 = StatusCheck()

status_check1.addServer()
print(&quot;Schedule statue check for servers (1)&quot;)

for i in range(4):
    print(&quot;Checking &quot;, status_check1._servers[i])

status_check2.changeServer()
print(&quot;Schedule statue check for servers (2)&quot;)

for i in range(4):
    print(&quot;Checking &quot;, status_check2._servers[i])






  실행 결과





  동일한 객체 status_check1, status_check2 에서 _servers 배열을 조작




6. 정리

싱글턴의 단점


  같은 객체에 여러 참조자가 있을 수 있다.
  전역 객체에 종속적인 클래스간 관계가 복잡하며, 전역 객체 수정이 다른 클래스에 영향을 미칠 수 있다.




싱글턴을 사용하는 상황


  어플리케이션에서 풀, 캐시, 설정 등 한 개의 객체만 필요한 경우에 생성하여 사용
  글로벌 액세스를 제공해야 하는 경우
  클래스 객체가 한 개만 필요한 경우에 사용



</description>
        <pubDate>Tue, 07 Jan 2020 00:00:00 +0000</pubDate>
        <link>https://don2101.github.io//2020/01/07/Python-Python%EC%9C%BC%EB%A1%9C-Singleton-%EA%B5%AC%ED%98%84/</link>
        <guid isPermaLink="true">https://don2101.github.io//2020/01/07/Python-Python%EC%9C%BC%EB%A1%9C-Singleton-%EA%B5%AC%ED%98%84/</guid>
        
        <category>Design pattern</category>
        
        <category>Python</category>
        
        
      </item>
    
      <item>
        <title>[python] thread condition</title>
        <description>Thread Condition


  Event와 Lock을 섞은 기능
  Condition을 잡으면 모든 스레드가 lock을 잡은 것처럼 멈춘다
  notify 를 실행하면 다시 동작


import time
import logging
import threading


logging.basicConfig(level=logging.DEBUG, format=&quot;(%(threadName)s) %(message)s&quot;)


def receiver(condition):
    logging.debug(&quot;Start reciever&quot;)

    with condition:
        logging.debug(&quot;Waiting&quot;)
        condition.wait()
        time.sleep(1)
        logging.debug(&quot;End&quot;)


def sender(condition):
    logging.debug(&quot;Start sender&quot;)

    with condition:
        logging.debug(&quot;Send notify&quot;)
        condition.notifyAll()
        logging.debug(&quot;End&quot;)


def main():
    condition = threading.Condition()

    for i in range(5):
        t = threading.Thread(target=receiver, name=&quot;receiver %s&quot; %i, args=(condition, ))

        t.start()

    send = threading.Thread(target=sender, name=&quot;sender %s&quot; %i, args=(condition, ))

    time.sleep(1)
    with condition:
        condition.notify(1)

    time.sleep(3)
    send.start()


if __name__ == &quot;__main__&quot;:
    main()



  5개의 스레드가 notify 를 기다리는 상황
  메인 스레드에서 하나의 스레드에 notify 를 보냄
  이후 모든 스레드에게 nofity 를 보내는 상황


condition


  lock과 비슷하게 사용되며, 사용하는 스레드가 공유
  notify 가 올때까지 기다리며, notify 를 받으면 그 이후를 실행
  마지막에 있는 time.sleep() 에 10초를 넣으면 10초동안 1, 2, 3, 4번 스레드가 기다린다.


</description>
        <pubDate>Wed, 11 Dec 2019 00:00:00 +0000</pubDate>
        <link>https://don2101.github.io//2019/12/11/Python-Thread-Condition/</link>
        <guid isPermaLink="true">https://don2101.github.io//2019/12/11/Python-Thread-Condition/</guid>
        
        
      </item>
    
      <item>
        <title>Reentrant Lock</title>
        <description>Thread Reentrant Lock


  스레드에서 락을 잡은 상태에서 다른 클래스나 함수를 호출할 때, 그 클래스나 함수에서 다시 락을 잡아야 하는 경우가 발생 =&amp;gt; 교착상태 발생
  재진입이 가능한 RLock(Re-entrant Lock) 사용


예시 코드

import time
import logging
import threading

logging.basicConfig(level=logging.DEBUG, format=&quot;(%(threadName)s) %(message)s&quot;)

RESOURCE = 0

def set_zero(lock, end=False):
    logging.debug(&quot;Start set zero&quot;)
    
    while True:
        with lock:
            logging.debug(&quot;Grab Lock, RESOURCE to 0.&quot;)
            RESOURCE = 0
            time.sleep(0.5)
        
        time.sleep(1)

        if end:
            break

def set_one(lock, end=False):
    logging.debug(&quot;Start set one&quot;)
    
    while True:
        with lock:
            logging.debug(&quot;Grab Lock, RESOURCE to 1.&quot;)
            RESOURCE = 1
            time.sleep(0.5)
        
        time.sleep(1)

        if end:
            break


def set_reverse(lock):
    logging.debug(&quot;Start reverse&quot;)

    with lock:
        logging.debug(&quot;Grab Lock, reverse&quot;)

        if RESOURCE == 1:
            set_zero(lock, True)
        else:
            set_one(lock, True)

    logging.debug(&quot;Reversed&quot;)

def main():
    lock = threading.RLock()

    zero = threading.Thread(target=set_zero, name=&quot;zero&quot;, args=(lock,))
    zero.setDaemon(True)
    zero.start()

    one = threading.Thread(target=set_one, name=&quot;one&quot;, args=(lock,))
    one.setDaemon(True)
    one.start()

    time.sleep(6)

    reverse = threading.Thread(target=set_reverse, name=&quot;reverse&quot;, args=(lock,))
    reverse.start()

if __name__ == &apos;__main__&apos;:
    main()
    



  set_one과 set_zero에서 각각 lock을 잡으며, RESOURCE 를 1과 0으로 변경하는 작업
  reverse 스레드에서는 set_one과 set_zero 를 호출
  reverse 스레드에서 set_one과 set_zero를 호출하면 각 함수에서 lock을 얻기 위해 무한정 기다릴 수 있다.
  이를 방지하기 위해 re-entrant lock을 사용하여 reverse에서 잡은 락을 set_zero나 set_one에서 잡을 수 있도록 한다.


실행 결과



RLock을 사용하지 않을 경우




  set_reverse 내부의 set_one 을 실행하면서 lock을 잡지 못하기 때문에 교착상태 발생

</description>
        <pubDate>Mon, 09 Dec 2019 00:00:00 +0000</pubDate>
        <link>https://don2101.github.io//2019/12/09/Python-Reentrant-Lock/</link>
        <guid isPermaLink="true">https://don2101.github.io//2019/12/09/Python-Reentrant-Lock/</guid>
        
        <category>Python</category>
        
        <category>Thread</category>
        
        
      </item>
    
      <item>
        <title>Docker와 Docker Portainer</title>
        <description>Docker




  컨테이너 기반 오픈소스 가상화 플랫폼




1. 개념

Container


  격리된 공간에서 프로세스가 동작하게 하는 기술
  프로세스를 격리 시켜 작동하는 방식 사용
  프로그램을 독립된 프로세스간 영향을 미치지 않고 독립적으로 실행




기존 가상화 OS와 다른 점


  가상화는 OS 전체를 가상화 하기 때문에 무겁고 용량이 큰 반면, 도커는 프로세스 자체를 격리하기 때문에 추가적인 부하가 적다.
  가상화는 OS를 생성시점에 필요한 자원을 할당해 놓고 사용하지만, 도커는 필요한 만큼의 자원을 사용하고 반납하기 때문에 자원 이용이 효율적





  작동구조(가상환경과 비교). 출처






Image


  컨테이너 생성 및 실행에 필요한 파일과 설정값을 갖고있는 파일
    
      Ubuntu 이미지, Mysql 이미지, Nginx 이미지…
    
  
  컨테이너 실행에 필요한 모든 파일과 설정을 갖고 있기 때문에 추가 설치 및 의존성 관리가 필요 없다.
    
      독립된 프로세스 공간에서 Python을 사용하고 싶다면 Python 이미지를 받아서 컨테이너화 해서 사용
    
  
  이미지는 hub.docker.com 등의 사이트에서 받아서 사용할 수 있으며, 자신이 만든 이미지를 공유하는 것도 가능




volume


  Docker 내부에서 사용하는 데이터를 컨테이너가 아닌 호스트에 저장하는 방식
  컨테이너 내부의 폴더와 호스트의 폴더를 동기화 시켜 도커 내부 데이터를 관리





  작동 구조. 출처






2. 기본 명령어

docker ps: 컨테이너 출력. -a 를 붙이면 실행을 멈춘 컨테이너도 출력

docker exec -it &amp;lt;컨테이너 이름&amp;gt; bash: bash창으로 컨테이너에 접속



추가 명령어 모음

도커 명령어 모음



3. Docker Portainer


  Docker Container를 GUI환경에서 편하게 관리할 수 있는 도구
  image화 되어 있으며, docker를 통해 설치하고 사용한다.





  설치 방법


docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer



  image를 받아 호스트의 9000번 port와 게스트의 9000번 포트를 연결
  게스트와 호스트의 볼륨을 연결 후 작동




</description>
        <pubDate>Fri, 06 Dec 2019 00:00:00 +0000</pubDate>
        <link>https://don2101.github.io//2019/12/06/Docker-Docker%EC%99%80-Docker-container/</link>
        <guid isPermaLink="true">https://don2101.github.io//2019/12/06/Docker-Docker%EC%99%80-Docker-container/</guid>
        
        <category>Docker</category>
        
        
      </item>
    
      <item>
        <title>리눅스 리다이렉션과 파이프</title>
        <description>Linux Redirection




  표준 스트림의 흐름을 바꾸는 것
  특수기호 &amp;gt; 과 &amp;lt; 을 사용




1. Stream


  일반적인 커맨드의 프로세스는 입력 스트림, 표준 출력 스트림, 오류 출력 스트림을 갖는다.
  모든 스트림은 일반적인 문자열로 콘솔에 출력한다.




2. Redirection


  프로세스의 스트림을 콘솔이 아닌 파일로 사용하는 등의 흐름을 바꾸는데 사용





  파일 목록을 텍스트 파일로 저장


ls &amp;gt; ls.txt



  ls는 디렉토리와 파일의 목록을 출력하는 것으로 콘솔에 출력된다.
  이것을 ls.txt 라는 파일로 흐름을 바꾸어 출력하게 되고, 이는 ls.txt에 파일의 목록을 기록한다.





  파일에 쓰여진 것을 콘솔로 출력


head &amp;lt; ls.txt



  head: 파일의 처음 부분을 설정한 라인만큼 출력
  파일 출력을 콘솔 출력으로 흐름을 바꾸게 되고 콘솔에 ls.txt 의  내용이 출력된다.




심화

head &amp;lt; ls.txt &amp;gt; ls2.txt



  왼쪽부터 실행된다.
  ls.txt의 내용을 head로 보내 내용을 출력하고 이를 ls2.txt에 기록한다.






Linux Pipe




  프로세스간 사용하는 기능
  | 표시로 사용
  
    
      
        
          A
          B: A의 커맨드 표준 출력을 B의 커맨드 표준 입력으로 사용
        
      
    
    
      A의 결과를 걸러서 B에서 사용
    
  




1. 사용 예시

ls | grep ls.txt



  ls 명령어의 출력 스트림을 grep 커맨드의 입력 스트립으로 보내고, grep의 인자로 ls.txt 를 넘기는 상황
  현재 디렉토리에 ls.txt 가 있다면 콘솔에 출력




2. grep


  입력으로 전달된 파일의 내용에서 특정 문자열을 찾고자 할 때 사용
  정규표현식, 패턴 매칭 방식을 사용
  기본적으로 대소문자 구분


grep [option] [pattern] [file]



  grep을 파이프로 입력 스트림을 주는 경우에는 필터처럼 동작
  파일이나 디렉토리 명을 쓰지 않아도 패턴과 옵션으로 해당 패턴 탐색




혼합해서 사용

ls | grep ls.txt &amp;gt; ls2.txt



  현재 디렉토리의 파일, 디렉토리 리스트를 grep 으로 전송
  해당 리스트에서 ls.txt가 있는지 검색
  검색 결과를 ls2.txt에 기록


</description>
        <pubDate>Wed, 04 Dec 2019 00:00:00 +0000</pubDate>
        <link>https://don2101.github.io//2019/12/04/Linux-%EB%A6%AC%EB%88%85%EC%8A%A4-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98%EA%B3%BC-%ED%8C%8C%EC%9D%B4%ED%94%84/</link>
        <guid isPermaLink="true">https://don2101.github.io//2019/12/04/Linux-%EB%A6%AC%EB%88%85%EC%8A%A4-%EB%A6%AC%EB%8B%A4%EC%9D%B4%EB%A0%89%EC%85%98%EA%B3%BC-%ED%8C%8C%EC%9D%B4%ED%94%84/</guid>
        
        <category>Linux</category>
        
        
      </item>
    
      <item>
        <title>Django에서 queryset 사용하기</title>
        <description>Queryset의 특징



queryset


  django에서 ORM을 사용하기 위한 DB의 레코드




1. Queryset은 마지막 까지 지연(lazy)된다


  필터링을 하는 경우 .filter() 를 사용
  .filter() 를 사용한다 하더라도, django는 DB에 아무 쿼리도 전달하지 않는다.
    
      실제로 데이터에 접근하는 로직에만 작동
      이는 DB에 접근하는 횟수를 줄여 웹 앱이 느려지게 하는 것을 방지한다
    
  
  최종적으로 queryset을 순회(iterate)하는 경우에만 레코드를 가져온다




filter &amp;amp; iteration

post_set = Posts.obejcts.filter(title=&quot;branch&quot;)

for post in post_set:
    print(post.title)



  글 제목이 title인 모든 글을 나타낸다.
  최종적으로 iteration 하는 경우에만 query 실행




2. queryset은 캐시된다

evaluation


  queryset을 순회하는 시점에 쿼리셋에 해당하는 DB의 레코드를 가져온다(fetch)
  또한, 이는 모두 django model로 변환되며 이를 평가(evaluation)이라 한다.




cashing


  평가된 모델은 queryset 내장 캐시에 캐시된다.
  또다시 queryset을 순회해도 같은 query를 DB에 전달하지 않는다.





  두번의 순회를 하더라도 한번만 query를 전달


for post in post_set:
    print(post.title)
    
for post in post_set:
    print(post.title)




3. If 문에서는 queryset evaluation 발생


  queryset에 레코드가 있는지 검사한 후 있을 때만 순회하도록 할 수 있다.




사용 예시

post_set = Posts.objects.filter(title=&quot;branch&quot;)

# if문에서 queryset evaluation 실행

if post_set:
  # 순회시에는 캐시된 queryset 사용
  
  	for post in post_set:
    		print(post.title)




4. 결과 전체가 필요하지 않은 경우 queryset 캐시는 비효율적

post_set = Posts.objects.filter(title=&quot;branch&quot;)

# queryset eavalution 발생

if post_set:
  	print(&quot;For only one query, whole size of queryset is fetched&quot;)



  if문이 queryset evaluation을 발생시키므로 하나의 결과를 위해 전체 query를 가져오게 되어 비효율적이다




exist()


  최소한 하나의 레코드가 존재하는지 판단


post_set = Posts.objects.filter(title=&quot;branch&quot;)

if post_set.exists():
  	print(&quot;Exist method is useful for checking there is queryset has a record&quot;)



  .exists() 메소드는 최소한 하나의 레코드가 존재하는지 판별한다.




5. queryset이 엄청 큰 경우 queryset 캐시는 문제가 된다


  수많은 레코드를 다루는 경우 한번에 메모리에 올리는 것은 비효율적
  거대한 쿼리가 서버의 프로세스에 lock을 걸어 app이 죽을 수도 있다.




iterator()


  queryset 캐시를 방지하며 전체 레코드를 순회해야 할 때 사용
  데이터를 작은 덩어리로 쪼개서 가져오며, 사용한 레코드는 메모리에서 지운다.


post_set = Posts.objects.all()

for post in post_set.iterator():
  	print(post.title())



  전체 레코드의 일부만 가져오브로 메모리를 절약할 수 있다.
  그러나 같은 queryset을 순회하면 다시 evaluation이 발생하므로 조심해야 한다.




6. queryset이 엄청 큰 경우 if문도 문제가 된다


  queryset 캐시와 if/for문을 사용하여 상황에 따라 queryset을 순회하는 것을 정할 수 있다.
  그러나, queryset이 큰 경우 queryset 캐시는 고려할 수 없으므로 다른 방법을 사용




exists()와 iterator()를 함께 사용하는 방법

post_set = Posts.objects.all()

# exists()메서드로 레코드가 존재하는지 검사

if post_set.exists():
  	# 다음 query로 레코드를 조금씩 가져온다
  
  	for post in post_set.exists():
      	print(post.title)




고급 순회 메서드


  순회 진행을 정하기 전에 iterator() 메서드의 첫번째 레코드를 감지(peek)


post_set = Posts.objects.all()

# 이 때부터 첫 번째 query로 레코드를 가져오기 시작한다

post_iterator = post_set.iterator()

# 첫 번째 레코드를 감지(peek)

try:
  	first_post = next(post_iterator)
except StopIteration:
  	# 레코드가 없으면 아무일도 하지 않는다.
		pass
else:
  	# 레코드가 하나라도 존재하면, 모든 레코드를 순회
    from itertools import chain
    for post in chain([first_post], post_set):
      	print(post.mass)


</description>
        <pubDate>Sun, 24 Nov 2019 00:00:00 +0000</pubDate>
        <link>https://don2101.github.io//2019/11/24/Django-Django%EC%97%90%EC%84%9C-queryset-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0/</link>
        <guid isPermaLink="true">https://don2101.github.io//2019/11/24/Django-Django%EC%97%90%EC%84%9C-queryset-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0/</guid>
        
        <category>Django</category>
        
        
      </item>
    
      <item>
        <title>Celery를 사용한 Django에서 분산 비동기 처리</title>
        <description>Celery




  웹 서비스에서 응답을 받기 까지 오래 걸리는 작업은 사용자가 기다리기 힘들다
  이렇게 비용이 무거운 로직은 비동기로 처리하여, 사용자가 응답을 받은 후 결과를 받게 할 수 있다.
  Celery는 django에서 비동기 처리를 도와주는 프레임워크(worker)




1. 설치(Ubuntu)


  Celery 설치


pip install celery




2. 브로커 설정

브로커


  웹 서버에서 작업을 브로커에게 전달하고, celery 워커가 작업을 받아서 처리
  브로커는 웹에서 전달한 작업 요청을 큐에 담아 보관하고, 워커에 적절하게 분배
  브로커의 종류는 rabbitmq, redis 등을 사용할 수 있음



  Celery 작동 구조






rabbitmq 설치 및 유저 추가

apt-get install rabbitmq-server # rabbitmq 설치
rabbitmqctl add_user 사용자명 비밀번호 # 유저 추가
# server 실행. -detached: 백그라운드로 실행

rabbitmq-server -detached 
rabbitmqctl stop # rabbitmq 중지

rabbitmqctl start_app # localhost에서 어플리케이션 실행
rabbitmqctl stop_app # 어플리케이션 중지



  Rabbitmq에 접근해서 자원을 사용할 유저를 생성




vhost 추가 및 권한 설정

rabbitmqctl add_vhost /vhost # /vhost라는 vhost 추가
# /vhost와 guest를 연결. guest는 /vhost를 통해 자원에 접근

rabbitmqctl set_permissions -p /vhost guest &quot;.*&quot; &quot;.*&quot; &quot;.*&quot;



  rabbitmq의 각 유저가 사용할 가상 호스트를 추가한다
  vhost: rabbitmq의 자원에 접근할 수 있게 하는 인터페이스. 혹은 논리적 그룹.
    
      유저는 vhost를 통해 rabbitmq를 사용한다
    
  
  set_permissions 명령어를 통해 유저에게 message queue의 자원에 configure, writer, read할 권한을 부여한다.




3. worker 생성


  프로젝트 구조


├── celery_testing
├── db.sqlite3
├── manage.py
└── testing




workers.py

from celery import Celery

# Celery 객체를 정의. 객체의 이름과 브로커로 사용할 서버 주소를 설정
app = Celery(&apos;tasks&apos;, broker=&quot;amqp://&amp;lt;유저&amp;gt;:&amp;lt;비밀번호&amp;gt;@localhost:5672//&amp;lt;vhost이름&amp;gt;&quot;)

@app.task
def sum_number(x, y):
    num = 0

    for i in range(x) :
        for j in range(y):
            num += 1

    return num



  비용이 많이 소모되는 작업 수행시 사용자에게 처리 완료를 알리고, 비동기로 처리
  비동기로 처리할 작업을 모듈화




views.py

from django.shortcuts import render
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .workers import sum_number

# Create your views here.
@api_view([&quot;GET&quot;])
def sum_it(request, x, y):
    sum_number.delay(x, y)
        
    return Response(data={&quot;res&quot;: &quot;good&quot;})



  .delay()는 해당 작업을 비동기로 처리함을 의미
    
      .delay()를 사용하지 않고 실행하면 일반적인 실행 방식으로 실행된다.
    
  




settings.py

CELERY_IMPORTS = [&quot;testing.workers&quot;, ]



  django application내에 비동기 처리 모듈을 import




4. celery 실행

celery -A 실행app이름 worker --loglevel=info



  실행할 어플리케이션 이름(여기서는 workers)를 celery가 worker로 실행한다
  loglevel을 설정하여 로깅도 가능





  결과





  url을 통해 10000*10000 연산을 요청할 경우 결과가 바로 return된다.
  연산은 비동기적으로 실행되며 5.5초 후 연산이 완료됨을 알려준다.


</description>
        <pubDate>Thu, 07 Nov 2019 00:00:00 +0000</pubDate>
        <link>https://don2101.github.io//2019/11/07/Django-Django%EC%97%90%EC%84%9C-%EB%B6%84%EC%82%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC/</link>
        <guid isPermaLink="true">https://don2101.github.io//2019/11/07/Django-Django%EC%97%90%EC%84%9C-%EB%B6%84%EC%82%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC/</guid>
        
        <category>Django</category>
        
        <category>Celery</category>
        
        
      </item>
    
      <item>
        <title>JWT기반 인증 방식</title>
        <description>Json Web Token




  django에서 사용하는 인증, 로그인 방식과 jwt를 사용하는 방식을 비교한다




1. 계정 정보를 헤더에 넣는 방식


  계정 정보를 http 헤더에 담아 요청하는 방식




단점


  데이터를 요청할 때 마다 사용자 개인 정보를 담아 보내는 것은 보안상 아주 취약하다
  또한 서버에서 요청이 올 때 마다 id, pw를 통해 인증을 해야 하므로 매우 비효율적




2. Session / Cookie 방식

작동 순서


  사용자가 로그인을 요청
  서버에서 사용자를 확인한 뒤, 회원정보를 세션저장소에 저장하고 사용자에게 고유한 ID값을 부여
  서버에서 세션 ID를 사용자에게 전송하고 사용자는 이를 Cookie에 저장
  이후 인증이 필요한 상황마다 쿠키를 헤더에 실어서 전송
  서버에서 쿠키에 저장된 데이터를 세션저장소에 있는 데이터와 비교하여 사용자를 인증
  인증이 완료되면 사용자에게 정보를 제공





  기본적으로 세션저장소를 필요로 한다
    
      ex) Redis
    
  




Session vs Cookie


  Session: 서버에서 사용자의 정보를 저장하는 저장소
  Cookie: 사용자(브라우저)측에서 서버에서 제공된 정보를 저장하는 공간




단점


  쿠키만으로 인증한다는 것은 서버의 자원을 사용하지 않는다는 것
  http가 탈취되면 쿠키 또한 탈취되며, 올바르지 않은 사용자의 접근이 가능하다
  세션저장소로 인해 서버측의 부하가 높다




3. 토큰(JWT)기반 인증 방식


  인증에 필요한 정보를 암호화 해서 보내는 방식
  Session / Cookie방식과 유사하며, 사용자는 token을 http 헤더에 실어 보낸다





  예시


https://jwt.io



구조

토큰을 구성하는 요소


  Header: 암호화 할 알고리즘을 명시하는 부분
  Payload: 서버에서 보낼 데이터가 담기는 부분. 보통 id, 유효 기간 등을 명시한다
  Verify Signature: Base64 방식으로 인코딩한 Header, payload 그리고 secret key를 더한 후 암호화




최종으로 만들어 지는 token 결과

Encoded Header + Encoded Payload + Verify Signature



Verify Signature


  결국 Verify Signature에는 Header와 Payload의 정보가 담긴다


Verify Signature = Encoded Header + Encoded Payload + secret key


  복호화 할 때는 secret key가 필요하며 이는 서버에서 지정한다




특징


  Header와 Payload는 인코딩 될 뿐 암호화 되지 않는다
  따라서 누구나 디코딩 하면 정보를 확인할 수 있다(중요 정보를 payload에 담지 않는다)
  Session / Cookie 방식과 다르게 악의적인 사용자가 token을 가로챈다 하더라도 인가를 받을 수 없다
    
      악의적인 사용자가 Payload의 id정보를 자신의 id로 변경하면, Verify Signature의 값이 변경되기 때문에 유효한 사용자가 아님을 판단할 수 있다
    
  
  token 안에 유저의 정보를 담기 때문에 서버에서 별도의 저장소가 필요하지 않다




작동 순서


  사용자가 로그인을 요청
  서버에서 사용자를 확인한 뒤, 여러 정보(id, expired date…)를 Payload에 포함
  secret key를 통해 정보를 암호화 하고 jwt를 생성 한 뒤, 사용자에게 전송
  사용자는 jwt를 session storage등에 저장하고, 인증이 필요할 때 마다 jwt를 전송
  서버에서 jwt의 Verify Signature를 secret key를 통해 복호화 한 뒤, 조작 여부, 유효 기간을 판별
  인증이 완료되면 사용자에게 정보를 제공






참고 사이트

https://tansfil.tistory.com/58
</description>
        <pubDate>Thu, 31 Oct 2019 00:00:00 +0000</pubDate>
        <link>https://don2101.github.io//2019/10/31/Django-Json-Web-Token/</link>
        <guid isPermaLink="true">https://don2101.github.io//2019/10/31/Django-Json-Web-Token/</guid>
        
        <category>Web</category>
        
        <category>Jwt</category>
        
        
      </item>
    
      <item>
        <title>Thread의 lock</title>
        <description>Thread Lock



스레드에서 자원을 사용할 때


  서로 다른 스레드가 동시에 자원에 접근하면 무결성이 훼손될 수 있다.
  스레드를 사용할 때는 자원에 대한 무결성을 보장해야 한다.
  Python 내장 자료구조중에는 무결성을 보장하는 자료구조 또한 존재
  Lock을 통해 무결성을 보장할 수 있다.




lock 객체 메서드


  threading.Lock(): lock 객체 생성
  .acquire(True): 스레드에서 lock을 잡는 요청
    
      인자에 False를 전달하면 lock을 잡을 때 까지 blocking 되지 않고 다음 로직 수행.
    
  
  .release(): lock을 헤제




2개의 스레드가 lock, release를 반복하는 예시

import time
import logging
import threading

logging.basicConfig(level=logging.DEBUG, format=&quot;(%(threadName)s) %(message)s&quot;)

def blocking_lock(lock):
    logging.debug(&quot;Start blocking lock&quot;)

    while True:
        time.sleep(1)
        lock.acquire()

        try:
            logging.debug(&quot;Grab it&quot;)
            time.sleep(0.5)
        
        finally:
            logging.debug(&quot;Release&quot;)
            lock.release()


def nonblocking_lock(lock):
    logging.debug(&quot;Start nonblocking lock&quot;)

    attempt, grab = 0, 0

    while grab &amp;lt; 3:
        time.sleep(1)
        logging.debug(&quot;Attempt&quot;)
        success = lock.acquire(False)

        try:
            attempt += 1
            
            if success:
                logging.debug(&quot;Grap it&quot;)
                grab += 1
        
        finally:
            if success:
                logging.debug(&quot;Release&quot;)
                lock.release()

    logging.debug(&quot;Attempt: %s, grab: %s&quot; % (attempt, grab))


def main():
    lock = threading.Lock()

    blocking = threading.Thread(target=blocking_lock, name=&quot;blocking&quot;, args=(lock,))
    blocking.setDaemon(True)
    blocking.start()

    nonblocking = threading.Thread(target=nonblocking_lock, name=&quot;nonblocking&quot;, args=(lock,))
    nonblocking.start()


if __name__ == &quot;__main__&quot;:
    main()





  1개의 lock을 설정
  blocking_lock 스레드는 1초 쉬고, lock을 잡을 때 까지 대기하는 작업을 반복
    
      lock을 0.5초 동안 잡고 release
    
  
  nonblocking_lock은 lock을 잡으려는 시도를 반복
    
      attempt와 grab 변수는 lock을 잡으려는 시도, 실제 잡은 횟수를 의미
    
  
  nonblocking_lock이 3번의 lock을 쥐면, 프로그램을 종료





  결과





  nonblocking_lock이 5번의 시도 중 3번의 lock을 획득함을 볼 수 있다.





  nonblocking_lock에서 aquire에 False 인자를 전달하지 않으면





  nonblocking_lock이 lock을 잡을 때 까지 대기하게 되므로 시도 마다 lock을 획득할 수 있다.



</description>
        <pubDate>Wed, 30 Oct 2019 00:00:00 +0000</pubDate>
        <link>https://don2101.github.io//2019/10/30/Python-Thread%EC%9D%98-Lock/</link>
        <guid isPermaLink="true">https://don2101.github.io//2019/10/30/Python-Thread%EC%9D%98-Lock/</guid>
        
        <category>Python</category>
        
        <category>Thread</category>
        
        
      </item>
    
      <item>
        <title>Thread의 Event</title>
        <description>Thread Event




  파이선의 threading 모듈에서 스레드간 통신을 위해 이벤트를 사용
  이벤트 설정, 초기화, 기다리기 등의 동작을 제공
  특정 조건에 따라 스레드를 동작시킬 때 유용
  스레드의 인자로 넘겨 이벤트를 확인한다




메서드


  threading.Event(): 이벤트 객체 생성
  .isSet(): 이벤트가 설정 되었는지 확인
  .wait(num): num 초 동안 대기




2개의 Event를 다루는 예시 코드

import time
import logging
import threading

logging.basicConfig(level=logging.DEBUG, format=&quot;(%(threadName)s) %(message)s&quot;)

def first_wait(e1, e2):
    while not e1.isSet():
        event = e1.wait(1)
        logging.debug(&quot;Event status: (%s)&quot;, event)
        
        if event:
            logging.debug(&quot;e1 is set.&quot;)
            time.sleep(3)
            logging.debug(&quot;Set e2&quot;)
            e2.set()
            

def second_wait(e2):
    while not e2.isSet():
        event = e2.wait(1)
        logging.debug(&quot;Event status: (%s)&quot;, event)

        if event:
            logging.debug(&quot;e2 is set.&quot;)
            

def main():
    # Event 객체 생성
    
    e1 = threading.Event()
    e2 = threading.Event()
		
    # 두 이벤트를 시작한다
    
    t1 = threading.Thread(name=&quot;first&quot;, target=first_wait, args=(e1, e2))
    t1.start()

    t2 = threading.Thread(name=&quot;second&quot;, target=second_wait, args=(e2, ))
    t2.start()

    logging.debug(&quot;Wait...&quot;)
    time.sleep(5)
    logging.debug(&quot;Set e1&quot;)
    e1.set()
    time.sleep(5)
    logging.debug(&quot;Exit&quot;)
    

if __name__ == &apos;__main__&apos;:
    main()



  프로그램을 2개의 흐름으로 나눈다
  스레드를 start 하면 이벤트가 시작된다
  이벤트 e1은 5초동안 event가 set 되었는지 확인.
    
      e1이 set되면 종료
    
  
  이벤트 e2는 e1이 완료된 후 3초동안 set 되었는지 확인





  결과





</description>
        <pubDate>Tue, 29 Oct 2019 00:00:00 +0000</pubDate>
        <link>https://don2101.github.io//2019/10/29/Python-Thread%EC%9D%98-Event/</link>
        <guid isPermaLink="true">https://don2101.github.io//2019/10/29/Python-Thread%EC%9D%98-Event/</guid>
        
        <category>Python</category>
        
        <category>Thread</category>
        
        
      </item>
    
  </channel>
</rss>
