在浏览器中刷新页面时 sseEmitter 完成
我正在使用 sse 发射器实现通知系统, 1-客户端调用订阅 APi,服务器创建新的 ssEmtter 并将其返回给客户端
SseEmitter sseEmitter = new SseEmitter(24 * 60 * 60 * 1000l);
emitterService.addEmitter(sseEmitter);
log.info("subscribed");
return sseEmitter;
}
2-在发布服务器中使用此 sseEmiter 并发送消息
我的问题是当我在浏览器中刷新页面时,服务器尝试发送消息我得到了这个例外 即使我在浏览器中刷新页面,我怎样才能保持 sse 发射器的连接? java.lang.IllegalStateException:ResponseBodyEmitter 已完成 在 org.springframework.util.Assert.state(Assert.java:76) 在 org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter.send(ResponseBodyEmitter.java:196) 在 org.springframework.web.servlet.mvc.method.annotation.SseEmitter.send(SseEmitter.java:126) 在 com.efinance.service.entity.service.EmitterServiceImpl.lambda$2(EmitterServiceImpl.java:102) 在 java.base/java.util.ArrayList.forEach(ArrayList.java:1541) 在 com.efinance.service.entity.service.EmitterServiceImpl.pushNotification(EmitterServiceImpl.java:95) 在 com.efinance.service.entity.service.EmitterServiceImpl$$FastClassBySpringCGLIB$$9e4e84c6.invoke(<生成>) 在 org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) 在 org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:687) 在 com.efinance.service.entity.service.EmitterServiceImpl$$EnhancerBySpringCGLIB$$c2c5d230.pushNotification(<生成>) 在 com.efinance.service.entity.service.EntityPansUnderThresholdJob.notifyEntityPansUnderThreshold(EntityPansUnderThresholdJob.java:57) 在 jdk.internal.reflect.GenerateMethodAccessor136.invoke(来源未知) 在java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.base/java.lang.reflect.Method.invoke(Method.java:566) 在org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) 在 org.springframework.aop.framework.ReflectiveMethodInspiration.invokeJoinpoint(ReflectiveMethodInspiration.java:198) 在 org.springframework.aop.framework.CglibAopProxy$CglibMethodInspiration.invokeJoinpoint(CglibAopProxy.java:774) 在 org.springframework.aop.framework.ReflectiveMethodInspiration.proceed(ReflectiveMethodInitation.java:163) 在 org.springframework.aop.framework.CglibAopProxy$CglibMethodInitation.proceed(CglibAopProxy.java:749) 在org.springframework.aop.aspectj.MethodInitationProceedingJoinPoint.proceed(MethodInitationProceedingJoinPoint.java:88) 在 com.efinance.service.entity.tenant.TenantAspect.lambda$0(TenantAspect.java:36) 在 java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:684) 在 com.efinance.service.entity.tenant.TenantAspect.handelTenantNames(TenantAspect.java:29) 在 jdk.internal.reflect.GenerateMethodAccessor138.invoke(来源未知) 在java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.base/java.lang.reflect.Method.invoke(Method.java:566) 在org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644) 在org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633) 在 org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70) 在 org.springframework.aop.framework.ReflectiveMethodInspiration.proceed(ReflectiveMethodInitation.java:175) 在 org.springframework.aop.framework.CglibAopProxy$CglibMethodInitation.proceed(CglibAopProxy.java:749) 在org.springframework.aop.interceptor.ExposeInitationInterceptor.invoke(ExposeInitationInterceptor.java:95) 在 org.springframework.aop.framework.ReflectiveMethodInspiration.proceed(ReflectiveMethodInitation.java:186) 在 org.springframework.aop.framework.CglibAopProxy$CglibMethodInitation.proceed(CglibAopProxy.java:749) 在 org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691) 在 com.efinance.service.entity.service.EntityPansUnderThresholdJob$$EnhancerBySpringCGLIB$$e85299cc.notifyEntityPansUnderThreshold(<生成>) 在 jdk.internal.reflect.GenerateMethodAccessor136.invoke(来源未知) 在java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.base/java.lang.reflect.Method.invoke(Method.java:566) 在org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) 在 org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) 在 java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) 在 java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305) 在 java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) 在java.base / java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) 在 java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) 在 java.base/java.lang.Thread.run(Thread.java:834)
客户端,我正在使用 Angular,
console.log('call subscripe'); const eventSource = new EventSourcePolyfill(('http://localhost:8587/subscription'), { headers: { 'Authorization': 'Bearer ' + sessionStorage.getItem("token") }, heartbeatTimeout: 1000000 });
eventSource.onopen = (e) => console.log("open");
eventSource.onmessage = (e) => { console.log(e)}
eventSource.onerror = (e) => {
if (e.readyState == EventSource.CLOSED) {
console.log("close");
} else {
console.log(e);
}
即使我刷新浏览器中的页面,如何保持 sse 发射器的连接?
i am implementing notification system using sse emitter,
1-the client call for subscribe APi and the server create new ssEmtter and return it to the client
SseEmitter sseEmitter = new SseEmitter(24 * 60 * 60 * 1000l);
emitterService.addEmitter(sseEmitter);
log.info("subscribed");
return sseEmitter;
}
2- in publish the server use this sseEmiter and send the message
my question is when i refresh the page in browser then the server try to send message i got this exception
how could i keep the connection of sse emitter live even i refresh the page in the browser
java.lang.IllegalStateException: ResponseBodyEmitter has already completed
at org.springframework.util.Assert.state(Assert.java:76)
at org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter.send(ResponseBodyEmitter.java:196)
at org.springframework.web.servlet.mvc.method.annotation.SseEmitter.send(SseEmitter.java:126)
at com.efinance.service.entity.service.EmitterServiceImpl.lambda$2(EmitterServiceImpl.java:102)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at com.efinance.service.entity.service.EmitterServiceImpl.pushNotification(EmitterServiceImpl.java:95)
at com.efinance.service.entity.service.EmitterServiceImpl$$FastClassBySpringCGLIB$$9e4e84c6.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:687)
at com.efinance.service.entity.service.EmitterServiceImpl$$EnhancerBySpringCGLIB$$c2c5d230.pushNotification(<generated>)
at com.efinance.service.entity.service.EntityPansUnderThresholdJob.notifyEntityPansUnderThreshold(EntityPansUnderThresholdJob.java:57)
at jdk.internal.reflect.GeneratedMethodAccessor136.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:774)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
at com.efinance.service.entity.tenant.TenantAspect.lambda$0(TenantAspect.java:36)
at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
at com.efinance.service.entity.tenant.TenantAspect.handelTenantNames(TenantAspect.java:29)
at jdk.internal.reflect.GeneratedMethodAccessor138.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
at com.efinance.service.entity.service.EntityPansUnderThresholdJob$$EnhancerBySpringCGLIB$$e85299cc.notifyEntityPansUnderThreshold(<generated>)
at jdk.internal.reflect.GeneratedMethodAccessor136.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
for client i am using angular
console.log('call subscripe'); const eventSource = new EventSourcePolyfill(('http://localhost:8587/subscription'), { headers: { 'Authorization': 'Bearer ' + sessionStorage.getItem("token") }, heartbeatTimeout: 1000000 });
eventSource.onopen = (e) => console.log("open");
eventSource.onmessage = (e) => { console.log(e)}
eventSource.onerror = (e) => {
if (e.readyState == EventSource.CLOSED) {
console.log("close");
} else {
console.log(e);
}
how could i keep the connection of sse emitter live even i refresh the page in the browser?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
当你刷新时。您必须再次订阅您的客户端。
这将在后端创建一个新的 SseEmitter。然后当你想从后端发送新数据时发送到所有相关的SseEmitters。
一个会成功,其他将从后端发送失败(因为刷新破坏了前端的旧 eventSource 侦听器)。从发射器服务中删除那些失败的。
When you refresh. You have to subscribe your client again.
This will create a new SseEmitter in the backend. Then when you want to send new data from the backend send to all relevant SseEmitters.
One will succeed, the others will fail sending from the backend (because the refresh destroyed the old eventSource listeners on the frontend). Remove those failed from your emitterService.