[Web]SSE Management

HW·2023년 5월 29일
0

서론

Java Spring에서 SSE를 List에 추가하며 관리하게 되었는데,
List에 emitter를 아무리 추가해도 최대 크기가 6개를 넘지 않는 현상이 발생했습니다.

이 현상에 대한 이유를 알아보려고 합니다.

본론

public class CustomSseEmitter extends SseEmitter{
    private Long id;
    public CustomSseEmitter() {
        super();
    }
    public CustomSseEmitter(Long timeout) {super(timeout);}
    public CustomSseEmitter(Long timeout, Long id) {
        super(timeout);
        this.id = id;
    }

    public void onError() {
    }
}
@Component
@Slf4j //로깅이 유용
public class SseEmitters {

    public enum DataType {
        CURRENT_GAME_STAGE(0),
        WAITING_LIST(1);

        private final int value;

        DataType (int value){
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    }

    private List<CustomSseEmitter> emitterList = new CopyOnWriteArrayList<>();

    static AtomicInteger counter;

    public SseEmitters (){
        this.counter = new AtomicInteger(0);
    }

    public Integer size (){
        return this.emitterList.size();
    }

    public boolean empty() {
        return (this.emitterList.size() == 0);
    }

    public CustomSseEmitter add(CustomSseEmitter emitter){
        this.emitterList.add(emitter);

        emitter.onCompletion(()->{
            this.emitterList.remove(emitter); //Emitter 객체 다시 생성 되기 때문에 자기 자신 지우기.
        });
        emitter.onTimeout(()->{
            emitter.complete(); //타임아웃 발생 시, 브라우저에서 재연결 요청 & 새로운 Emitter 객체 다시 생성.
        });

        return emitter;
    }

    public void send (String eventName){}

    public void send (String eventName, Map<String, Integer> data ){
        ExecutorService executorService = Executors.newFixedThreadPool(emitterList.size());

        emitterList.forEach( emitter->{
            executorService.submit(()->{
                try {
                    System.out.println("출력"+eventName);
                    emitter.send(CustomSseEmitter.event()
                            .name(eventName)
                            .data(data)
                    );
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        });
        executorService.shutdown();
    }
}
private class WaitingListEmitters extends CustomSseEmitter {
        @Override
        public void send (String eventName){ //WaintingList
            DataMap dataMap = new DataMap();
            List<String> waitngList = memberService.getWaitingList();
            send (eventName, dataMap.mapOf("waitingList", waitngList));
        }

        public void send (String eventName, String memberName){ //AdditionalWaitingList
            DataMap dataMap = new DataMap();
            send (eventName, dataMap.mapOf("memberName", memberName));
        }


    }

게임 페이지에 진입하면 emitterList에 emitter가 추가되고

event 발생 시 send를 하도록 구현했습니다.

server.tomcat.max-threads 값과 관련됐다고 해서 값을 늘려보기도 하고

server.tomcat.max-connections 값을 늘려보기도 했는데 여전히 최대 크기는 6개였습니다.

이 문제의 원인을 알아보기 위해 여러 가지 시도를 해보았지만, 서버 설정과는 무관한 것으로 밝혀졌습니다.

알고보니 서버 설정과 관계 없이, 이는 브라우저가 단일 도메인에 대한 동시 연결 수를 제한하고 있었기 때문이었죠.

실제로 문제는 크롬 브라우저에서 여러 창을 띄워 테스트하는 과정에서 발생한 것으로 확인되었습니다.

이러한 제한은 브라우저에서 과도한 리소스 사용과 성능 문제를 방지하기 위해 설정되어 있는 것인데요.

이전에는 브라우저가 도메인당 동시 연결 수를 엄격하게 2개로 제한했지만, 최근의 브라우저는 성능 향상을 위해 이 제한을 늘려두고 있으며, 일반적으로 도메인당 4에서 10개의 연결을 허용하고 있습니다.

따라서 여러 창을 테스트하면 단일 도메인에 대한 연결 수가 6개로 제한되는 것입니다.

When not used over HTTP/2, SSE suffers from a limitation to the maximum number of open connections, which can be especially painful when opening multiple tabs, as the limit is per browser and is set to a very low number (6). The issue has been marked as "Won't fix" in Chrome and Firefox. This limit is per browser + domain, which means that you can open 6 SSE connections across all of the tabs to www.example1.com and another 6 SSE connections to www.example2.com (per Stackoverflow). When using HTTP/2, the maximum number of simultaneous HTTP streams is negotiated between the server and the client (defaults to 100). - 출처 모질라

결론

SSE, 웹소켓 같은 연결형 통신을 사용할 때는 이 통신들이 브라우저에서 허용하는 총 연결 수에 포함됩니다.

제한에 도달하면 브라우저 자체에서 기존 연결이 닫히거나 해제되기 전까지 새로운 연결을 막습니다.

따라서 애플리케이션을 개발할 때 이 점을 고려하여 적절한 로직을 구현해야합니다.

profile
예술융합형 개발자🎥

0개의 댓글