
프로젝트에서 Back과 Front에서 SSE 통신을 하던 중 발생한 에러의 트러블 슈팅 과정을 기록해보았다.
ERR_INCOMPLETE_CHUNKED_ENCODING
넌 또 뭐니..!
로컬 → 로컬에서 SSE 통신이 잘 이루어졌으나, 로컬 → 서버로 요청시 SSE로 데이터가 전송이 안되는 문제가 발생하였다.
SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
SSE 커넥션 자체는 열렸으나. 데이터 전송이 되지 않고 약 1분이 지나자 연결이 끊어졌다.
Chunked Transfer Encoding 이란?
HTTP 프로토콜에서 사용되는 전송 인코딩 방식 중 하나로, HTTP 응답 메시지의 본문(body)을 여러 개의 조각(chunk)으로 나누어 전송하는 방식
일반적으로 HTTP 응답에서 본문의 크기를 미리 알 수 없거나, 동적으로 생성되는 경우에 사용된다.
예를 들어, 웹 서버가 대용량 파일을 전송하거나, 실시간으로 생성되는 데이터를 전송하는 경우에 Chunked Transfer Encoding이 유용하게 사용된다.
Nginx의 proxy buffering 기능은 default 값이 ON
→ 실시간 전송이 되어야 하는 SSE에서 연결이 끊김.
로컬에서는 Nginx을 통해서 진행되지 않기 때문에 해당 문제가 발생하지 않았다.
여기서 프록시버퍼링이란?
- 프록시 서버는 데이터를 실시간으로 전송하는 것이 아닌, 버퍼에 데이터를 일정량이 모이면 전송하는 방식 사용.
→ 네트워크 효율성 높이고, 빠르게 데이터 전달하는데 도움.
스프링서버에서 Sse 연결시간을 길게 잡아도 Nginx 에서 prox_read_timeout동안 아무런 데이터 전송이 없으면 연결을 끊음.
응답 헤더를 아래와 같이 설정하면 된다.
Content-Type: text/event-stream;
Cache-Control: no-cache;
X-Accel-Buffering: no;
아래 코드와 같이 헤더에 캐시, 버퍼 설정해줄 수 있다.
headers.add("Cache-Control", "no-cache");headers.add("X-Accel-Buffering", "no");@GetMapping(value = "/subscribe", produces = "text/event-stream")
public ResponseEntity<SseEmitter> subscribe() {
System.out.println("sse 연결");
Long userId = 1L;
SseEmitter sseEmitter = sseEmitters.addEmitter(cacheService.cacheRequestIdWithUserId(userId));
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache");
headers.add("X-Accel-Buffering", "no");
return new ResponseEntity<>(sseEmitter, headers, HttpStatus.OK);
}
sse통신을 하는 서버에서 오는 요청에 대해 buffering off 설정할 수 있다. (많은 블로그에서도 이 방법을 제시하고 있다)
하지만 이는 proxy_buffering Nginx의 성능 최적화를 위한 것으로, 무조건 proxy_buffering off를 하는 것이 좋은 방법은 아닐 것이다.
전체 proxy_buffering을 off하는 것 외에 다른 방법은 없을까?
location /api { # /api 로 들어오는 요청은 개발용 서버로
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://ssafy;
proxy_pass_request_headers on;
proxy_buffering off; # 이것 보다는 아래 방법으로 해보자
} location /api {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://ssafy;
proxy_pass_request_headers on;
proxy_read_timeout 120s; # timeout default 60s에서 120s
}https://velog.io/@sally_devv/알람-서비스와-SSE
https://nginxstore.com/blog/nginx/가장-많이-실수하는-nginx-설정-에러-10가지/
로컬환경에선 잘되던게 운영환경가니 안되길래
헤더추가해서 된건지 return type을 SseEmitter 에서 ResponseEntity 로 바꿔서 해결된건지 어쨌든 해결이 되었습니다.