📅 Week 11 : 07/28 ~ 08/01
7/28 : order service 세팅
7/29 : refresh token
7/30 : dto 내부 데이터 설정, 클래스 분리
7/31 : 동시성 문제 해결(jmeter, redis, rabbitmq)
8/1 : SSE(실시간 알림), pub&sub
7/28~8/1 : 프로젝트
JMeter를 통한 동시성 테스트
오픈 소스 데이터인 JMeter를 통해 동시성 테스트를 진행했다.
스레드 그룹을 통해 100명의 가상 유저를 만들고, 주문을 100개를 넣어 테스트를 진행하였다.
Spring과 Redis 연동, 이를 통한 동시성 이슈 해결
// RedisConfig.java
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private int port;
@Bean
// Qualifier : 같은 Bean객체가 여러개 있을 경우 Bean객체를 구분하기 위한 어노테이션
@Qualifier("rtInventory")
public RedisConnectionFactory redisConnectionFactory(){
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
configuration.setHostName(host);
configuration.setPort(port);
configuration.setDatabase(0);
return new LettuceConnectionFactory(configuration);
}
@Bean
@Qualifier("stockInventory")
public RedisConnectionFactory stockInventory(){
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
configuration.setHostName(host);
configuration.setPort(port);
configuration.setDatabase(1);
return new LettuceConnectionFactory(configuration);
}
@Bean
@Qualifier("rtInventory")
// Bean들끼리 서로 의존성을 주입받을 때 메서드 파라미터로도 주입가능
// 모든 template중에 무조건 redisTemplate 이라는 메서드명이 반드시 1개는 있어야함.
public RedisTemplate<String, String> redisTemplate(@Qualifier("rtInventory") RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
@Bean
@Qualifier("stockInventory")
public RedisTemplate<String, String> stockTemplate(@Qualifier("stockInventory") RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
드디어 Redis를 Spring과 연동하여 실제 서버에서 사용했다.
Redis를 Spring과 연결하는 방법은 생각보다는 어렵지 않았다.
Redis가 싱글 스레드 구조라는 점을 이용해 멀티 스레드 환경(일반rdb)에서 발생하는 동시성 문제를 쉽게 해결 할 수 있었다.
SSE와 Pub&Sub
SSE를 이용해서 실시간 알림 서비스를 구현하는 방법을 배웠다.
@Component
@RequiredArgsConstructor
public class SseAlarmService {
private final SseEmmiterRegistry sseEmmiterRegistry;
// 특정 사용자에게 message 발송
public void publishMessage(String receiver, String sender, Long orderingId){
SseMessageDto dto = SseMessageDto.builder()
.sender(sender)
.receiver(receiver)
.orderingId(orderingId)
.build();
ObjectMapper objectMapper = new ObjectMapper();
String data = null;
try {
data = objectMapper.writeValueAsString(dto);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
// emmiter객체를 통해 메시지 전송
SseEmitter sseEmitter = sseEmmiterRegistry.getEmitter(receiver);
try {
sseEmitter.send(sseEmitter.event().name("ordered").data(data));
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Component
public class SseEmmiterRegistry {
// SseEmitter는 연결된 사용자정보(ip, macadress정보 등)를 의미
// ConcurrentHashMap은 Thread-Safe한 map(동시성 이슈 발생 x)
private Map<String, SseEmitter> emitterMap = new ConcurrentHashMap<>();
public void addSseEmmiter(String email, SseEmitter sseEmitter){
emitterMap.put(email, sseEmitter);
}
public void removeEmmiter(String email){
emitterMap.remove(email);
}
public SseEmitter getEmitter(String email){
return emitterMap.get(email);
}
}
Spring에서는 클라이언트와 서버 간의 실시간 단방향 통신을 가능하게 하는 SseEmmiter라는 클래스를 지원한다. Sse는 단방향 통신, 실시간 업데이트, 그리고 마지막으로 간단하게 구현할 수 있는 특징이 있다. 이에 대한 자세한 내용은 나중에 따로 글로 정리해서 올리겠다. 마침 프로젝트 기능 중 알림기능이 있는데, 이것을 활용해서 개발하면 될 것 같다.
이번주는 Spring의 심화 기능을 들여다 본 한 주였다.
대부분 예전에 알지 못했던 기술들이며, 이번에 배운 기술들은 기능 구현보다는 프로그램의 성능을 높여지는데 초점이 맞춰진 느낌이었다.
동시성 이슈, 성능 테스트, 실시간 통신 등 성능 및 네트워크랑 관련된 기술들을 하나하나 자세하게 배웠다.
이번에 배운 기술들은 백엔드 개발에 있어서 성능 부분과 연결되므로, 상당히 중요한 기술들이라고 볼수 있을 것 같다.
본격적인 프로젝트 기능 개발이 시작되었고, 아직까지는 기능 구현에 미친듯이(?) 어려움을 겪는것은 없던것 같다. 다만 코드 리뷰를 대비해 내가 왜 기술을 사용했는지, 이 메서드는 왜 이렇게 설계했는지에 대한 충분한 이해와 설명은 사전에 충분히 준비해야겠다는 생각이 든다.
[회고 작성 주기 정상화]
저번주에 다짐한 대로, 회고 작성 주기가 드디어 정상화 되었다.
앞으로도 지금처럼 회고를 월요일전에 작성해서 올리는 습관을 쭉 기르자.
[클라이밍 복귀]
드디어 부상 4주, 29일만에 클라이밍에 복귀했다.
그레이드는 원래 풀던것 보다 0.5정도 낮아졌지만, 그래도 부상, 통증 없이 무사히 복귀클을 했다. 당분간은 부상을 당할것 같은 루트들은 철저하게 배제하고 재활한다는 느낌으로 운동을 해야겠다. 몸 상태를 빨리 원래대로 돌려놔야지....
[코테 공부 미흡]
지난주와 같다.
Spring 복습과 프로젝트 집중으로 자꾸 코테 문제를 안풀게 되는 것 같다.
시간을 낼려 해도 전혀 시간이 나지를 않는다...
당장 월요일이 PCCP 시험인데 걱정된다. Lv.1은 따야되는데...
뜻 깊은 한 주 였다.
열심히 배운만큼 열심히 노력해서
내 자신을 성장시키자.