Hessian远程服务InputStream流关闭异常问题求助
解决Hessian远程调用中InputStream读取时"stream is closed"异常
这个问题的核心原因是Hessian与Spring的默认封装机制导致HTTP连接提前关闭:当你通过Spring的HessianServiceExporter返回InputStream时,Hessian并不会将流的内容完整序列化到响应中,而是仅传递一个"引用";但Spring会在请求处理完成后立即关闭HTTP响应流,此时客户端还没来得及读取流内容,就会触发stream is closed异常。
Hessian官网提到的"自行处理流",本质是让你避免直接返回InputStream,或者手动控制Hessian的流传输逻辑,下面提供两种可行方案:
方案一:转成byte[]传输(简单通用,适合中小数据量)
这是最直接的解决方式,把服务端的流内容提前转为字节数组,让Hessian完整序列化整个数组,客户端拿到的是完整数据,不依赖HTTP连接存活。
修改服务接口与实现
// 服务接口:将返回值改为byte[] public interface EchoService { byte[] echo(String text); } // 服务实现:直接返回字节数组,无需InputStream public class EchoServiceImpl implements EchoService { @Override public byte[] echo(String text) { System.out.println("Echo on server " + text); return text.getBytes(); } }
修改客户端代码
import com.caucho.hessian.client.HessianProxyFactory; public class Client { public static void main(String[] args) throws Exception { HessianProxyFactory factory = new HessianProxyFactory(); EchoService service = (EchoService) factory.create(EchoService.class, "http://localhost:8080/repeat"); // 直接获取byte[],无需处理流 byte[] responseData = service.echo(args[0]); System.out.println(new String(responseData)); } }
这个方案完全兼容Spring的HessianServiceExporter,不需要额外配置,解决了连接提前关闭的问题。
方案二:使用Hessian Streaming API(适合大数据量,避免内存溢出)
如果数据量极大,无法一次性转为byte[],可以使用Hessian原生的流式API,手动控制请求与响应的流传输,脱离Spring的自动封装。
服务端自定义Controller(替代HessianServiceExporter)
import com.caucho.hessian.server.HessianSkeleton; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.InputStream; @RestController public class HessianStreamController { // 手动创建Hessian骨架,绑定服务接口与实现 private final HessianSkeleton skeleton = new HessianSkeleton(EchoStreamService.class, new EchoStreamServiceImpl()); @PostMapping("/repeat-stream") public void handleStreamRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { response.setContentType("application/x-hessian"); // 直接操作Hessian的输入输出流,避免Spring提前关闭连接 skeleton.invoke(request.getInputStream(), response.getOutputStream()); } // 保持原接口的InputStream返回值 public interface EchoStreamService { InputStream echo(String text); } public static class EchoStreamServiceImpl implements EchoStreamService { @Override public InputStream echo(String text) { System.out.println("Echo on server " + text); return new ByteArrayInputStream(text.getBytes()); } } }
客户端手动处理流式调用
import com.caucho.hessian.io.Hessian2Input; import com.caucho.hessian.io.Hessian2Output; import com.google.common.io.ByteStreams; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; public class StreamClient { public static void main(String[] args) throws Exception { URL url = new URL("http://localhost:8080/repeat-stream"); URLConnection conn = url.openConnection(); conn.setDoOutput(true); conn.setRequestProperty("Content-Type", "application/x-hessian"); // 手动写入Hessian请求:调用echo方法并传递参数 Hessian2Output out = new Hessian2Output(conn.getOutputStream()); out.startCall("echo"); out.writeString(args[0]); out.completeCall(); out.close(); // 手动读取Hessian响应:用readInputStream获取流,确保连接在读取完成后才关闭 Hessian2Input in = new Hessian2Input(conn.getInputStream()); in.startReply(); try (InputStream is = in.readInputStream()) { byte[] data = ByteStreams.toByteArray(is); System.out.println(new String(data)); } in.completeReply(); in.close(); } }
这种方式直接操作Hessian的底层流,让服务端和客户端共同维护流的生命周期,不会出现提前关闭的问题,适合处理GB级别的大文件或数据流。
关键注意事项
- 若使用方案一,需注意数据大小:过大的byte[]会导致JVM内存溢出,此时必须切换到方案二。
- 保持服务端与客户端的Hessian版本一致(你当前使用的4.0.51版本没问题),避免序列化/反序列化兼容性问题。
内容的提问来源于stack exchange,提问作者willix
相关产品推荐
相关产品推荐

