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

22 篇文章 0 订阅
13 篇文章 0 订阅

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.<init>(ObjectInputStream.java:357)
        at org.apache.dubbo.common.serialize.java.JavaObjectInput.<init>(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 的值 在客户端和服务端保持一致。
在这里插入图片描述
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的包路径,但是客户端并没有升级,也会导致上面的问题。

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 将一个对象放入HashMap中的过程如下: 1. 创建一个HashMap对象,或者使用已有的HashMap对象。 2. 创建要放入HashMap中的对象。 3. 调用HashMap的put方法,将对象作为键(key)和值(value)同时放入HashMap中。 例如,下面是一个将字符串对象放入HashMap中的示例代码: HashMap<String, String> map = new HashMap<String, String>(); map.put("key1", "value1"); map.put("key2", "value2"); 在这个例子中,"key1"和"key2"是字符串对象,分别作为键(key)放入了HashMap中,而"value1"和"value2"分别作为值(value)放入了HashMap中。 需要注意的是,在Java中,HashMap是一种泛型类型,因此在创建HashMap对象时需要指定键(key)和值(value)的类型。在上面的例子中,我们指定了键(key)和值(value)都是字符串类型。 ### 回答2: 将一个对象放入HashMap的过程分为以下几个步骤: 1. 首先,计算该对象的哈希码。哈希码是通过对象的hashCode()方法获得的一个整型值,用于确定该对象在HashMap中的位置。 2. 根据哈希码计算对象在HashMap中的存放位置。HashMap内部会将所有的键值对存放在一个由数组实现的桶(bucket)中,因此需要根据哈希码计算对象在数组中的索引位置。 3. 在数组对应索引的位置上放置对象。如果该位置已经有其他对象存在,可能会发生哈希冲突。HashMap采用链表或红黑树等数据结构来解决冲突,将新的对象插入到链表或红黑树上。 4. 如果哈希冲突解决后,HashMap的size属性会自动加1,表示元素数量增加了。 需要注意的是,HashMap中的键值对是以键(Key)和值(Value)的形式存在的。在放置对象时,需要指定键和值的对应关系。HashMap通过键的哈希码来确定存取位置,并通过键的equals方法来判断键值对是否相同。 当要从HashMap获取对象时,也需要提供键,HashMap会根据键的哈希码找到对应的位置,并在该位置上查找与给定键相等的键值对。 总的来说,将一个对象放入HashMap中,首先通过哈希码确定对象在数组中的位置,然后解决哈希冲突并放置对象。这样就完成了将对象放入HashMap的过程。 ### 回答3: 将一个对象放入HashMap的过程如下: 1. 首先,创建一个HashMap对象。HashMapJava中的一个集合类,用于存储键值对。 2. 接下来,创建该对象的实例,并将其存储在一个变量中,例如: ``` HashMap<String, String> hashMap = new HashMap<>(); ``` 3. 创建一个要存储的对象,并为其分配内存空间。可以使用new关键字来创建对象,例如: ``` Object obj = new Object(); ``` 4. 调用HashMap对象的put()方法,将对象存储在HashMap中。put()方法接收两个参数:键和值。键是一个唯一标识对象的值,而值则是要存储的对象本身。例如: ``` hashMap.put("key1", obj); ``` 5. 在put()方法内部,HashMap会根据键的hashcode值和散列算法找到存储位置。每个键都会被映射到一个特定的桶,从而使得键和值能够以键值对的形式被存储在HashMap内部的桶中。 6. 如果该键已经存在于HashMap中,则新的对象将会替换掉该键对应的旧对象。 7. 最后,对象成功存储在HashMap中,并可以通过键值进行检索。可以使用get()方法,通过键来获取存储在HashMap中的对象。例如: ``` Object retrievedObj = hashMap.get("key1"); ``` 通过以上步骤,一个对象就成功地存储在HashMap中,并可以通过键进行检索和访问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IccBoY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值