Dubbo序列化与反序列化问题、服务端获取到的请求参数类型转换异常(变成了Map类型的对象)

java.io.StreamCorruptedException: invalid stream header: 77D30000 ; Decode rpc invocation failed

现象

2023-02-14 09:49:32.251 [NettyServerWorker-4-2] [] [WARN] o.a.d.r.exchange.codec.ExchangeCodec-130  [DUBBO] Skip input stream 634, dubbo version: 2.7.0, current host: 10.10.1.44
2023-02-14 09:49:36.858 [NettyServerWorker-4-1] [] [WARN] o.a.d.r.p.d.DecodeableRpcInvocation-73  [DUBBO] Decode rpc invocation failed: invalid stream header: 77D30000, dubbo version: 2.7.0, current host: 10.10.1.44
java.io.StreamCorruptedException: invalid stream header: 77D30000
        at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:899)
        at java.io.ObjectInputStream.(ObjectInputStream.java:357)
        at org.apache.dubbo.common.serialize.java.JavaObjectInput.(JavaObjectInput.java:33)
        at org.apache.dubbo.common.serialize.java.JavaSerialization.deserialize(JavaSerialization.java:47)
        at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:91)
        at org.apache.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode(DecodeableRpcInvocation.java:70)
        at org.apache.dubbo.rpc.protocol.dubbo.DubboCodec.decodeBody(DubboCodec.java:132)
        at org.apache.dubbo.remoting.exchange.codec.ExchangeCodec.decode(ExchangeCodec.java:125)
        at org.apache.dubbo.remoting.exchange.codec.ExchangeCodec.decode(ExchangeCodec.java:85)
        at org.apache.dubbo.rpc.protocol.dubbo.DubboCountCodec.decode(DubboCountCodec.java:46)
        at org.apache.dubbo.remoting.transport.netty4.NettyCodecAdapter$InternalDecoder.decode(NettyCodecAdapter.java:95)
        at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:501)
        at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:440)
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.lang.Thread.run(Thread.java:748)

解决

配置 dubbo.protocol.serialization 的值 在客户端和服务端保持一致。
Dubbo序列化与反序列化问题、服务端获取到的请求参数类型转换异常(变成了Map类型的对象)_第1张图片
https://dubbo.apache.org/zh-cn/docs3-v2/java-sdk/reference-manual/protocol/dubbo/#%E7%89%B9%E6%80%A7%E8%AF%B4%E6%98%8E

  • Transporter: mina, netty, grizzy
  • Serialization: dubbo, hessian2, java, json
  • Dispatcher: all, direct, message, execution, connection
  • ThreadPool: fixed, cached

Serialization

  1. Dubbo 序列化,未开发成熟
  2. hessian2 , Thrift 之前使用的,是一种跨语言的高效二进制序列化方式
  3. json序列化:目前两种,一种是阿里的 fastjson 库,另一种是采用 dubbo 中自己实现的简单 json 库,建议使用 jkson
  4. Java序列化:主要采用JDK自带的Java序列化实现,性能很不理想
    针对Java语言的:Kryo, FST等等 Kryo是一种非常成熟的序列化实现,已经在Twitter\Groupon\Yahoo以及多个著名开源项目(如Hive\Storm)中广泛使用.FST则较新,缺乏足够多的成熟案例
    跨语言的:Protostuff, ProtoBuf, Thrift, Avro, MsgPack等等

服务端获取导入的入参变成了Map类型的对象

现象:

dubbo的接口

public interface PersonApi {
	void addUser(Request<PersonDTO> req);
}
@Data
public class Request<T> {
	private String invokeId;
	private T data;
}

@Data
public class PersonDTO {
	private String userName;
	private Integer age;
}

public class StudentDTO extends PersonDTO {
	private String className;
}

客户端调用


public class Demo {
		
	@Reference
	private PersonApi personApi;
	
	// 正常调用
	public void normalInvoke() {
		PersonDTO person = new PersonDTO();
		person.setName("IccBoY");
		person.setAge(20);
		Request<PersonDTO> req = new Request<PersonDTO>();
		req.setData(person);
		personApi.addUser(req);
	}
	// 上面这种方式服务端可以正常获取对象

	// 通过子类调用
	public void normalInvoke() {
		PersonDTO person = new StudentDTO();
		person.setName("IccBoY");
		person.setAge(20);
		person.setClassName("一班");
		Request<PersonDTO> req = new Request<PersonDTO>();
		req.setData(person);
		personApi.addUser(req);
	}
	// 上面这种方式,服务端 打印req日志正常,但是调用 PersonDTO person = req.getData() 时,报类型转换异常
}

经过调试,req.Data() 的类型是Map类型。原因是 客户端 使用 PersonDTO的子类 作为 入参。然后服务端没有这个StudentDTO对象,然后就给转成了Map对象。

解决办法:

  1. 客户端调用时,严格验证 服务端提供的api来调用,不要使用起客户端新写的子类来调用。

补充

还有一种情况,服务端api升级后,移动了请求参数DTO的包路径,但是客户端并没有升级,也会导致上面的问题。

综上,为了避免上面的问题,客户端和服务度的方法签名必须要保持一致,如果服务端出现了对接口方法定义进行了调整(非兼容性升级)则需要及时告知调用方,进行升级。

你可能感兴趣的:(Java,后端,问题,dubbo,java,开发语言)