在浏览器中刷新页面时 sseEmitter 完成

发布于 2025-01-16 13:35:22 字数 5458 浏览 3 评论 0原文

我正在使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

遮云壑 2025-01-23 13:35:23

当你刷新时。您必须再次订阅您的客户端。

这将在后端创建一个新的 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文