간단한 예제 코드
MVC
@SpringBootApplication
@RestController
@RequiredArgsConstructor
public class MvcApplication implements ApplicationListener<ApplicationReadyEvent> {
private final RedisTemplate<String, String> redisTemplate;
private final UserRepository userRepository;
public static void main(String[] args) {
SpringApplication.run(MvcApplication.class, args);
}
@GetMapping("/health")
public Map<String, String> heatlh() {
return Map.of("health", "ok");
}
@GetMapping("/users/1/cache")
public Map<String, String> getCachedUser() {
var name = redisTemplate.opsForValue().get("users:1:name");
var email = redisTemplate.opsForValue().get("users:1:email");
return Map.of("name", name == null ? "" : name, "email", email == null ? "" : email);
}
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userRepository.findById(id).orElse(new User());
}
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
redisTemplate.opsForValue().set("users:1:name", "greg");
redisTemplate.opsForValue().set("users:1:email", "greg@fastcampus.co.kr");
Optional<User> user = userRepository.findById(1L);
if (user.isEmpty()) {
userRepository.save(User.builder().name("greg").email("greg@fastcampus.co.kr").build());
}
}
}
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Data
@Table(name = "users")
class User {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Long id;
private String name;
private String email;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
interface UserRepository extends JpaRepository<User, Long> {
}
기본 스레드 최소 설정은 1000개로 한다.
server:
port: 9000
tomcat:
max-connections: 10000
accept-count: 1000
threads:
max: 3000
min-spare: 1000
logging:
level:
root: INFO
spring:
datasource:
url: jdbc:mysql://localhost:3307/mvc_webflux_jmeter_test
username: root
password: xxxx
data:
redis:
host: localhost
port: 6379
jpa:
generate-ddl: on
hibernate:
ddl-auto: create
Webflux
@SpringBootApplication
@RestController
@RequiredArgsConstructor
public class WebfluxApplication {
private final ReactiveRedisTemplate<String, String> reactiveRedisTemplate;
private final UserRepository userRepository;
public static void main(String[] args) {
SpringApplication.run(WebfluxApplication.class, args);
}
@GetMapping("/health")
public Mono<Map<String, String>> health() {
return Mono.just(Map.of("health", "ok"));
}
@GetMapping("/users/1/cache")
public Mono<Map<String, String>> getCachedUser() {
var name = reactiveRedisTemplate.opsForValue().get("users:1:name");
var email = reactiveRedisTemplate.opsForValue().get("users:1:email");
return Mono.zip(name, email)
.map(i -> Map.of("name", i.getT1(), "email", i.getT2()));
}
@GetMapping("/users/{id}")
public Mono<User> getUser(@PathVariable Long id) {
return userRepository.findById(id).defaultIfEmpty(new User());
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("users")
class User {
@Id
private Long id;
private String name;
private String email;
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
}
interface UserRepository extends ReactiveCrudRepository<User, Long> {
}
server:
port: 9010
spring:
r2dbc:
url: r2dbc:mysql://localhost:3307/mvc_webflux_jmeter_test
username: root
password: xxxx
data:
redis:
host: 127.0.0.1
port: 6379
jpa:
generate-ddl: on
hibernate:
ddl-auto: create
Jmeter 설치
Jmeter는 brew 명령어로 간단히 설치할 수 있다.
Jmeter는 java로 만들어진 오픈 소스 -> https://jmeter.apache.org/
brew install jmeter
몇가지 플러그인을 더 설치한다
- json-lib
- jmeter-plugins-cmn-jmeter
- cmdrunner
- jpgc-graphs-basic 2.0
- jpgc-graphs-additional 2.0
Jmeter 실행
listner를 다음과 같이 추가한다.
- summary: 요약 내용 보여줌
- reponse times over time: 응답 시간이 얼마나 걸리는
- transaction per second: tps가 얼마나 되는지
각각 실행하기 위해 한쪽 실행시 한 쪽은 disable 해주었다.
기본 요청 응답 -> MVC test
warm up으로 유저 1명으로 실행해보자.
summary와 tps
이제 threds(users)를 200으로 올리고 루프 카운트를 infinite로 해서 계속 요청을 해보자
summary, 응답시간, tps
- throughput: 42000.0/sec
- 응답시간: 8~9ms
- tps: 35000~40000
기본 요청 응답 -> Webflux test
동일하게 200명의 유저로 loop infinite로 실행해보자.
summary, 응답시간, tps
- throughput: 50375/sec
- 응답시간: 4~5ms
- tps: 48000 ~ 50000
기본 요청 응답 -> 비교 결과
비교해보면 약 throughput, tps의 경우 20~30% 성능 향상이 있는 것을 볼 수 있다.
응답시간의 경우 2배에 가까이 차이가 난다.
엄청 빠르진 않지만 그래도 Webflux가 빠르다.
db IO 포함 -> MVC test
- throughput: 1701/sec
- 응답시간: 100~270ms
- tps: 1200 ~ 2000
db IO 포함 -> Webflux test
- throughput: 4738/sec
- 응답시간: 36~50ms
- tps: 4000 ~ 5500
db IO 포함 -> 비교 결과
비교해보면 전체적으로 최소 2배 ~ 3배 가량의 차이가 발생하는 것을 볼 수 있다.
확실히 Webflux가 빠르다.
총평
일반 응답과 CRUD를 사용한 응답 요청의 성능에서 Webflux가 MVC가 좋은 성능을 보여준다.
참고로 테스트 결과를 제시하진 않았지만 redis 캐시를 사용할 때 MVC와 Webflux 모두 큰 성능 효과가 있다.
reference :
- fastcampus 시그니처 백엔드 path 초격차 패키지 course 7
- https://docs.spring.io/spring-data/r2dbc/docs/current/reference/html/
- https://jmeter.apache.org/
반응형
'Lecture' 카테고리의 다른 글
대용량 트래픽을 대비한 Spring Webflux와 Reactive Redis를 이용한 접속자 대기열 시스템 (0) | 2023.10.22 |
---|---|
webflux 리액티브 프록그래밍에서 블록킹을 디버깅하는 도구 blockhound 간단 사용법 (1) | 2023.10.21 |
Spring webflux - R2DBC, Redis (0) | 2023.10.21 |
객체 필드 중복을 해결하는 다양한 방법들과 객체지향 패러다임을 이용한 순수 코드 방식의 해결법(feat. DDD의 Aggregate와의 연관성) (0) | 2023.07.22 |
RestDocs로 API 문서를 만들어보자 (MockMvc 테스트, 커스터마이징 옵션 설정하기) (0) | 2023.07.13 |