잡다한 프로그래밍 :: 잡다한 프로그래밍
반응형

대용량 트래픽 처리 시스템이란?

- 하나의 서버, 데이터베이스로 감당하기 힘든 부하를 처리하는것 > 다수의 서버와 데이터베이스를 마치 하나인것처럼 동작하게한다 (이안에는 여러 마이크로서비스들 포함)

 

대용량 트래픽 처리를 위한 특징 세가지

1. 고가용성

- 언제든 서비스를 이용할 수 있어야함

 

2. 확장성

- 시스템이 비대해짐에 따라 증가하는 데이터와 트래픽에 대응할 수 있어야한다.

 

3. 관측 가능성

- 문제가 생겼을 때 빠르게 인지할 수 있어야하고 문제의 범위를 최소화 할 수 있어야함

 앞서 DB의 병목현상에 대해 알아봤는데, 점진적으로 대용량 트래픽 처리 시스템을 발전시켜보자

1. 기본 구성

2. 사용자의 증가로인해 서버의 응답속도가 느려짐 > 서버의 스케일 아웃

해당 서버의 부하를 분산하는건 로드밸런서(nginx...)의 역할 (RR알고리즘....등)

 

3. 서버를 충분히 늘렸음에도 DB응답속도가 느려짐

> 쿼리튜닝(인덱스 등), 로컬캐시 (각 서버의 메모리에 캐싱), 글로벌캐시 (redis)

글로벌 캐시 예시

캐시를 이용할경우, 주기, 만료정책 등을 고려해야함

 

> DB 다중화 (스케일 아웃) 아키텍처 참고 https://blog.naver.com/takane7/221440417322

 

웹 어플리케이션 시스템 아키텍쳐의 변화

초기 아키텍쳐에서부터 시작해 클러스터링 아키텍쳐로의 확장을 고려하기까지의 스토리를 다뤄보고자 합니...

blog.naver.com

4. 이메일, 알림과 같은 대외기관 서버와의 연동이 많이 필요 해짐, 대외기관의 응답이 느려짐

> 클라이언트는 대외기관의 응답을 기다리느라, 요청을 기다리게 됨

비동기 큐 = kafka rabbitmq...(스레드풀을 이용한 비동큐도 가능)

비동기 큐에 요청을 쌓으므로서, 대외기관과의 트랜잭션을 클라이언트 요청에서 제외 시킬 수 있음 (내 서버에 데이터에만 의존하게 됨)

비동기 큐를 사용하면, 대외기관의 적정 TPS의 맞게 요청량을 조절할 수 있음

반응형
반응형

웹 아키텍쳐에서 DB는 병목 지점이다. (대용량 처리시 아키텍쳐를 어떤식으로 구성해야하는지 고민해보자)

 

1. 스케일업(scale-up), 스케일 아웃 (scale-out)이란?

 

  • 하나의 서버의 사양을 보다 높은 사양으로 업그레이드하는 것을 말한다.(더 좋은 성능으로 더많은 요청을 처리)

스케일업

  • 서버의 대수를 늘려 부하를 분산시켜 더많은 요청을 처리

스케일 아웃

 

  스케일업 스케일 아웃
유지보수 및 관리 쉬움 여러 노드를 관리해야함 (분산 필요)
확장성 제약있음 (서버의 성능을 높이는데 한계) 자유로움
장애복구 서버가 1대, 다운타임 존재 장애에 탄력적인 대응 가능

 

※ 스케일 아웃시 같은 입력에대해서는 항상 같은 결과를 반환해야함

- 따라서 다음과 같은 구조를 띄게 된다.

데이터 베이스의 스케일 아웃은 어려운가?

- 스케일 아웃에는 상태가 없어야 하는데, DB에는 데이터의 상태가 존재함 따라서 서버보다 더많은 비용이 발생 (스케일 아웃이 가능한 방법이 존재함)

※ 참고 https://willseungh0.tistory.com/131

 

따라서 서버 아키텍쳐는 상태관리는 DB에 위임 서버는 상태 관리를 하지않는 형태로 발전

 

DB는 왜 병목지점?

  • 앞서 말한 스케일 아웃이 어렵기 때문에 병목지점이 될 수 있음
  • DB는 디스크에서 데이터를 가져오기 때문에 속도가 상대적으로 느림 (병목지점이 될 수 있음)
  • 서버와 DB사이에 네트워크 구간이 존재 (네트워크 상황에 따라 요청속도가 느려질 수 있음)
반응형
반응형

1. 엘라스틱 서치 쿼리에대해 간단하게 정리하고자 한다.

 

기본적으로 아래와 같은 형태를 띄고 있다.

- query: 기존 DB의 where 조건문에 해당 (query, filter)

- size: 0으로 설정할경우 결과는 보지않고 집계결과만 보겠다는 의미

- aggs: 집계 부분 기존 쿼리의 Group by에 해당

GET _search
{
  "query":{
  },
  "size":0,
  "aggs": {
  }
}

 

집계 정리

1. SELECT count(*) FROM test group by userNo

- 유저 번호로 그룹핑한 결과를 보고 싶을때

{
  "size":0,
  "aggs": {
    "user_no (집계 대상 이름)" : {
      "terms": {
        "field": "user.no"
      }
    }
  }
}

결과

  "aggregations" : {
    "aa" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : 0,
          "doc_count" : 66
        },
        {
          "key" : 107,
          "doc_count" : 14
        }
        ...
        이하 생략

 

2. userNo 값으로 집계한 결과를 대상으로 특정 url로 집계하고 싶을때

{
  "size":0,
  "aggs": {
    "aa" : {
      "terms": {
        "field": "user.no"
      },
      "aggs": {
        "url_name": {
          "terms": {
            "field": "url.keyword"
          }
        }
      }
    }
  }
}

결과

  "aggregations" : {
    "user_no" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : 0,  # 유저별 그룹핑
          "doc_count" : 664745,
          "url" : {
            "doc_count_error_upper_bound" : 94,
            "sum_other_doc_count" : 4659,
            "buckets" : [ # url별 그룹핑
              {
                "key" : "http://aaa.com",
                "doc_count" : 655
              },
              {
                "key" : "https://test.com/",
                "doc_count" : 469
              },
              {
              .. 이하생략

 

3. 일별, 사용자별로 그룹핑 한다면? (2번은 집계한 결과의 집계, 3번과차이가 있음)

{
  "size":0,
  "aggs": {
    "abnormal_status" : {
      "composite": {
        "sources": [
         # time을 일별로 묶어줌, calendar_interval = day, week, hour..
          { "date": { "date_histogram": { "field": "time", "calendar_interval": "day", "format": "yyyy-MM-dd"}}},
          { "name": { "terms": { "field": "userNo" } } }
        ]
      }
    }
  }
}

 

결과

 "aggregations" : {
    "abnormal_status" : {
      "after_key" : {
        "date" : "2022-07-14",
        "name" : 2
      },
      "buckets" : [
        {
          "key" : {
            "date" : "2022-01-03",
            "name" : 0
          },
          "doc_count" : 1
        },
        {
          "key" : {
            "date" : "2022-05-30",
            "name" : 0
          },
          "doc_count" : 1
        }
        ... 이하생략

 

4. count(distinct column) > 사용자별로 (그룹핑) 몇개의 ip를 가지고있는가 (count(distint))

{
  "size":0,
  "aggs": {
    "user" : {
      "terms": {
        "field": "userNo"
      },
      "aggs": {
        "ip": {
          "cardinality": {
            "field": "ip"
          }
        }
      }
    }
  }
}

결과

"aggregations" : {
    "multi_ip_top_n" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : 0,
          "doc_count" : 45,
          "ip_cardinality" : {
            "value" : 3
          }
        },
        {
          "key" : 2,
          "doc_count" : 9,
          "ip_cardinality" : {
            "value" : 2
          }
        },
        {
          "key" : 25,
          "doc_count" : 5,
          "ip_cardinality" : {
            "value" : 2
          }
        },
        .. 이하 생략

 

5. sum, avg, min, max > status

status를 사용할경우 앞의 4가지를 전부 구할 수 있다.

  "size":0,
  "aggs": {
    "user_no" : {
      "terms": {
        "field": "user.no"
      },
      "aggs": {
        "sum": {
          "sum": {
            "field": "queryCnt"
          }
        },
        "avg": {
          "avg": {
            "field": "queryCnt"
          }
        },
        "min": {
          "min": {
            "field": "queryCnt"
          }
        },
        "max": {
          "max": {
            "field": "queryCnt"
          }
        }
      }
    }
  }
}
{
  "query":{
    "bool":{
      "filter": [
        { "range": { "reqTime": { "gte": 20210623000000, "lte": 20220723235959 }}}
      ]
    }
  },
  "size":0,
  "aggs": {
    "user_no" : {
      "terms": {
        "field": "user.no"
      },
      "aggs": {
        "stats": {
          "stats": {
            "field": "queryCnt"
          }
        }
      }
    }
  }
}

 

집계시 기억해야 할 점

 

엘라스틱 서치에서 제공하는 기본 집계의 수는 10000건이다. 이 이상으로 설정할 수 있지만 권장하지 않는다하고, 따라서 이에 맞게 결과를 도출하려 해야한다.

 

그럼에도 불구하고 10000건이상, 혹은 전체를 불러오고 싶을경우?(ex 페이징처리가 필요하다)

 

composite집계를 이용하여 처리할 수 있다.

앞선 composite 집계 결과를 보면 다음과 같이 after_key를 포함하고있다. 집계시 해당 키값을 넣어주게 되면 해당 키 다음부터 조회할 수 있다.

이런 방법으로 전체를 조회하거나 원하는 페이지를 조회할 수 있다.

 "aggregations" : {
    "abnormal_status" : {
      "after_key" : {
        "date" : "2022-07-14",
        "name" : 2
      },
      "buckets" : [
        {
          "key" : {
            "date" : "2022-01-03",
            "name" : 0
          },
          "doc_count" : 1
        },
        {
          "key" : {
            "date" : "2022-05-30",
            "name" : 0
          },
          "doc_count" : 1
        }
        ... 이하생략

 

{
  "size":0,
  "aggs": {
    "abnormal_status" : {
      "composite": {
        "sources": [
         # time을 일별로 묶어줌, calendar_interval = day, week, hour..
          { "date": { "date_histogram": { "field": "time", "calendar_interval": "day", "format": "yyyy-MM-dd"}}},
          { "name": { "terms": { "field": "userNo" } } }
        ],
        "size": 10000,
        "after": #해당부분에 afterKey값을 넣어주면 그 위치부터 시작함
      }
    }
  }
}

 

※ 이외에 알아둘만한 집계 함수

percentiles > 해당 퍼센트에 해당하는 기준값이 얼마인지 알 수 있다.

    "percentiles_bucket" : {
      "values" : {
        "75.0" : 75,
        "80.0" : 80,
        "85.0" : 85,
        "90.0" : 90,
        "95.0" : 95,
        "99.0" : 99
      }
    }

script사용

아래와 같이 집계중 script를 사용할 수 있는 집계들이 있다.

아래 집계는 time 필드를 HH형태로 바꾸어 집계한 결과이다.

ex 20220805124333 > 12

  "aggs": {
    "hourly_privacy_count" : {
      "terms": {
        "script": {"source": "doc['time'].value.getHour()"}
      }
 }
반응형
반응형

자바스크립트의 호이스팅에 대해 알아보는 시간을 가진다.


1. 호이스팅 (Hoisting)

- 자바스크립트에서 선언하는 변수값은 모두 유효 범위의 최상단으로 선언된다 = 즉 나는 밑에서 선언했지만 제일 상단에서 선언한것처럼 동작한다. 라는 의미를 가진다.

  • 자바스크립트 Parser가 함수 실행 전 해당 함수를 한번 훑는과정을 거친다.
  • 함수 안에 존재하는 변수/함수 선언에 대한 정보를 내부적으로 끌어올려서 처리한다(코드상의 변화가 있지는 않음)

 호이스팅의 대상

  • var 변수 선언, 함수 선언문에서 호이스팅이 일어난다.
    • let, const 에서는 호이스팅이 일어나지 않음 (ES6에서 개선) ※ 호이스팅은 일어나나, 발생하지 않는것처럼 참조 에러가 발생함

변수는 어떤식으로 생성 될까?

1. 변수의 선언

  • 변수를 실행 컨텍스트의 변수 객체에 등록함.

2. 초기화 단계

  • 변수 객체에 등록된 변수를 위한 공간을 메모리에 확보한다.
  • 이때 변수는 undefined로 초기화 된다.

3. 할당 단계

  • 실제 undefined로 초기화된 변수에 값을 할당한다.

 

앞서 말한 var키워드는 선언, 초기화가 한번에 이루어진다. 즉 스코프에 변수를 등록하고 공간을 확보한 후 undefined로 초기화한다. 하지만 let키워드는 선언과 초기화가 분리되어 진행되므로, 초기화 이전에 접근하면 에러가 발생한다.

 

- 앞서 이론적인 부분을 코드적으로 이해해보도록 하자

변수 선언에서의 호이스팅 예시

console.log(val); //선언 + 초기화
val = 'text'; //선언 + 초기화 + 할당
var val = 'hi';

- 해당 코드를 js가 아닌 다른 언어로 생각했을경우 1 번째 line에서 에러가 발생해야한다 하지만 결과는 undefined값이 찍힌다. 이유는 앞선 선언 + 초기화가 되어있기 때문이다

console.log(val); // 호이스팅에 의해 끌어올려졌지만 초기화는 안된 상태
let val = 'hi'

let을 사용하게되면 선언만 이루어지고 초기화는 되지않아서 에러가 발생한다.

 

함수 선언에서의 호이스팅 예시

test(); // 함수 선언문 정상동작 호이스팅 O
test2(); // 함수 표현식 오류 발생 호이스팅 X 

function test() {
	console.log('test');
}

var test2 = function() {
	console.log('test2');
}

- 함수 선언문같은 경우 호이스팅이 된다. 하지만 함수 표현식의 경우 호이스팅이 이루어지지 않는다.

함수 선언문의 경우 = 함수 전체가 최상단으로 호이스팅 된다. 하지만 함수 표현식의 경우 변수가 호이스팅 되기때문에 에러가 발생한다

 

클래스의 호이스팅

그렇다면 ES6에 추가된 클래스는 어떨까? (let, const가 변수 선언 전에 사용하면 에러가발생하듯 class의 경우도 오류가 발생할까?)

let test = new Test('hi'); // ReferenceError: Person is not defined
console.log(peter);

class Test {
  constructor(val) {
    this.val = val;
  }
}

생각한 대로 클래스는 ReferenceError를 내뱉는다. 함수 선언문의 경우 호이스팅 되어 이전에 사용할 수 있었지만. ES6에서 추가된 클래스의 경우는 호이스팅 되어도 초기화가 되지않기 때문에 (let, const 처럼) 에러를 내뱉는다.

 

- 마찬가지로 클래스 표현식 let val = class Test {....} 로 정의할 경우도 에러를 발생시킨다.

 

이상 간단한 호이스팅 정리였다.

반응형
반응형

자바스크립트의 프로토 타입, 클래스에 대해 알아보는 시간을 가진다.


1. 프로토타입 (prototype)

- 프로토 타입은 JS에 클래스가 생기기전 객체를 다를 객체로 상속하기 위해 사용하는 개념이다 (ES6의 클래스는 타 언어의 클래스와 거의 흡사하고, 내부적으로는 prototype을 따른다고 함)

 

- 앞서 JS에서 객체 생성자란 무엇인지, 상속은 어떤방식으로 하는지 간단하게 확인해보자.

function People(name, age) {
  this.name = name;
  this.age = age;
  this.info = function() {
    console.log('이름: ' + name);
    console.log('나이: ' + age);
  }
}

const test = new People('A', '30');
const test2 = new People('B', '20');

test.info();
test.info();

- 결과는 예상한대로 이름 : A, 나이 :30 이름: B, 나이: 20이 나온다.

 

여기서 프로토타입을 사용하면 다음과 같이 바뀐다.

function People(name, age) {
  this.name = name;
  this.age = age;
}

People.prototype.info = function() {
    console.log('이름: ' + this.name);
    console.log('나이: ' + this.age);
}

const test = new People('A', '30');
const test2 = new People('B', '20');

test.info();
test.info();

그렇다면 프로토타입을 사용했을때 무엇이 달라질까?

 

- 프로토 타입을 사용하지 않은경우 객체하나를 생성할때마다 info라는 메소드가 할당된다. 즉 2개의 객체를 생성하면 2개의 info가 할당되는 셈이다

 

- 반면 프로토타입을 사용한경우 People객체는 하나의 info메소드를 생성하고 객체에서 참조하여 사용하게 된다.

즉 2개의 객체를 생성했지만 info 메소드는 1개만 생성되는 차이가 있다.

 

- 이로서 프로토 타입은 불필요한 메모리 낭비를 막을 수 있는 중요한 개념이다.

 

프로토타입을 이용한 상속은 다음과 같다.

function People(name, age) {
    this.name = name;
    this.age = age;
}

People.prototype.info = function() {
    console.log('이름: ' + this.name);
    console.log('나이: ' + this.age);
}

function Child (name, age) {
    People.call(this, name, age);
}

Child.prototype = People.prototype;

const child = new Child('test', '30');

child.info();

2. 클래스

- 타언어의 클래스와 매우 유사하다 (JAVA, C...등)

ES6에서 새로 생긴 문법으로 기존 prototype을 활용한 객체 생성자를 클래스로 더 쉽게 표현할 수 있다.

class People {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this.info = function() {
      console.log('이름 :' + this.name);
      console.log('나이 :' + this.age);
    }
  }
}

const people = new People('A', '10');
people.info();

- 클래스 상속은 다음과 같다.

class People {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this.info = function() {
      console.log('이름 :' + this.name);
      console.log('나이 :' + this.age);
    }
  }
}

class Child extends People {
  constructor(name, age) {
    super(name, age);
  }
}


const child = new Child('A', '10');
child.info();
반응형
반응형

V자바스크립트의 실행 컨텍스트에 대해 간단하게 알아보는 시간을 가진다.

- 먼저 실행 컨텍스트란 왜 자바스크립트가 그렇게 동작하는지를 의미한다.


1. 실행 컨텍스트

function sum(a, b) {
	let res = a + b;
    return res;
}

let num = sum(1,2);

- 같은 코드가 존재한다고 가정했을때 동작 순서는 다음과 같다.


2. 콜 스택

- 코드가 실행될때 호출 스택이 쌓이는 부분

function sum(a, b) {
	let r = a + b;
    return r;
}

function calc(a, b, expr) {
	let result = expr(a, b);
    return result;
}

let num = calc(1, 2, sum);

- 다음과 같은 실행 순서로 동작한다

- 16번 후 expr() pop되어 제거
- 17번 후 calc() pop되어 제거

 


3. 스코프

- 현재 접근할 수 있는 변수들의 범위를 의미함

반응형

'프로그래밍 > Javascript' 카테고리의 다른 글

[JS] 호이스팅, Hoisting 이란?  (0) 2022.06.01
[JS] 프로토타입, 클래스란?  (0) 2022.05.26
반응형

Spring Security를 이용하면서 Config파일이 동작하지 않는 문제가 발생했다.

 

1. 문제

아래와 같이 config를 작성했는데 동작하지 않았다. 심지어 모든 /** 경로를 허용해도 계속 스프링 시큐리티 기본 로그인 화면만 뜨는 문제가 발생했다.


2. 원인

- 원인은 생각보다 간단했다. 나의 경우 spring boot를 사용하고 있었고, boot를 실행하는 스프링부트 어플리케이션 클래스가 최상단에 위치해야 config파일이 정상적으로 동작한다

 

즉 기존에 SecurityConfig가 스프링 어플리케이션 파일보다 바깥쪽에 위치하여 설정이 되지않았던 것이다.

 

 

※ 하지만 왜 스프링부트는 정상적으로 동작한걸까? 한번 확인이 필요하다.

반응형
반응형

Vue 에서 ES6 화살표 함수를 적용하려 할 때 다음과 같은 문제가 발생한다.

1. 문제점

created: () => console.log(this.a)  // 오류. this 는 vue instance 가 아닌 window
// 다른 상태 mounted, udpated, destroyed 등 포함
data: () => ({
  item: ''
}),
methods: {
  // 화살표함수
  methodA: () => {
    // this 는 window
    alert(this == window) // true
    this.methodB() // 오류
  },
  // 기존 함수
  methodB: function(){
    // this 는 현재 vue instance
    this.methodA() // 정상
  },
  // 리터럴 축약 함수
  methodC() {
    this.methodA() // 정상
  }
  
  addTodo: () => {
  	console.log(this.item) //undefined
  }
}

2. 원인

화살표 함수의 this는 기존 es5의 function에서의 this와는 다르게 화살표 함수 내부에서는 this를 새로 정의하지 않는다.

즉 화살표 함수 바로 바깥의 함수(혹은 class)의 this를 사용한다. (클로저)

methods: {
  // 화살표함수
  addTodo: () => {
  	console.log(this.item) //undefined
  }
}

methods 객체에서 this가 따로 만들어져 있지 않으므로 this는 window 객체를 의미하고 vue data를 찾지 못하게 된다.


3. 추가 내용

new Vue({
	el: "#app",
  data: {
    originalNumber: 0,
    arrowNumber: 0,
  },
  methods : {
    originalFunction: function () {
      setTimeout(function () { // 기존함수 선언법
      	console.log('original timeout')
      	this.originalNumber = 3
      }, 3000)
    },
    arrowFunction: function () {
      setTimeout(() => { // 화살표 함수
      	console.log('arrow timeout')
      	this.arrowNumber = 3
      }, 3000)
    }
  }
})

반대로 함수 안에 화살표함수와 function()을 사용했을때는 어떨까?

 

결과는 function()은 오류가나고, 화살표 함수는 정상 동작한다.

 

화살표함수의 this.는 상위 메소드의 this 즉 vue 인스턴스를 가르키고 있고, function()의 this는 vue가 아닌 새로운 function()함수를 가르키고있기 때문이다

반응형

+ Recent posts