RPC 는 기술 문서를 읽다보면, 현업에서 흔히 접하게 되는 용어지만 은근히 개념을 이해하기는 쉽지 않다.

 

RPC 의 개념을 인터넷에서 (혹은 최근에 핫한 ChatGPT 에서) 찾아보자면 다음과 같이 나온다.

 

Remote Procedure Call 은 원격 컴퓨터나 프로세스에 존재하는 함수를 호출하는데 사용하는 프로토콜로, 원격지의 프로세스를 동일 프로세스의 함수를 호출하는 것 처럼 사용하는 것을 말한다.

 

하지만 위의 개념을 읽고 한번에 와닿는 사람들은 많지 않을 것이라고 생각한다. 실제로 현업의 엔지니어들도 애매모호하게 해석하게 되는 개념 중 하나이다.

 

그도 그런게 RPC 는 특정 프로토콜 이라기 보다는 전통적인 개념의 API 호출 규약이기 때문이다.

예시를 보면 이해가 보다 쉬울 것이다.

 

POST /sayHello HTTP/1.1
HOST: api.example.com
Content-Type: application/json

{"name": "Tom"}

 

위의 예시는 HTTP 위에서 구현된 RPC 방식의 예시이다. 위의 HTTP 스키마는 HTTP Client (웹 브라우저) 를 통해 HTTP Server (웹 서버) 에 전달될 것이고, WAS 는 위의 프로토콜을 처리하기 위한 로직을 구현하게 된다.

 

여기서 보통은 "그냥 HTTP 프로토콜 아닌가?" 라는 생각이 들 것이고, 그 생각은 맞다.

하지만 일반적인 REST API 처럼 보인다면, 엄밀히 따지자면 그렇지 않다. RPC 와 REST 의 차이점은 다음과 같다.

 

  REST RPC
Protocol HTTP  프로토콜에 무관 (TCP, UDP, HTTP, WebSocket, ...)
Scheme Resource 기반의 API 인터페이스
(예) 강아지 목록을 반환하는 API 를 GET /dogs 로 설계
Action 기반의 API 인터페이스
(예) 강아지 목록을 반환하는 API 를 POST /listDogs 로 설계
Design RESTful API 는 Stateless 를 전제 제약이나 규약이 없음

 

즉, RPC 는 서버와 클라이언트가 통신하기 위한 API 의 통신 패러타임 중 하나이고, REST API 와 비교될 수 있는 차원의 개념이라고 할 수 있다.

RPC 는 기본적으로 정해진 구조를 갖고있지는 않지만, 기본적인 Terminology 를 몇가지 갖고있다.

 

  • IDL (Interface Definition Language) : 서로 다른 언어로 작성된 서비스들 사이에서 공통된 인터페이스를 정의하기 위한 중간 언어
  • Stub : 서버와 클라이언트는 서로 다른 주소 공간을 사용하므로 함수 호출에 사용된 매개변수를 변화해줘야하며, 그 역할을 담당한다
    • client stub - 함수 호출에 사용된 파라미터의 변환(Marshalling) 및 함수 실행 후 서버에서 전달된 결과의 반환
    • server stub - 클라이언트가 전달한 매개변수의 역변환(Unmarshalling) 및 함수 실행 결과 변환을 담당

그리고 위의 개념에 따라서 다음과 같은 순서로 통신이 이뤄지게 된다.

 

  • IDL 을 사용하여 호출 규약을 정의한다. IDL 파일을 rpcgen 으로 컴파일하면 stub code 가 자동으로 생성
  • Stub Code 에 명시된 함수는 원시 코드의 형태로 상세 기능은 server 에서 구현된다. Stub 코드는 서버/클라이언트에서 각각 빌드된다
  • 클라이언트에서 stub 에 정의된 함수를 사용 시 client stub 은 RPC runtime 을 통해 함수를 호출하고 서버는 수신된 Procedure 호출에 대해 처리 후 결과를 반환
  • 클라이언트는 서버로부터 응답을 받고 함수를 로컬에 있는 것처럼 사용할 수 있다

 

이해가 잘 되지 않는다면 쉽게 말해서, RPC 는 이를 해석하고 처리하기 위한 Client / Server 가 존재하며, 그 사이에 규약(IDL) 을 정의하고 IDL 을 클라이언트측 / 서버측 Stub 을 통해 해석하고 비즈니스 로직을 구현한다고 보면 된다. 

 

RPC 는 RESTful API 에 비해 복잡하며, 프로젝트에 따라 Learning Curve 가 있기 때문에,

근래에 RPC 의 개념은 전통적인 방식으로는 잘 사용되지 않으며 gRPC (Google RPC) 와 같은 발전된 개념으로 사용되어진다.

 

RESTful API 가 갖는 개념적 제한에서 벗어나서 보다 자유로운 설계를 위해 사용되는 개념이고, gRPC 와 같은 Modern RPC 는 Micro Service Architecture 등에서 Internal Bidirectional Communication 을 위해 사용되어진다.

 

 

위의 예시는 gRPC 를 이용해서 Micro Service Architecture 를 구현하는 방식을 보여준다. gRPC 의 경우 Protobuf 를 스키마로 사용하기 때문에, 통신을 위한 서버-클라이언트 간에 .proto 파일에 대한 별도 관리가 필요하다.

 

Protobuf 는 구글이 만든 언어 및 플랫폼 중립적인 Data Format 으로, 일반적인 JSON Serialize / Deserialize 대비 효율적인 통신이 큰 장점이다. 관련해서는 블로그의 다음 글을 참고해볼 수 있다.

 

현업에서 RPC 는 주로, 웹 서비스 보다는 게임이나 IoT, Device 등 Non HTTP 기반 산업에서 보다 많이 보이는 형태이지만 Micro Service Architecture 가 시장에 자리잡기 시작하면서 Modern RPC 형태로 많이 보이고 있다.

 

기술이 어떻게 진화할지 모르겠지만 여러 기술적인 문제를 해결하기 위한 시도 중 하나로 알아둘 법한 패러다임이라고 생각되어진다.

 

 

 

CSR (Client Side Rendering) 이니 SPA (Single Page Application) 과 같은 내용들은 10년도 지난 개념들이지만, 시장이 변하는건 그렇게 빠르지 않다.

 

많은 웹 서비스들이 아직 SSR (Server Side Rendering) 기반으로 백엔드와 프론트엔드를 관리하고 있으며 아키텍처를 어떻게 디자인하는 것이 좋은지는 장단점이 있는 중요한 설계 포인트 중 하나가 된다.

특히 팀의 개발 환경 구성 및 전체적인 설계도를 구상해야하는 시니어 개발자 및 Tech Lead 라면 사용 사례를 잘 분석하고 알맞은 접근 방법을 택하는 것이 중요해진다.

 

이번 포스팅에서는 CSR 기반의 Single Page Application 을 위한 프론트엔드 개발 파이프라인을 알아본다.

 

여전히 Java 와 Spring 환경은 주류이지만, 이 환경이 지배적이던 수 년 전에는 프론트엔드 코드가 백엔드 어플리케이션과 같이 묶여서 개발되어지고 배포되어지는 SSR (Server Side Rendering) 방식이 대부분이었다.

이렇게 되면 개발 환경이 분리되지 않기 때문에 역할 분리나 협업에 있어서 애로 사항들이 발생하고, 무엇보다도 하나의 WAS 가 프론트엔드 & 백엔드를 모두 서비스하게 되다보니 배포도 같이 일어나며 인프라 비용의 비효율성도 생기게 된다.

백엔드의 버그가 생겼는데 프론트엔드 코드까지 재빌드가 되어야하거나, 프론트엔드는 리소스를 거의 잡아먹지 않는데 백엔드가 리소스를 많이 잡아먹어서 Scaling 을 해야하는 경우를 생각해보면 이해가 빠를 것이다.

 

무튼.. 이러저러한 애로사항이 있던 와중에 밀결합(Decoupling)의 개념이 세계적으로 대세가 되며 프론트엔드와 백엔드가 분리되어 각자의 라이프사이클을 가지며, API 를 통해 통신하는 CSR 방식이 점차 확산되어졌다.

CSR 방식에서는 프론트엔드와 백엔드가 물리적으로 분리되기 때문에 인프라를 각자 관리할 수 있으며 개발 환경이 나뉘어지기 때문에 역할 분리 및 작업의 비효율성이 개선되어질 수 있다.

 

그렇다면 Client Side Rendering 을 위한 아키텍처 설계는 어떻게 이뤄질까?

먼저 동적 코드에서 정적 코드가 분리된다는 점에 주목해야한다.

기존에 프론트엔드는 Javascript 가 동적 로직을 처리하기는 하지만 기본적으로 클라이언트 브라우저에서 동작한다는 측면 때문에 정적 리소스 (Static Resource) 로 분류되어진다. 

 

이와 같은 정적 리소스들은 인터넷에서 접근될 때 클라이언트 브라우저가 "다운로드" 하여 "실행" 하는 방식이기 때문에 웹 기반의 스토리지에서 제공되어질 수 있다.

이 말은 웹 서버만으로도 정적 리소스는 제공될 수 있으며, 당연히 CDN 을 통한 가속화도 가능하다는 것이다.

 

예전에는 웹 서버 스택을 nginx, apache 위에서 로드밸런싱을 구축해서 제공을 했었지만 클라우드 환경이 대세가 되면서 이 역시 Amazon S3 와 같은 웹 기반의 스토리지로 옮겨가게 되었다. 그리고 이런 정적 웹사이트는 Amazon CloudFront 와 같은 CDN 을 통해 사용자 더 가까운 위치에서 제공될 수 있게끔 디자인이 되게 된다.

 

위와 같이 구성 시 사용자는 CDN 을 통해 전세계 어디에서든 빠르게 정적 리소스를 접근할 수 있으며, 해당 리소스의 렌더링을 받을 수 있다. 또한 S3 와 같은 스토리지는 내구성이나 가용성을 AWS 에서 관리해주기 때문에 개발자 입장에서는 nginx 환경 구성이나 Auto Scaling 을 직접 구축할 필요없이 최소한의 노력으로 서비스를 구축할 수 있게 된다.

 

특히 위와 같은 구조는 동적인 처리가 적게 요구되는 마케팅 페이지 등에서 많이 활용하는 아키텍처이기도 하다.

 

그러면 위와 같은 변경된 구조에서 코드의 배포는 어떻게 수행해야할까?

전통적인 환경에서는 Jenkins 등을 통해 백엔드와 프론트엔드가 전체 빌드된 이후 서버 머신들 위에 직접 배포되었지만, 위와 같은 경우에는 머지된 코드가 단순히 Amazon S3 라는 오브젝트 스토리지 위에 배포되기만 하면 된다. 다만 React 와 같이 프론트엔드 코드를 빌드해주는 경우, 이에 대한 작업 구성은 별도로 필요해진다.

 

AWS 의 CodePipeline 은 위의 배포를 지원하며, 코드 저장소에서 작업한 코드가 Merge 되면 자동으로 S3 에 빌드가 배포되게 구성할 수 있다.

다음 샘플은 AWS 의 Infra As A Code 툴인 AWS CDK 를 이용해서 위의 파이프라인을 만들어놓은 샘플이다.

CDK 를 구동하기만 함으로써 쉽게 AWS 를 활용해서 정적 페이지의 구성과 개발을 위한 파이프라인을 만들어 볼 수 있다.

 

코드 참고 : 

https://github.com/jinspark-lab/cdk-samples/tree/main/frontend-cicd 

 

React 기반으로 어플리케이션을 배포하는 프론트엔드 파이프라인이 만들어져있기 때문에 React 개발 및 배포 환경 구성이 필요할 때 참고해볼 수 있을 것 같다.

 

CSR 이 무조건적인 장점만 있는 것은 아니며, SEO (Search Engine Optimization, 검색 엔진 최적화) 에 있어서 불리한 측면이나 작업 완료 시간이 SSR 에 비해 느리다는 점 등이 단점으로 뽑히며, 현재 두 개념은 적절히 혼합되어 사용 사례에 알맞게 사용되고 있다고 볼 수 있다.

 

 

 

Dynamic Contents 와 Static Contents 의 차이는 명확하다.

용어가 생소하더라도 개념은 익히 알려진 내용일 것이다. 그럼에도 짚고 넘어가자면,

Static Contents 는 유저/지역 등 어떤 기준을 막론하고 같은 데이터, 즉 정적 데이터를 말하고,

Dynamic Contents 는 유저/지역 또는 어떤 기준에 대하 다를 수 있는, 동적 데이터를 말한다.

 

쉽게 이해하자면 변수와 상수의 차이 정도로 보면 쉽다.

 

너무나도 간단한 개념이지만, 캐싱의 관점에서는 또 다르게 적용될 수 있다.

Static Contents 의 캐싱은 간단하다. 단순히 변하지않는 정적 데이터를 캐싱해주면 된다.

일반적으로 API 등에서 Meta data 를 캐싱하는 경우 In memory 에 캐싱하거나 혹은 설정값으로 관리하는 경우가 많다.

Meta data 가 아닌 종류의 리소스들이라면 CDN 이라는 훌륭한 솔루션이 있고, Cache-invalidation 정책만 조절하여 관리해준다.

 

반면 Dynamic Contents 의 캐싱은 조금 다르다. 계속 변하는 컨텐츠이기 때문에 자체만으로는 캐싱이 불가능하다.

가령 "Wellcome Home" 과 "Wellcome Tom" 이라는 두 종류의 웹페이지가 있다고 해보자. 앞선 페이지는 Static web page 이고 다음 페이지는 Dynamic web page 이다.

Static web page 의 캐싱은 간단하며, 단순히 해당 페이지(컨텐츠)를 저장하지만, Dynamic web page 의 경우 동적 요소를 따로 분리해서 로직으로 저장해주고(web page 의 경우 javascript 객체로 매핑시켜줄 수 있겠다.) static contents 만 캐싱해서 응답을 재구성하다.

 

CDN 및 캐시 솔루션 중에는 Dynamic Contents 에 대한 캐싱을 서비스해주려는 노력들이 꽤 있다.

가령 AWS 의 CloudFront 같은 경우 별도의 Backbone Network 를 구성해서 오리진 서버(비즈니스 로직이 처리될 서버)까지의 Latency 를 줄이고 Region 을 확장하는 방식으로 노력을 하고 있다.

 

 

 

서버의 Scalability 를 관리하기 위한 방법은 굉장히 중요하다.

 

실 서비스의 아키텍처를 구조화할 때도 반드시 고려해야할 부분이고, 이 결정은 실제로 서비스에 큰 영향을 주게 된다.

 

가령 사용자가 갑자기 증가할 경우, 엄청난 수의 요청에 대한 처리를 어떻게 할 것인가는 Backend 에서 가장 중요하게 고려해야할 부분 중 하나이다.

 

Scaling 의 방법에는 크게 Scale Up 과 Scale Out 이 존재한다.

 

Scale Up : 서버 자체의 Spec 을 향상시킴으로써 성능을 향상시킨다. Vertical Scaling 이라 불리기도 한다. 서버 자체의 갱신이 빈번하여 정합성 유지가 어려운 경우(주로 OLTP 를 처리하는 RDB 서버의 경우) Scale Up 이 효과적이다.

 

성능 향상에 한계가 있으며 성능 증가에 따른 비용부담이 크다. 대신 관리 비용이나 운영 이슈가 Scale Out 에 비해 적어 난이도는 쉬운 편이다. 대신 서버 한대가 부담하는 양이 많이 때문에 다양한 이유(자연 재해를 포함한다...)로 서버에 문제가 생길 경우 큰 타격이 불가피하다.

 

Scale Out : 서버의 대수를 늘려 동시 처리 능력을 향상시킨다. Horizon Scaling 으로 불린다. 단순한 처리의 동시 수행에 있어 Load 처리가 버거운 경우 적합하다. API 서버나, 읽기 전용 Database, 정합성 관리가 어렵지않은 Database Engine 등에 적합하다.

특히 Sharding DB 를 Scale Out 하게 된다면 주의해야하는데, 기존의 샤딩 알고리즘과 충돌이 생길 수도 있고(샤드가 갯수에 영향을 받을 경우...) 원하는 대로 부하의 분산이 안생길 수 있으니 각별히 주의할 필요가 있다.

 

스케일 아웃은 일반적으로 저렴한 서버 여러 대를 사용하므로 가격에 비해 뛰어난 확장성 덕분에 효율이 좋지만 대수가 늘어날 수록 관리가 힘들어지는 부분이 있고, 아키텍처의 설계 단계에서부터 고려되어야 할 필요가 있다.

 

 

요즘은 클라우드를 사용하기 때문에 Scaling 에 있어서 큰 이점이 있으며, 서비스의 목표치에 알맞게 Scalability 를 설계하고 두 스케일링 방법을 모두 적용시키는 것이 일반적이다.

 

 

 

실서비스를 준비하는 데 있어서 서버의 성능 테스트는 매우 중요한 부분이다.

(여기서 성능 테스트란 단순히 Performance test 만을 말하지는 않는다. 자세한건 다음을 참조 - 

https://jins-dev.tistory.com/entry/load-testing-%EA%B3%BC-stress-testing-performance-testing-%EC%97%90-%EB%8C%80%ED%95%9C-%EB%B9%84%EA%B5%90)

 

그렇다면 성능 테스트를 어떻게 할 것이고 어떻게 병목현상을 해결하여 개선을 이루어낼 것인가가 관건이며, 이를 위해서는 단순히 코드를 효율적으로 짜는 것 이상의 학습이 필요하다.

"성능" 에 대한 이해와 "테스트" 에 대한 이해, Performance Engineering 에 대한 이해가 모두 필요한 것이다.

 

그렇다면 먼저 서버의 성능 측정에 앞서 알아두어야할 용어들을 정리해보자.

 

(1) Response Time : 클라이언트의 요청을 서버에서 처리하여 응답해주는데까지 걸리는 전체 시간을 말한다.

실제 요청이 들어간 시점부터 네트워크를 거치고 비즈니스 로직의 처리가 끝난 후 다시 클라이언트가 응답을 받는 시점까지 걸리는 시간을 말한다.

 

(2) Think Time : 사용자가 다음 Request 를 보내기 전까지 활동하는 시간을 말한다. 실제 별도의 서버 요청을 하지않고 웹문서를 보거나 컨텐츠를 즐기는 시간을 Think time 으로 분류한다.

첫번째 요청에서 다음 요청을 보낼때까지의 시간이므로 Request Interval 로 부르기도 한다.

 

(3) Concurrent Users : 말그대로 동시에 서비스를 사용하는 유저를 말한다. 웹서버 / 세션서버 등 서버의 종류에 따라 유저의 행동양식에 대한 분류는 매우 다양해지게 된다.

일반적으로 Concurrent User 는 Active User 와 Inactive User 로 분류한다.

 

 - Active User : 실제 Request 를 보내고 Response 를 대기 혹은 Connection 을 잡고 있는 유저를 말한다.

일반적으로는 Request 에 대해 실제 Transaction 을 발생시키는 유저에 대해 언급한다.

여기서 포함되는 유저들의 목록에는 실제로 서버에서 클라이언트의 요청을 받을 시, Connection Pool 을 모두 가져가는 유저들과, Connection Pool 의 할당을 대기하고 있는 Pending User 들 역시 Active User 에 포함된다는 점이다.

 

 - Inactive User : 실제 Request 를 수행하지 않고 대기중인 유저를 말한다. 서버의 종류에 따라 연결을 유지하고 있는 유저의 경우에도 Inactive User 로 보기도 한다.

웹서버의 경우 대부분의 Inactive User 는 Think time 을 갖는 유저로 간주된다.

 

(4) Throughput : 서버가 클라이언트에 송신한 데이터량

 

(5) Vuser : Virtual User - 주로 퍼포먼스 테스트를 위해 만드는 가상 유저

 

(6) MRT : Mean Response Time 의 약자로 유저들에 대한 평균 응답시간을 말한다.

 

(7) TPS : Transaction Per Second 의 약자로 주로 서버 성능의 척도가 된다. 초당 트랜잭션 처리 수를 의미한다.

일반적으로 TPS = AU / MRT 라는 공식에 따라 Active User 의 수에 비례하고 유저들에 대한 평균 응답시간(MRTT)에 반비례한다.

 

(8) Scenario : 테스트를 위한 시나리오를 말한다. 정확한 TPS 측정을 위해서 가장 중요한 부분 중 하나로, 실제 서비스에서 유저가 어느정도 비율로 어떤 API 를 호출할지 예측하는 과정이 필수적이다.

Service Architecture 를 잘 이해하고 있어야됨은 물론이고, 이에 따라 API 호출의 비율과 그에 맞는 튜닝으로 서비스의 질을 향상시켜야 한다.

 

 

위의 용어를 이해했으면 다음으로 퍼포먼스 테스트를 위한 프로세스를 정리해보자.

 

1. 먼저 서비스의 목표와 그를 위한 규모를 측정해야 한다.

 

2. 서비스 내에서 사용자의 요청 빈도 수를 예측하고, 그에 알맞게 테스트 시나리오를 만든다.

 

3. 테스트 시나리오에 맞게 다양한 방향으로 테스트를 진행한다.

여기서 테스트는 Stress Test / Performance Test / Load Test 가 복합적으로 진행되어야 한다. 각 테스트별 한계값 임계치를 측정하는 것이 중요하며, 각각의 테스트 결과는 서로에 영향을 미치게 될 것이다.

 

4. Stress Test / Load Test / Performance Test 를 통해 현재 시스템의 부하 한계치, 퍼포먼스 등이 측정이 되었다면, 모듈별로 분석해서 튜닝하는 작업이 필요하다.

비즈니스 로직 뿐 아니라 Database / Network / Middleware / Infrastructure 등을 모두 튜닝하는 것이 중요하다.

 

5. 튜닝을 통해 테스트를 다시 수행하며 서비스의 한계치를 다시 측정한다. 목표에 이를 때까지 4~5 과정을 반복한다.

 

 

위의 테스트 Process 를 거치면 실제 생각했던것과 다른 상황을 많이 마주치게 된다.

 

가령, 우리는 응답시간이 큰 변동폭이 없을 것이며, 가용 자원량 만큼 TPS 가 올라갈 것이라 예측하고 한계점을 측정하겠지만, 실제로는 가용자원의 양은 처음에 급격이 증가하되, 어느정도 TPS 이상이 확보되면 일정해지며 응답시간은 TPS 증가에 따라 기하급수적으로 올라가게 된다.

 

이는 주로, Service architecture 가 Caching 을 하기 때문에 가용자원에 대한 부분을 상당 부분 부담해주기 때문이며, 응답시간의 경우 부하가 쌓이는 만큼 Connection Pool 에서 유저의 요청에 대한 Pending 이 생기기 때문이다.

 

 

아직 Performance Engineering 에 대해서는 지식이 짧아 정리하지 못했는데, 계속해서 공부를 해보면서 추가 포스팅 하도록 해야겠다.

 

(포스팅의 글이 너무 좋아 참조하였습니다. 

https://bcho.tistory.com/787, http://www.jidum.com/jidums/view.do?jidumId=562dumId=562)

 

iLiFO 지덤 :: 테지덤 성능테스트 – Little’s law 포함

I. 성능테스트의 개요와 원리 Little’s law 가. 시스템 테스트의 정의 사용자가 시스템을 사용하기에 성능상 문제가 있는 지를 검증하여 개선하기 위한 테스트 나. 소프트웨어 성능 테스트의 기본 원리 Little’s law ‘공간 내에 머무는 객체 수(L)’는 ‘객체의 공간유입량(λ)’과 ‘객체가 머무는 시간(W)’에 비례한다. 즉 L = λW이다. 시스템의 성능(TPS)는 트랜잭션을 발생시키는 사용자(Active User)를 평균응답시간(Mean

www.jidum.com

 

성능 엔지니어링 대한 접근 방법 (Performance tuning)

성능 엔지니어링에 대한 접근 방법 조대협 성능 개선, Performance Tuning, 용량 선정 과 같은 튜닝 관련 용어들은 모든 개발자나 엔지니어에게 모두 흥미가 가는 주제일 것이다. 그 만큼 소프트웨어에서 고성능을..

bcho.tistory.com

 

 

실제 라이브 서비스를 위해 서버 산정을 할 때, 서비스 규모에 걸맞는 트래픽을 감당하기 위한 테스팅은 필수적이다.

 

근래에는 클라우드의 발전으로 Scalability 나 Elasticity 를 확보할 수 있는 많은 방법과 기술들이 생겼다고는 해도, 결국 해당 솔루션을 사용하는 것은 "비용" 이며 서버개발자라면 이 비용을 최소화하고 효율적인 서버를 구축해내는 것이 "실력" 을 증명해내는 일이다.

 

여기서 피할 수 없는 백엔드 테스트의 종류들이 나타나는데, 일반적으로 스트레스 테스트 또는 부하 테스트라고 알려진 테스트들이다.

이 테스트들은 중요하지만, 개발자들이라도 차이를 잘 모르는 사람들이 꽤나 있고 서비스의 규모가 작다면 혼용되어 쓰는 경우가 다수이기 때문에 정리해서 알아둘 필요가 있다.

 

(1) Load Testing (부하 테스트)

말그대로 시스템이 얼마만큼의 부하를 견뎌낼 수 있는 가에 대한 테스트를 말한다.

 

보통 Ramp up 이라하여 낮은 수준의 부하부터 높은 수준의 부하까지 예상 트래픽을 꾸준히 증가시키며 진행하는 테스트로, 한계점의 측정이 관건이며, 그 임계치를 높이는 것이 목적이라 할 수 있다.

 

일반적으로 "동시접속자수" 와 그정도의 부하에 대한 Response Time 으로 테스트를 측정한다.

예를들어 1분동안 10만명의 동시접속을 처리할 수 있는 시스템을 만들수 있는지 여부가 테스트의 주요 관건이 된다.

 

(2) Stress Testing (스트레스 테스트)

스트레스 테스트는 "스트레스받는 상황에서 시스템의 안정성" 을 체크한다.

 

가령 최대 부하치에 해당하는 만큼의 많은 동시접속자수가 포함된 상황에서 시스템이 얼마나 안정적으로 돌아가느냐가 주요 관건이 된다. 

스트레스 상황에서도 시스템의 모니터링 및 로깅, 보안상의 이슈나 데이터의 결함 등으로 서비스에 영향이 가서는 안되므로 결함에 대한 테스트가 일부 포함된다.

가령 메모리 및 자원의 누수나 발생하는 경우에 대한 Soak Test 나 일부러 대역폭 이상의 부하를 발생시키는 Fatigue Test 가 포함된다.

 

이처럼 부하가 심한 상황 또는 시스템의 복구가 어려운 상황에서 얼마나 크래시를 견디며 서비스가 운영될 수 있으며 빠르게 복구되는지, 극한 상황에서도 Response Time 등이 안정적으로 나올 수 있는지 등을 검사하게 된다.

예를들어 10만명의 동시접속 상황에서 크래시율을 얼마나 낮출 수 있는가, 혹은 데이터 누락을 얼마나 방지할 수 있는가 에 대한 테스팅이 있을 수 있다.

 

(3) Performance Testing (퍼포먼스 테스트)

Load Test 와 Stress Test 의 모집합 격인 테스트의 종류이다. 리소스 사용량, 가용량, 부하 한도(Load Limit) 를 포함해서 응답시간, 에러율 등 모든 부분이 관건이 된다.

 

특정 상황에서 어떤 퍼포먼스를 보이는지에 대한 측정이 주가 되며, 서비스의 안정성보다는 퍼포먼스 자체에 집중한다.

 

주로 서비스적 관점에서는 Performance Test 보다는 Load Test 나 Stress Test 가 더 중점이 되며, 시스템 전체적인 성능의 양적 측정과 같은 관점에서 Performance Test 로 분류한다.

보통 그럴 때에 수행하는 테스트로 Benchmark 라고 하기도 한다.

 

 

위와 같은 종류의 테스트 들을 수행하는 데 있어 제일 중요한 부분 중 하나는 모니터링과 리포팅이라고 할 수 있겠다.

관련해서 경험과 같이 포스팅으로 정리할 예정이다.

 

 



메시지 지향 미들웨어(Message Oriented Middleware)란 독립될 수 있는 서비스간에 데이터를 주고받을 수 있는 형태의 미들웨어를 말한다.


구성요소간 통신을 하는 방법에 있어서 네트워크(Network) 를 이용하거나 Process 간 통신 등의 중계를 해주는 미들웨어의 경우에도 Message Oriented Middleware 라고 부를 수 있지만, 일반적으로 Server to Server 로 전달되는 Message 서비스를 말한다.


메시지 지향 미들웨어의 사용은 통신을 통해 Service 들 간의 분리와 독립, 그럼에도 불구하고 안정적인 기능을 제공해줌으로써 Software Architecture 를 설계하는 데 큰 유연성을 제공해준다.


메시지 지향 미들웨어는 대부분 메시지큐(Message Queue) 의 형태로 구현되어 있으며 줄여서 MQ라고 부른다.


MQ의 종류에는 흔히 알려진 Rabbit MQ 가 있으며 이런 종류의 메시지큐들은 AMQP(Advanced Message Queueing Protocol) 를 이용하여 구현되어 있다.


MQ 시스템을 인프라에 구축했을 때에 아키텍처상으로 이를 브로커(Broker)라고 부르기도 한다.


Message Queue 를 이용할 때 얻을 수 있는 장점으로는 다음과 같은 것들이 있다.


(1) Redundancy : 메시지의 실패는 보관 및 관리가 되며 메시지가 실패한다고 하더라도 시스템에 영향을 주지 않는다.


(2) Traffic : 메시지 큐에 메시지를 쌓아두면 무분별하게 증가할 수 있는 트래픽에 대해 느리더라도 안정된 처리가 가능하다.


(3) Batch : 메시지 큐는 메시지에 대한 일괄처리를 수행하며 특히 부하가 심한 Database 에 대한 처리에 대한 효율성을 기대할 수 있다..


(4) Decouple : 소프트웨어와 MQ는 분리되며, 이는 소프트웨어 설계에 큰 이점을 준다.


(5) Asynchronous : 큐에 넣기 때문에 영향을 받지않고 비즈니스 로직을 처리할 수 있다.


(6) Ordering : 트랜잭션의 순서가 보장되며 동시성 문제에서도 안전하다.


(7) Scalable : 다수의 서비스들이 접근하여 원하는 서비스를 이용하는데 용이하다. 이는 다수의 서비스를 분리하는 데에도 큰 도움을 준다.


(8) Resiliency : 연결된 서비스에 이상이 생기더라도 다른 서비스에 영향을 미치지 않는다.


(9) Guarantee : MQ 는 트랜잭션방식으로 설계되어 있으며 메시지 처리의 트랜잭션을 보장한다.


이러한 장점들을 갖고 있기 때문에 주로 API 서버를 구축할 때 중요하거나 그 자체로 무거울 수 있는 기능들을 별도의 서버로 분리하고, MQ 를 통해 통신하는 방식이 선호된다.


이는 프로젝트의 규모가 커짐에도 유연성을 확보할 수 있는 좋은 수단이며 서비스 자체에서 처리하기 부담스러운 요구사항들을 만족시킬 수 있는 옵션을 제공한다.



다음 링크들을 참조했습니다.


https://docs.oracle.com/cd/E19435-01/819-0069/intro.html

https://stackify.com/message-queues-12-reasons/

https://heowc.tistory.com/35




COMET 이란 2006년 알렉스 러셋(Alex Russel)이 정의한 용어로, 브라우저가 HTTP 요청에 대해 데이터를 푸시하는 방법을 고안한 웹 모델이다.


일반적인 웹모델은 널리 알려진 바 대로 서버와 클라이언트 브라우저간 상태를 유지한 통신이 불가능하다. 


COMET 은 이러한 HTTP 의 본질적 한계를 어느정도 극복하고자 만든 Web Application Model 이라고 생각하면 된다.


가령 브라우저가 지속적으로 서버에 데이터를 받아야할 필요가 있을 때 서버의 데이터가 업데이트 되지않았다면 이는 무의미하다.

즉, 클라이언트의 입장에서 갱신이 필요한 절대적인 시점은 무조건 서버의 데이터가 변경되었을 때가 된다.


COMET 은 간단하게는 Client 로 유의미한 메시지를 전달할 때까지 HTTP 응답을 지연시키는 기술이다.


좀 더 정확히는 서버가 클라이언트의 요청에 응답할 때 응답을 "늘어뜨리는 방법" 을 이용해서 긴 시간동안 브라우저가 접속을 끊지않고 서버의 응답을 대기하게 만든다. 


즉, Long polling 과 원리가 같다.

(Long Poll – 서버측에서 단순히 클라이언트 측에 대한 연결을 길게 유지함으로써 지정한 기간 내에 정보가 있으면 응답을 전달한다. 메시지 양이 많으면 Polling 과 차이가 없으며 지정한 시간이 초과되어 끊기면 다시 요청하는 말그대로 긴 – Polling 이다.)


클라이언트는 Long polling 을 해주고, 그 시간 동안 서버는 서버에서 발생한 이벤트 정보 등도 같이 끼워넣어줌으로써 이른바 "HTTP 푸시" 를 흉내내게끔 한다.


COMET 모델에서 서버는 비동기 방식을 취하고, 클라이언트가 다양한 테크닉으로 푸시를 받을 수 있는 구조를 취하는 경우가 일반적이다.


이에 Javascript 레벨에서 사용되는 테크닉으로, Hidden Iframe 영역(Forever frame)을 할당하고 서버측에서 발생하는 이벤트별로 <script> 태그 내에 데이터를 채워넣는 방식 또는 Ajax 의 long polling 방식, 그리고 Active X 를 이용하는 방식을 사용하기도 한다.


COMET 모델에서 서버와 클라이언트가 통신하기 위해 JSONP 라는 방식을 사용하는데, 이는 JSON 규격의 데이터로 통신을 하되 Client 로부터 전달받은 Callback 함수를 호출하게끔 하는 방식이다.


이렇게 되면 브라우저는 Callback 함수로 감싸여진 Json 데이터를 이용해서 <script> 또는 XHR 을 구성하며 해당 리소스를 읽고 수행하게 된다.


이처럼 COMET 모델을 이용하면 서버에서 발생한 이벤트를 Client 의 추가 요청없이 송신할 수 있다. 



현재는 Websocket 및 HTML5에서 표준화된 Server-sent Event 와 연관되어 발전해가고 있다.







백엔드를 개발하다보면, Client 의 요청을 자체 처리하는 경우가 아닌 다른 url 핸들러에게 위임하는 경우가 종종 있다.


이렇게 클라이언트 요청에 대해서 다른 리소스로 연결을 할 때, redirect 와 forward 라는 2가지 테크닉이 있다.


Redirect 와 Forward 모두 클라이언트 요청에 대한 처리를 다른 url 로 보내서 처리를 위임하는 개념이지만, 


두 개념에는 차이가 있다. 각각의 개념을 통해 차이점을 정리해보자.



redirect - 클라이언트의 요청에 대해 서버가 다른 url 로 요청을 하게끔 만듬.




이 때, 원래 클라이언트가 요청한 url 의 핸들링 시, 서버는 다른 url 로 클라이언트가 "요청" 을 던지게끔 한다.


이 후 클라이언트는 서버로부터 전달받은 다른 url, 즉 위의 예제에서 /home 으로 다시 요청을 하게 하고, 해당 url 매퍼에서 요청에 대한 응답이 처리되게 된다.


즉, 연결이 끊기고 재연결이 들어가는 등 요청이 여러번 왕복하기 때문에 Rquest - Response 쌍이 하나 이상 생기게 된다.




forward - 서버가 클라이언트의 요청을 다른 서버로 넘김(forwarding)




서버는 클라이언트로부터 요청을 전달받았을 때, 이 요청을 서버 내부에서 다른 url 핸들러로 요청을 "전달" 한다.


즉, 클라이언트가 다시 서버에 대한 요청을 할 필요 없이 서버가 다른 url 매퍼에서 처리된 응답을 받기만 하면 되는 구조가 된다.


실제 처리는 "다른 url" 에서 처리되었지만 응답은 초기 url 핸들러로부터 내려받으며, 서버와 클라이언트 간 Request - Response 쌍은 하나만 존재하며 연결이 끊기지 않음. (연결이 하나로 유지됨)




실무에서도 많이 쓰이는 개념이고 익숙해지면 차이를 헷갈릴 수 있으니 틈틈히 정리하고 알아두는게 필요하다.



'Server > Basic' 카테고리의 다른 글

Message Oriented Middleware 및 Message Queue 에 대한 설명  (0) 2019.03.13
COMET 이란?  (0) 2019.01.23
Java Servlet 에 대하여  (0) 2018.12.23
HTTP/2 특징들에 대한 정리  (0) 2018.12.17
무중단 배포의 원리와 솔루션 종류  (0) 2018.12.09

 

Servlet 은 WAS에서 동작하는 Java 의 클래스를 말하며, 단순히 HTTP Request 에 대해 HTTP Response 를 응답하는 고차원 추상화를 제공하는 클래스를 말한다.

Java로 웹 어플리케이션 제작 시 동적인 처리를 담당한다.

 

Web Server 의 성능 향상을 위해 사용되는데, 외부 요청에 대해 Thread 로 할당하여 응답하므로 아주 가벼운 서버로 구현되고 Java 의 특성 상 다양한 플랫폼에서도 동작이 가능하다.

 

Servlet 은 일반적으로 HttpServlet  클래스를 상속받으며 웹페이지 개발시 JSP 와 Servlet 을 함께 이용하는 것이 도움이 된다.

(JSP 는 HTML 문서 안에서 Java 코드를 포함하는 반면, Servlet 은 Java 코드 안에서 HTML 코드를 사용하곤 한다.)

 

Servlet 3.0 미만의 버전에서는 web.xml 파일에 Servlet 을 등록하고 사용하도록 되어있지만, 

Servlet 3.0 이상에서는 web.xml 파일을 사용하지 않으며 대신 Annotation 을 이용해 정의한다.

 

3.0 이상에서 어노테이션을 이용해 서블릿을 작성할 때에는

 

@WebServlet("/test")
public class TestServlet extends HttpServlet {

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 response.setContentType("text/html;charset=utf-8");
 PrintWriter out = response.getWriter();
 out.print("Hello world");
 out.close();
 }
}

위와 같이 작성하게 된다.

 

Java Servlet 은 Servlet Container 위에서 동작가능한데, Tomcat 과 같은 서블릿 컨테이너 들은 다음과 같은 기능들을 지원함으로써 서블릿의 동작을 돕는다.

 

 - Network Services : Remote System 및 네트워크 서비스를 접근하고 이용할 수 있도록 제공한다.

 - Decode and Encode MIME based Message : MIME 타입의 메세지에 대한 인코딩 / 디코딩을 제공한다.

 - Manage Servlet container : 서블릿 들의 라이프사이클을 관리해준다.

 - Resource Management : HTML, JSP 등과 같은 static 및 dynamic 리소스 들을 관리해준다.

 - Security Service : 리소스 접근에 대한 인증과 권한을 담당한다.

 - Session Management : 세션을 관리한다.

 

Tomcat 과 같은 Servlet Container 들은 서블릿들을 구동시켜 WAS 로써 동작할 수 있도록 해준다.

 

+ Recent posts