背景:
使用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;
}