背景:
使用okhttp框架进行网络访问时,一般会使用 HttpLoggingInterceptor 打印请求和响应的log。在使用okhttp访问AI大模型时,如果选择流式输出,那么响应的body数据使用的SSE技术,服务异步发送大模型生成的增量token,使用HttpLoggingInterceptor拦截后,会在所有数据接收完成后一次打印完,业务逻辑处也是在log打印完后,才集中收到全部的内容,达不到流式返回的效果。
优化方案:
优化的思路是将body 流拦截打印,然后重新构建response,responsebody和inputstream。将真实的body数据重新写入到重新构建的responsebody流里面,这样业务端就会实时收到流式返回的内容。下面是拦截打印body部分的代码:
@Override
public Response intercept(@NotNull Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
PipedOutputStream pipedOutputStream = new PipedOutputStream();
PipedInputStream pipedInputStream = new PipedInputStream(pipedOutputStream);
BufferedSource cloneSource = Okio.buffer(Okio.source(pipedInputStream));
ResponseBody cloneBody = ResponseBody.create(cloneSource, response.body().contentType() , response.body().contentLength());
Response cloneResponse = response.newBuilder().body(cloneBody).build();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(pipedOutputStream));
InputStream inputStream = response.body().byteStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
new Thread(() -> {
String line;
while (true) {
try {
if ((line = reader.readLine()) == null) break;
} catch (IOException e) {
e.printStackTrace();
break;
}
System.out.println(">>>>>>>>>>>>> log intercept:" + line);
try {
writer.write(line);
writer.write("\r\n");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("log intercept end");
}).start();
return cloneResponse;
}