2017-12-28 34 views
3

이 게시물에서 스프링 SseEmitter를 사용하려고합니다. Angular 2 spring boot server side events. 푸시 이벤트가 작동하지만 탭을 닫거나 새로 고칠 때마다 예외가 발생합니다.봄 MVC SseEmitter - 연결이 중단되었습니다. 예외

이상한 점은 try try catch 블록으로 둘러싸인 메소드를 보내면 예외가 발생한다는 것입니다. 예외는 catch되고 기록되어야하며 메소드 내에서 다시 throw되어야합니다. 그러나 나는 그것을 어떻게 막을 수 있습니까? 오류 로그를 표시하지 않으려합니다.

IOException: Eine bestehende Verbindung wurde softwaregesteuert durch den Hostcomputer abgebrochen 

IOEexception an established connection was aborted by the software in your host machine 

고마워요!

SseController.java

import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RequestMethod; 
import org.springframework.web.bind.annotation.RestController; 
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; 

import java.io.IOException; 
import java.util.ArrayList; 
import java.util.Collections; 
import java.util.List; 

@RestController 
public class SSEController { 

    public static final List<SseEmitter> emitters = Collections.synchronizedList(new ArrayList<>()); 

    @RequestMapping(path = "/stream", method = RequestMethod.GET) 
    public SseEmitter stream() throws IOException { 

     SseEmitter emitter = new SseEmitter(); 

     emitters.add(emitter); 
     emitter.onCompletion(() -> emitters.remove(emitter)); 

     return emitter; 
    } 
} 

ServiceClass.java

@Scheduled 
public void sendSseEventsToUI(Notification notification) { //your model class 
     List<SseEmitter> sseEmitterListToRemove = new ArrayList<>(); 
     SSEController.emitters.forEach((SseEmitter emitter) -> { 
      try { 
       emitter.send(notification, MediaType.APPLICATION_JSON); 
      } catch (Exception e) { 
       emitter.complete(); 
       sseEmitterListToRemove.add(emitter); 
      } 
     }); 
     SSEController.emitters.removeAll(sseEmitterListToRemove); 
    } 

예외 :

> 2017-12-27 13:54:53.206 INFO 4248 --- [pool-4-thread-1] 
> o.apache.coyote.http11.Http11Processor : An error occurred in 
> processing while on a non-container thread. The connection will be 
> closed immediately 
> 
> java.io.IOException: Eine bestehende Verbindung wurde 
> softwaregesteuert durch den Hostcomputer abgebrochen at 
> sun.nio.ch.SocketDispatcher.write0(Native Method) ~[na:1.8.0_121]  at 
> sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:51) 
> ~[na:1.8.0_121] at 
> sun.nio.ch.IOUtil.writeFromNativeBuffer(IOUtil.java:93) 
> ~[na:1.8.0_121] at sun.nio.ch.IOUtil.write(IOUtil.java:65) 
> ~[na:1.8.0_121] at 
> sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:471) 
> ~[na:1.8.0_121] at 
> org.apache.tomcat.util.net.NioChannel.write(NioChannel.java:134) 
> ~[tomcat-embed-core-8.5.23.jar:8.5.23] at 
> org.apache.tomcat.util.net.NioBlockingSelector.write(NioBlockingSelector.java:101) 
> ~[tomcat-embed-core-8.5.23.jar:8.5.23] at 
> org.apache.tomcat.util.net.NioSelectorPool.write(NioSelectorPool.java:157) 
> ~[tomcat-embed-core-8.5.23.jar:8.5.23] at 
> org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.doWrite(NioEndpoint.java:1267) 
> ~[tomcat-embed-core-8.5.23.jar:8.5.23] at 
> org.apache.tomcat.util.net.SocketWrapperBase.doWrite(SocketWrapperBase.java:670) 
> ~[tomcat-embed-core-8.5.23.jar:8.5.23] at 
> org.apache.tomcat.util.net.SocketWrapperBase.flushBlocking(SocketWrapperBase.java:607) 
> ~[tomcat-embed-core-8.5.23.jar:8.5.23] at 
> org.apache.tomcat.util.net.SocketWrapperBase.flush(SocketWrapperBase.java:597) 
> ~[tomcat-embed-core-8.5.23.jar:8.5.23] at 
> org.apache.coyote.http11.Http11OutputBuffer.flushBuffer(Http11OutputBuffer.java:581) 
> ~[tomcat-embed-core-8.5.23.jar:8.5.23] at 
> org.apache.coyote.http11.Http11OutputBuffer.flush(Http11OutputBuffer.java:272) 
> ~[tomcat-embed-core-8.5.23.jar:8.5.23] at 
> org.apache.coyote.http11.Http11Processor.flush(Http11Processor.java:1560) 
> ~[tomcat-embed-core-8.5.23.jar:8.5.23] at 
> org.apache.coyote.AbstractProcessor.action(AbstractProcessor.java:283) 
> ~[tomcat-embed-core-8.5.23.jar:8.5.23] at 
> org.apache.coyote.Response.action(Response.java:173) 
> [tomcat-embed-core-8.5.23.jar:8.5.23]  at 
> org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:317) 
> [tomcat-embed-core-8.5.23.jar:8.5.23]  at 
> org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:284) 
> [tomcat-embed-core-8.5.23.jar:8.5.23]  at 
> org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:118) 
> [tomcat-embed-core-8.5.23.jar:8.5.23]  at 
> sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:297) 
> [na:1.8.0_121] at 
> sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141) [na:1.8.0_121] 
> at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229) 
> [na:1.8.0_121] at 
> org.springframework.util.StreamUtils.copy(StreamUtils.java:119) 
> [spring-core-4.3.13.RELEASE.jar:4.3.13.RELEASE] at 
> org.springframework.http.converter.StringHttpMessageConverter.writeInternal(StringHttpMessageConverter.java:106) 
> [spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at 
> org.springframework.http.converter.StringHttpMessageConverter.writeInternal(StringHttpMessageConverter.java:41) 
> [spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at 
> org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:227) 
> [spring-web-4.3.13.RELEASE.jar:4.3.13.RELEASE] at 
> org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler$HttpMessageConvertingHandler.sendInternal(ResponseBodyEmitterReturnValueHandler.java:207) 
> [spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]  at 
> org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler$HttpMessageConvertingHandler.send(ResponseBodyEmitterReturnValueHandler.java:200) 
> [spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]  at 
> org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter.sendInternal(ResponseBodyEmitter.java:166) 
> [spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]  at 
> org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter.send(ResponseBodyEmitter.java:159) 
> [spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]  at 
> org.springframework.web.servlet.mvc.method.annotation.SseEmitter.send(SseEmitter.java:126) 
> [spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]  at 
> org.springframework.web.servlet.mvc.method.annotation.SseEmitter.send(SseEmitter.java:107) 
> [spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]  at 
> com.example.demo.PushService.sendSseEventsToUI(PushService.java:22) 
> [classes/:na]  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native 
> Method) ~[na:1.8.0_121] at 
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
> ~[na:1.8.0_121] at 
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
> ~[na:1.8.0_121] at java.lang.reflect.Method.invoke(Method.java:498) 
> ~[na:1.8.0_121] at 
> org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65) 
> [spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at 
> org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) 
> [spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE] at 
> java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) 
> [na:1.8.0_121] at 
> java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) 
> [na:1.8.0_121] at 
> java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) 
> [na:1.8.0_121] at 
> java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) 
> [na:1.8.0_121] at 
> java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
> [na:1.8.0_121] at 
> java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
> [na:1.8.0_121] at java.lang.Thread.run(Thread.java:745) 
> [na:1.8.0_121] 
+0

당신 사용하고 있습니까? 내가 가지고있는 문제를 재현 할 수 없다. – shazin

+0

PushService.java의 22 행이 무엇인지 알 수없고 주어진 스택 추적을 인쇄해서는 안된다는 점을 감안할 때 문제를 실제로 이해하지 못한다. 질문을 실제 코드 및 행 번호 정보로 업데이트 해주십시오. 이 문제는 클라이언트가 연결을 닫을 때 발생합니다. – JEY

+0

예제 프로젝트를 만들었습니다. https://git.io/vNff7. 실행 - 크롬 http : // localhost : 8080 열기 guid가 인쇄 될 때까지 기다리십시오. 탭/브라우저를 닫으십시오. 다음에 send 메소드가 호출 될 때 모든 에러를 포착 했음에도 불구하고 stacktrace가 출력됩니다. – meleagros

답변

1

당신은 org.apache.coyote.http11에서 로그를보고있다 .Http11Processor. 서블릿 API는 클라이언트 연결이 닫힐 때이를 알리지 않기 때문에 가져옵니다. 당신이 (당신의 application.properties에서)이 속성을 추가하여이 로그가이 패키지에 대한 로그 수준을 변경보고 싶지 않은 경우가 jira.spring.io/browse/SPR-13292

에서 어쨌든 소켓, 더 많은 정보를 작성하려고하는 이유는 다음과 같습니다

logging.level.org.apache.coyote.http11=ERROR 

로거 구성 파일을 편집 할 수 있습니다. 컨트롤러에서

private final Collection<SseEmitter> emitters = Collections.synchronizedCollection(new HashSet<SseEmitter>()); 

public void register(SseEmitter emitter) { 
    emitter.onTimeout(() -> timeout(emitter)); 
    emitter.onCompletion(() -> complete(emitter)); 

    emitters.add(emitter); 
} 

private void complete(SseEmitter emitter) { 
    System.out.println("emitter completed"); 
    emitters.remove(emitter); 
} 

private void timeout(SseEmitter emitter) { 
    System.out.println("emitter timeout"); 
    emitters.remove(emitter); 
} 

@Scheduled(fixedDelay = 3000) 
public void sendSseEventsToUI() { //your model class 
    for(SseEmitter emitter : emitters) { 
     try { 
      emitter.send(UUID.randomUUID().toString(), MediaType.APPLICATION_JSON); 
     } catch (Throwable e) { 
      emitter.complete(); 
     } 
    }; 
} 

서비스에서

: 또한 당신의 이미 터를 유지하기위한 정적 최종 수집을 사용하지 않는

같은 일을 봄 버전이 무엇

@Autowired 
public PushController(PushService service) { 
    this.service = service; 
} 

@RequestMapping(path = "/", method = RequestMethod.GET) 
public SseEmitter stream() { 
    final SseEmitter emitter = new SseEmitter(0L); 
    service.register(emitter); 
    return emitter; 
} 
+0

몇 가지 유용한 정보가있을 수 있기 때문에 로그를 표시하고 싶지 않습니다. 어쩌면 문제는 전송 전에 연결이 중단되었는지 확인해야하는 스프링 방출기의 누락 된 체크입니다. 또는 에미 터는 isConnected 메소드 등을 프로그램 적으로 검사 할 수 있습니다. 내 설명에서 : "나는 오류 로그를 억제하고 싶지 않아." – meleagros

+1

문제는 클라이언트 연결이 닫힐 때 알리지 않는 서블릿 API에서 발생합니다. 그래서 어쨌든 소켓에 쓰기를 시도합니다. https://jira.spring.io/browse/SPR-13292 – JEY

+0

감사!티켓에 대한 링크와 서블릿 스펙이 연결이 끊어진 것에 대한 통지를 제공하지 않으므로 제대로 처리 할 수 ​​없다는 정보로 응답을 업데이트하십시오. 동의하겠습니다. – meleagros