GCP/원데이

[GCP 원데이] I/O 바운드 애플리케이션 (2)

ozofweird 2021. 5. 3. 13:10

1. I/O 바운드 애플리케이션

1) application.yml

로컬 환경에서의 DB와 서비스 환경에서의 DB는 다르다. 이를 위해 두 환경에서 동작할 수 있도록 수정해주어야한다. 이러한 기능을 profile이라고 한다.

 

'application.properties' 파일을 이용할 경우, 여러 개의 파일로 나눌 수 있다. 단, 특정 profile이 활성화되지 않을 경우 'application-default.properties'가 기본적으로 로딩된다.

 

'application.yml' 파일을 이용할 경우, 한 파일에 profile 설정이 가능하다. 하지만 @PropertySource 어노테이션을 통해 값을 읽어 올 수 없다는 단점이 있다. 각각의 profile은 '---'로 구분된다.

spring:
  profiles:
    active: default
logging:
  level:
    root: debug

---

spring:
  profiles: dev
logging:
  level:
    root: info

---

spring:
  profiles: test
logging:
  level:
    root: debug
  level.org.hibernate:
    SQL: debug
    type.descriptor.sql.BasicBinder: trace

특정 profile을 실행시킬 경우에는 지정을 해주어야 하며, 지정을 하지 않을 경우 기본 default로 동작한다.

java -jar -Dspring.profiles.active=dev  ./kk-0.0.1-SNAPSHOT.jar

이 점을 프로젝트에 적용을 하면 다음과 같다.

spring:
  config:
    activate:
      on-profile: local

  datasource:
    url: jdbc:postgresql://localhost:5432/postgresql
    username: postgresql
    password: postgrespassword
  jpa:
    show-sql: true
    hibernate:
      dialect: org.hibernate.dialect.PostgreSQLDialect
      ddl-auto: update

---

spring:
  config:
    activate:
      on-profile: prod

  datasource:
    url: jdbc:postgresql://[Postgresql 서버 IP]:5432/postgresql
    username: postgresql
    password: postgrespassword
  jpa:
    show-sql: true
    hibernate:
      dialect: org.hibernate.dialect.PostgreSQLDialect
      ddl-auto: update

IntelliJ에서 특정 profile을 실행시키는 방법은 Run/Debug Configurations에서 VM options를 주면 된다.

※ 스프링 부트 2.4.0 이후에는 spring.config.activate.on-profile로 변경되었다.

2) 페이징 기능

Git Flow를 init하고, 페이징이라는 이름의 feature 브랜치를 생성해준다.

package com.example.io;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
public class PostController {

    private static Integer PAGE_SIZE = 20;

    @Autowired
    private PostRepository postRepository;

    // 1. 글을 작성한다.
    @PostMapping("/post")
    public Post createPost(@RequestBody Post post) {
        return postRepository.save(post);
    }

    // 2. 글 목록을 페이징하여 반환 (page 파라미터가 없을 경우 1번 page 요청)
    @GetMapping("/posts")
    public Page<Post> getPostList(@RequestParam(defaultValue = "1") Integer page) {
        return postRepository.findAll(
                PageRequest.of(page - 1, PAGE_SIZE, Sort.by("id").descending())
        );
    }

    // 3. 글 번호로 조회

    // 4. 글 내용으로 검색 -> 해당 내용이 포함된 모든 글
}

로컬용 profile로 프로젝트를 실행시켜 DB에 50개의 데이터와 이를 조회하는 기능을 확인해본다.

localhost:8080/posts?page=2

3) 페이징 기능 PR

'feature/paging'과 'develop'을 Push 해준다. PR로 'feature/paging'을 'develop'으로 합병하도록 한다.

마지막으로 PR에 대한 결과를 Pull 받아온다.

4) 검색 기능 및 PR

이후 검색 기능을 추가해주고 PR로 반영해준다.

    ...

    // 3. 글 번호로 조회
    @GetMapping("/post/{id}")
    public Post getPostById(@PathVariable("id") Long id) {
        return postRepository.findById(id).get();
    }
    
    // 4. 글 내용으로 검색 -> 해당 내용이 포함된 모든 글 (spring boot jpa like 구글링)
    @GetMapping("/search")
    public List<Post> findPostsByContent(@RequestParam String content) {
        return postRepository.findByContentContains(content);
    }

5) 배포

Jenkins에서 배포를 해준다. 빌드 과정에서 'Context Loader' 에러가 나올 경우, 테스트 케이스에 대한 부분을 주석 처리해준다.

package com.example.io;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class IoApplicationTests {

//    @Test
//    void contextLoads() {
//    }

}

 

 

 

2. 성능 측정

1) 테스트 준비

실제 한 줄 게시판에 작성될 법한 내용들이 있어야 성능 테스트를 했다고 할 수 있다. 이러한 데이터셋의 경우, 데이터 과학을 하는 사람들이 만들어 놓았기 때문에 구글링을 통해 쉽게 얻을 수 있다.

 

다운받은 데이터셋을 프로젝트에 적합한 형태로 구글 스프레드 시트에서 가공을 해준다. 구글 스프레드 시트에서 파일을 불러올 때는 확장자가 csv 형태여야만 가능하다.

가공한 파일을 다시 csv 확장자로 다운을 받고 성능 테스트를 진행하면 된다. 약 5만 개의 데이터가 존재하며, 이를 천 개, 만 개씩, 등등으로 나누어 저장을 한다.

 

이전 글에서는 Post 하나에 약 300ms(0.3초)가 걸렸다. 이를 천 번 반복면 300초 (5분), 만 번 반복한다고 하면 3000초(50분) 걸린다. 대략적인 계산이지만, 실제 테스트에서는 작은 숫자씩 늘려가면서 진행해보아야 한다.

2) Artillery 성능 측정

Artillery 스크립트의 경우, 공식 문서에서 'Core Concept'와 'Test Script Reference' 항목을 참고하여 만들어준다. 333초 동안 1초에 3번씩 요청하고, 1000개 짜리 csv를 지정해준다.

 

json의 content 항목은 지정한 csv에서 하나 하나의 content가 들어가게 되는 설정이다.

config:
  target: "http://[NginX IP]"
  phases:
    - duration: 333
      arrivalRate: 3
      name: Warm up
  payload:
    path: "ratings_test_1k.csv"
    fields:
      - "content"
scenarios:
  - name: "just post content"
    flow:
      - post:
          url: "/post"
          json:
            content: "{{ content }}"
artillery run --output report.json io-test.yaml
artillery report ./report.json

위의 결과로는 무난하게 요청이 처리가 되는 결과를 얻을 수 있다.

 

다음으로는 스크립트를 고도화하여 60초 동안 초당 3번씩 요청을 하는 'Warm up', 120초 동안 초당 3번이었던 요청을 극단적으로 100번 늘린 'Ramp up load', 600초 동안 초당 100번씩 요청하던 것을 계속 유지를 하는 'Sustained load' 단계를 추가해준다. 이는 실제 서비스에서 초당 사용자 요청이 적게 들어오다가 갑자기 증가해버리는 구간에 대한 테스트이다. 이러한 구간 테스트를 통해 대용량 트래픽을 잘 처리할 수 있는지 없는지를 확인할 수 있다.

config:
  target: "http://[NginX IP]"
  phases:
    - duration: 60
      arrivalRate: 3
      name: Warm up
    - duration: 120
      arrivalRate: 3
      rampTo: 100
      name: Ramp up load
    - duration: 600
      arrivalRate: 100
      name: Sustained load
  payload:
    path: "ratings_test_10k.csv"
    fields:
      - "content"
scenarios:
  - name: "just post content"
    flow:
      - post:
          url: "/post"
          json:
            content: "{{ content }}"

다음 스크립트로는 초당 50번 요청할 수 있도록 phases를 수정해주고, 작성만이 작성 후 1초동안 대기한 뒤 글 목록을 10분동안 요청할 수 있도록 한다.

config:
  target: "http://[NginX IP]"
  phases:
    - duration: 60
      arrivalRate: 3
      name: Warm up
    - duration: 120
      arrivalRate: 3
      rampTo: 50
      name: Ramp up load
    - duration: 600
      arrivalRate: 50
      name: Sustained load
  payload:
    path: "ratings_test_10k.csv"
    fields:
      - "content" 
scenarios:
  - name: "just post content"
    flow:
      - post:
          url: "/post"
          json:
            content: "{{ content }}"
      - think: 1
      - get:
          url: "/posts"

728x90