报错信息
o.a.catalina.connector.CoyoteAdapter : Encountered a non-recycled response and recycled it forcedly.
org.apache.catalina.connector.CoyoteAdapter$RecycleRequiredException: null
at org.apache.catalina.connector.CoyoteAdapter.checkRecycled(CoyoteAdapter.java:521)
at org.apache.coyote.http11.Http11Processor.recycle(Http11Processor.java:1417)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.release(AbstractProtocol.java:1129)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:1052)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:840)
报错的代码
@Slf4j
public class V1SSEListener extends EventSourceListener {
@Getter
protected CountDownLatch countDownLatch = new CountDownLatch(1);
protected HttpServletResponse response;
@Override
public void onEvent(EventSource eventSource, String id, String type, String data) {
try {
response.getWriter().write("event:data\n");
JSONObject jsonObject = JSONUtil.parseObj(data);
String res = jsonObject.getStr("res");
if (StringUtils.isNotEmpty(res)) {
// 省略处理逻辑
}
} catch (Exception e) {
countDownLatch.countDown();
throw new RuntimeException(e);
}
}
@Override
public void onFailure(final EventSource eventSource, final Throwable t, final Response response) {
try {
this.response.getWriter().write("event:end\n\n");
this.response.getWriter().flush();
} catch (IOException e) {
log.error("使用事件源时出现异常....");
throw new RuntimeException(e);
} finally {
countDownLatch.countDown();
}
}
}
简单解释一下这段代码,就是从data的 res
里面拿到一些数据,做一些处理并且返回,如果处理的时候有异常的话,那么执行 countDownLatch.countDown();
即让等待的线程继续执行,放开执行权限
onFailure
因为OpenAi的接口不会调 close()
方法(这里没写出来),但是观察到似乎会调 onFailure
方法,于是想当然的就认为这个就相当于 close()
排错
思路一:
Encountered a non-recycled response and recycled it forcedly.
中文意思为:遇到未回收的回复,强行回收。
就想着是不是 response.getWriter()
没有关闭,所以就想着,加上 this.response.getWriter().close();
,但是问题依然在
,并且在本地调试的时候还发现,第一次请求不报错,第二次请求就一定会报错
思路二:
打断点,在 onFailure
方法内打断点,看下会不会调用 onFailure
方法
这时候发现,在调用 onFailure
方法的时候,countDownLatch
就已经是 0
了,震惊到我了,为什么???
然后去 onFailure
的 catch
里打断点,发现最后一条消息不是JSON数据,而是 [DONE]
,所以报错,然后执行了 countDownLatch.countDown();
并且又抛出了一个异常
修复
@Override
public void onEvent(EventSource eventSource, String id, String type, String data) {
try {
response.getWriter().write("event:data\n");
JSONObject jsonObject = JSONUtil.parseObj(data);
String res = jsonObject.getStr("res");
if (StringUtils.isNotEmpty(res)) {
// 省略处理逻辑
}
} catch (JSONException e) {
log.info("[DONE]");
} catch (Exception e) {
countDownLatch.countDown();
throw new RuntimeException(e);
}
}
就是在碰到这个异常的时候,不用执行 countDownLatch.countDown();
然后又发现,这么改根本不会执行 onFailure
方法,此时意识到,只有异常才会走 onFailure
方法,我giao
再次修复
@Override
public void onEvent(EventSource eventSource, String id, String type, String data) {
try {
response.getWriter().write("event:data\n");
JSONObject jsonObject = JSONUtil.parseObj(data);
String res = jsonObject.getStr("res");
if (StringUtils.isNotEmpty(res)) {
// 省略处理逻辑
}
} catch (JSONException e) {
log.info("[DONE]");
// 把 onFailure 方法里的逻辑移过来了
this.response.getWriter().write("event:end\n\n");
this.response.getWriter().flush();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
this.response.getWriter().close();
} catch (IOException e) {
log.error("关闭Writer时出现异常", e);
} finally {
countDownLatch.countDown();
}
}
}
至此,已经BUG已经修复完毕
完整代码如下:
@Slf4j
public class V1SSEListener extends EventSourceListener {
@Getter
protected CountDownLatch countDownLatch = new CountDownLatch(1);
protected HttpServletResponse response;
@Override
public void onEvent(EventSource eventSource, String id, String type, String data) {
try {
response.getWriter().write("event:data\n");
JSONObject jsonObject = JSONUtil.parseObj(data);
String res = jsonObject.getStr("res");
if (StringUtils.isNotEmpty(res)) {
// 省略处理逻辑
}
} catch (JSONException e) {
log.info("[DONE]");
// 把 onFailure 方法里的逻辑移过来了
this.response.getWriter().write("event:end\n\n");
this.response.getWriter().flush();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
this.response.getWriter().close();
} catch (IOException e) {
log.error("关闭Writer时出现异常", e);
} finally {
countDownLatch.countDown();
}
}
}
@Override
public void onFailure(final EventSource eventSource, final Throwable t, final Response response) {
log.error("使用事件源时出现异常....");
}
}
总结
- OpenAI只会触发
onEvent
方法,onFailure
是处理异常才会来到这个方法,不会调用onClose
方法 - LangServer在关闭连接的时候会调用
onClose
方法 - 使用的资源需要关闭
但是我还是没有明白为什么第一次请求不报错,而是第二次请求报错????