문제 상상하기
문제 상상하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Service
@RequiredArgsConstructor
public class StorageService {
private final Map<String, StorageInner> map = new ConcurrentHashMap<>();
public void add(StorageInner storageInner) {
map.put(storageInner.getUuid(), storageInner);
}
public byte[] getImage(String uuid) {
byte[] image = map.get(uuid).getImageByte();
map.remove(uuid);
return image;
}
public List<StorageInner> getAll() {
List<StorageInner> temp = new LinkedList<>();
for (String i : map.keySet()) {
temp.add(map.get(i));
}
return temp;
}
public void remove(String uuid) {
map.remove(uuid);
}
}
운영중인 지도 이미지 서비스가 있는데, 조금은 독특한 방식으로 이미지를 이용중이다. DB에 저장하는건 약관 문제랑 금전적인 문제도 있고 해서 Map 자료구조를 통해 임시 보관소 정도로 사용중이다. 이게 서버에다가 이미지 바이트값을 직접 넣어놓는 방식이다 보니 트래픽이 지금보다 커지면 문제의 소지가 다분하긴 한데, 런칭한지도 3년 넘었고 어느정도의 예측범주로만 몰리기 때문에 지금은 큰 문제가 없다.
그래도 별일 없다고 안주하기보다는, 다양한 상황을 가정해서 고민을 해보고 싶었다. 트래픽의 규모에 따라 고민하는 요소나 구현 방법도 달라질 수 있다고 생각하고, 현재 구현된 방식보다 더 좋은 방법이 있을지, 서버가 증설된다면 어떤 방식으로 해결해나갈지 한번쯤은 고민해 보면 좋을 것 같았다.
문제 가정
상황 및 제약
- DB 사용 금지, 스토리지 관련도 금지(s3, presigned url…)
- 매번 외부 api 호출을 통해서 이미지를 생성해야 함
- 이미지는 유저가 처음 요청한 서버의 메모리에 저장됨
풀어보기
동일 서버
일단 하나씩 생각해보자. 내 서비스는 대략 이런 구조인데, ec2에서 lambda를 호출하고, lambda에서 외부 api를 호출한다. 외부 api는 최상의 상황이라고 가정했으니 더이상 고려하지 말고, ec2와 lambda 사이만 고려해보면 된다.
람다에서 이미지 제작이 완료되면 ec2에서 돌고있는 스프링의 Map에 바이트 값을 넣어주고, 유저에게 랜덤하게 생성된 키값을 돌려준다. 그러면 유저는 해당 키값으로 서버에 자신의 이미지를 요청하고, 이미지를 발급받음과 동시에 해당 값은 삭제된다.
앞에 로드밸런서가 있고, 라운드로빈과 같은 방식으로 여러 대의 서버에 유저 요청을 뿌린다면 문제가 되는 구간이 존재한다. 유저가 처음 이미지 제작을 요청한 서버와, 키값을 전달받고 이미지 발급을 요청하는 서버가 다를 수 있다는 것이다.
나는 지금 aws를 사용중이니까 혹시 제공하는 서비스가 있는지 살펴봤는데, Session Affinity이나 Sticky Session을 이용하면 처음 요청을 보낸 서버를 계속 유지할 수 있다고 한다.
그렇다면 또 여기서, 저런 부가서비스를 사용하지 않는다고 가정하면 방법이 없을까 더 생각을 해봤다.
람다와 직접 연결
예전부터 이 방향으로 가려고 이것저것 조사해봤는데, 자꾸 핑계 대면서 미루고 있다. 딴 얘기긴 하지만 서비스의 여러 방향성을 고려해 봤는데, ec2를 아예 걷어내는 쪽으로 마음이 쏠리는 것 같다.
일단 aws lambda에 대한 설명을 먼저 해야 할 것 같은데, 일종의 일회용 컴퓨터를 대여해주는 서비스라고 보면 될 것 같다. 개발자 입장에서는 인프라 관리에 대한 부담이 적은 서비스다. 같은 url로 요청을 보내지만 코드가 구동되는 서버는 매번 다르기 때문에 일회용 컴퓨팅 서비스라고 생각하면 될 것 같다.
현재 굳이 람다에서 다시 ec2로 이미지 바이트 값을 전달해서 유저한테 주는 이유는 람다의 응답용량 제한 때문이다. 이미지가 보통 최소 10MB는 넘는데, 람다 최대 응답용량을 넘어선다. 그래서 이미지를 잘게 나눠서 ec2에 저장 요청을 해놓고 유저가 찾아가는 방식이다.
그런데 굳이 이걸 한 방에 보낼 생각 하지 말고, 클라이언트와 커넥션을 유지하면서 이미지를 보내는 방법들도 있다. 웹소켓이나 SSE 같은 방식인데, 다행히도 람다에서 해당 프로토콜들을 지원해준다. 둘 중에 어떤걸 사용할지 더 골라본다면, 일단은 SSE를 사용할 것 같다. 서버쪽에서 유저한테 일방향으로 내려주기만 하면 되고, SSE의 약점 중 하나가 유저와의 커넥션이 끊겼는지 감지할 수단이 마땅히 없다는 점인데 이런 1대1 방식에서는 해당 단점이 큰 문제가 될 것 같지는 않다.
참고
- 내 생각, 경험