暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

记一次Dubbo RpcException反序列化失败的解决过程

编程阁楼 2020-08-03
853

近期发现线上的一个dubbo网关类应用在解析后端接口RpcException时出现了反序列化失败问题,小编花了一些心思最终解决了该问题,同时小编也认为该问题比较具有代表性,有必要拿出来和大家一起分享下解决过程。



1. 问题描述

后端接口异常信息如下:

{
"@type": "com.alibaba.dubbo.rpc.RpcException",
"biz": false,
"cause": {
"@type": "com.alibaba.dubbo.remoting.RemotingException",
"localAddress": {
"address": "172.31.6.206",
"port": 63608
},
"localizedMessage": "java.lang.NullPointerException\njava.lang.NullPointerException\n\tat com.alibaba.dubbo.common.utils.PojoUtils.realize0(PojoUtils.java:368)\n\tat com.alibaba.dubbo.common.utils.PojoUtils.realize(PojoUtils.java:204)\n\tat com.alibaba.dubbo.common.utils.PojoUtils.realize(PojoUtils.java:90)\n\tat com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:65)\n\tat com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)\n\tat com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)\n\tat com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)\n\tat com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38)\n\tat com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)\n\tat com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:108)\n\tat com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:84)\n\tat com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170)\n\tat com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52)\n\tat com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:82)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\n\tat java.lang.Thread.run(Thread.java:748)\n",
"message": "java.lang.NullPointerException\njava.lang.NullPointerException\n\tat com.alibaba.dubbo.common.utils.PojoUtils.realize0(PojoUtils.java:368)\n\tat com.alibaba.dubbo.common.utils.PojoUtils.realize(PojoUtils.java:204)\n\tat com.alibaba.dubbo.common.utils.PojoUtils.realize(PojoUtils.java:90)\n\tat com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:65)\n\tat com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)\n\tat com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)\n\tat com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)\n\tat com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38)\n\tat com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)\n\tat com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:108)\n\tat com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:84)\n\tat com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170)\n\tat com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52)\n\tat com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:82)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\n\tat java.lang.Thread.run(Thread.java:748)\n",
"remoteAddress": {
"address": "10.31.31.181",
"port": 48242
},
"stackTrace": [
{
"className": "com.alibaba.dubbo.remoting.exchange.support.DefaultFuture",
"fileName": "DefaultFuture.java",
"lineNumber": 245,
"methodName": "returnFromResponse",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.remoting.exchange.support.DefaultFuture",
"fileName": "DefaultFuture.java",
"lineNumber": 162,
"methodName": "get",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.remoting.exchange.support.DefaultFuture",
"fileName": "DefaultFuture.java",
"lineNumber": 135,
"methodName": "get",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker",
"fileName": "DubboInvoker.java",
"lineNumber": 95,
"methodName": "doInvoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.protocol.AbstractInvoker",
"fileName": "AbstractInvoker.java",
"lineNumber": 155,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.filter.GenericImplFilter",
"fileName": "GenericImplFilter.java",
"lineNumber": 172,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1",
"fileName": "ProtocolFilterWrapper.java",
"lineNumber": 72,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.csp.sentinel.adapter.dubbo.SentinelDubboConsumerFilter",
"fileName": "SentinelDubboConsumerFilter.java",
"lineNumber": 60,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1",
"fileName": "ProtocolFilterWrapper.java",
"lineNumber": 72,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "cn.gov.zcy.zeye.dubbo.client.EagleyeDubboFilter",
"fileName": "EagleyeDubboFilter.java",
"lineNumber": 162,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1",
"fileName": "ProtocolFilterWrapper.java",
"lineNumber": 72,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.monitor.support.MonitorFilter",
"fileName": "MonitorFilter.java",
"lineNumber": 75,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1",
"fileName": "ProtocolFilterWrapper.java",
"lineNumber": 72,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.csp.sentinel.adapter.dubbo.DubboAppContextFilter",
"fileName": "DubboAppContextFilter.java",
"lineNumber": 41,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1",
"fileName": "ProtocolFilterWrapper.java",
"lineNumber": 72,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter",
"fileName": "FutureFilter.java",
"lineNumber": 54,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1",
"fileName": "ProtocolFilterWrapper.java",
"lineNumber": 72,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.filter.ConsumerContextFilter",
"fileName": "ConsumerContextFilter.java",
"lineNumber": 49,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1",
"fileName": "ProtocolFilterWrapper.java",
"lineNumber": 72,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.listener.ListenerInvokerWrapper",
"fileName": "ListenerInvokerWrapper.java",
"lineNumber": 77,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.protocol.InvokerWrapper",
"fileName": "InvokerWrapper.java",
"lineNumber": 56,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker",
"fileName": "FailoverClusterInvoker.java",
"lineNumber": 78,
"methodName": "doInvoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker",
"fileName": "AbstractClusterInvoker.java",
"lineNumber": 244,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker",
"fileName": "MockClusterInvoker.java",
"lineNumber": 75,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler",
"fileName": "InvokerInvocationHandler.java",
"lineNumber": 52,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.common.bytecode.proxy0",
"fileName": "proxy0.java",
"lineNumber": -1,
"methodName": "$invoke",
"nativeMethod": false
},
{
"className": "org.apache.dubbo.proxy.service.GenericInvoke",
"fileName": "GenericInvoke.java",
"lineNumber": 58,
"methodName": "genericCall",
"nativeMethod": false
},
{
"className": "org.apache.dubbo.proxy.worker.RequestWorker",
"fileName": "RequestWorker.java",
"lineNumber": 85,
"methodName": "run",
"nativeMethod": false
},
{
"className": "java.util.concurrent.ThreadPoolExecutor",
"fileName": "ThreadPoolExecutor.java",
"lineNumber": 1142,
"methodName": "runWorker",
"nativeMethod": false
},
{
"className": "java.util.concurrent.ThreadPoolExecutor$Worker",
"fileName": "ThreadPoolExecutor.java",
"lineNumber": 617,
"methodName": "run",
"nativeMethod": false
},
{
"className": "java.lang.Thread",
"fileName": "Thread.java",
"lineNumber": 745,
"methodName": "run",
"nativeMethod": false
}
]
},
"code": 0,
"forbidded": false,
"localizedMessage": "Failed to invoke the method $invoke in the service com.alibaba.dubbo.rpc.service.GenericService. Tried 3 times of the providers [10.31.31.181:48242, 10.31.16.150:31936] (2/2) from the registry 172.31.2.166:1500 on the consumer 172.31.6.206 using the dubbo version 2.6.5. Last error is: Failed to invoke remote method: $invoke, provider: dubbo://10.31.31.181:48242/cn.gov.zcy.service.brand.service.BrandReadService?anyhost=true&application=dubbo-proxy&check=false&dubbo=2.5.3&generic=true&heartbeat=10000&interface=cn.gov.zcy.service.brand.service.BrandReadService&logger=slf4j&methods=findByFullNameList,pagination,getBrands,paginationByAdmin,findByCategoryCodeSet,findSingleBrand,findBrandList,paginationWithInstitution,getChangeDetail,queryBrandAuditLog,findListByName,findById,findByIds,pageQueryAudit,findByName,getFinalBrandDetail,findByCategoryCode,exist,findByCategoryCodeAndName,getAllBrand,filterBrand,findByFullName,findByNamePrefix,findCachedByName&pid=10978&register.ip=172.31.6.206&remote.timestamp=1596185263797&retries=2&revision=1.0.0&service.filter=dubboFilter&side=consumer&timeout=10000&timestamp=1594202613252&version=1.0.0, cause: java.lang.NullPointerException\njava.lang.NullPointerException\n\tat com.alibaba.dubbo.common.utils.PojoUtils.realize0(PojoUtils.java:368)\n\tat com.alibaba.dubbo.common.utils.PojoUtils.realize(PojoUtils.java:204)\n\tat com.alibaba.dubbo.common.utils.PojoUtils.realize(PojoUtils.java:90)\n\tat com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:65)\n\tat com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)\n\tat com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)\n\tat com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)\n\tat com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38)\n\tat com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)\n\tat com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:108)\n\tat com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:84)\n\tat com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170)\n\tat com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52)\n\tat com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:82)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\n\tat java.lang.Thread.run(Thread.java:748)\n",
"message": "Failed to invoke the method $invoke in the service com.alibaba.dubbo.rpc.service.GenericService. Tried 3 times of the providers [10.31.31.181:48242, 10.31.16.150:31936] (2/2) from the registry 172.31.2.166:1500 on the consumer 172.31.6.206 using the dubbo version 2.6.5. Last error is: Failed to invoke remote method: $invoke, provider: dubbo://10.31.31.181:48242/cn.gov.zcy.service.brand.service.BrandReadService?anyhost=true&application=dubbo-proxy&check=false&dubbo=2.5.3&generic=true&heartbeat=10000&interface=cn.gov.zcy.service.brand.service.BrandReadService&logger=slf4j&methods=findByFullNameList,pagination,getBrands,paginationByAdmin,findByCategoryCodeSet,findSingleBrand,findBrandList,paginationWithInstitution,getChangeDetail,queryBrandAuditLog,findListByName,findById,findByIds,pageQueryAudit,findByName,getFinalBrandDetail,findByCategoryCode,exist,findByCategoryCodeAndName,getAllBrand,filterBrand,findByFullName,findByNamePrefix,findCachedByName&pid=10978&register.ip=172.31.6.206&remote.timestamp=1596185263797&retries=2&revision=1.0.0&service.filter=dubboFilter&side=consumer&timeout=10000&timestamp=1594202613252&version=1.0.0, cause: java.lang.NullPointerException\njava.lang.NullPointerException\n\tat com.alibaba.dubbo.common.utils.PojoUtils.realize0(PojoUtils.java:368)\n\tat com.alibaba.dubbo.common.utils.PojoUtils.realize(PojoUtils.java:204)\n\tat com.alibaba.dubbo.common.utils.PojoUtils.realize(PojoUtils.java:90)\n\tat com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:65)\n\tat com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)\n\tat com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)\n\tat com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)\n\tat com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38)\n\tat com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)\n\tat com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:108)\n\tat com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:84)\n\tat com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170)\n\tat com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52)\n\tat com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:82)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\n\tat java.lang.Thread.run(Thread.java:748)\n",
"network": true,
"serialization": false,
"stackTrace": [
{
"className": "com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker",
"fileName": "FailoverClusterInvoker.java",
"lineNumber": 109,
"methodName": "doInvoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker",
"fileName": "AbstractClusterInvoker.java",
"lineNumber": 244,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker",
"fileName": "MockClusterInvoker.java",
"lineNumber": 75,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler",
"fileName": "InvokerInvocationHandler.java",
"lineNumber": 52,
"methodName": "invoke",
"nativeMethod": false
},
{
"className": "com.alibaba.dubbo.common.bytecode.proxy0",
"fileName": "proxy0.java",
"lineNumber": -1,
"methodName": "$invoke",
"nativeMethod": false
},
{
"className": "org.apache.dubbo.proxy.service.GenericInvoke",
"fileName": "GenericInvoke.java",
"lineNumber": 58,
"methodName": "genericCall",
"nativeMethod": false
},
{
"className": "org.apache.dubbo.proxy.worker.RequestWorker",
"fileName": "RequestWorker.java",
"lineNumber": 85,
"methodName": "run",
"nativeMethod": false
},
{
"className": "java.util.concurrent.ThreadPoolExecutor",
"fileName": "ThreadPoolExecutor.java",
"lineNumber": 1142,
"methodName": "runWorker",
"nativeMethod": false
},
{
"className": "java.util.concurrent.ThreadPoolExecutor$Worker",
"fileName": "ThreadPoolExecutor.java",
"lineNumber": 617,
"methodName": "run",
"nativeMethod": false
},
{
"className": "java.lang.Thread",
"fileName": "Thread.java",
"lineNumber": 745,
"methodName": "run",
"nativeMethod": false
}
],
"timeout": false
}

仔细看异常信息,实际上真实的异常信息应该是"java.lang.NullPointerException\njava.lang.NullPointerException",但是经过dubbo框架层层包装成了RpcException。


当我们拿到以上异常信息的字符串后,应该如何转换为RpcException,使用Fastjson库代码如下:

RpcException exception = JSON.parseObject(result.getBytes(), RpcException.class, Feature.IgnoreNotMatch);
System.out.println(exception);

代码非常的简单,但是很遗憾,以上代码会报错:

Exception in thread "main" com.alibaba.fastjson.JSONException: set property error, com.alibaba.dubbo.remoting.RemotingException#localAddress
at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:183)
at com.alibaba.fastjson.parser.deserializer.ThrowableDeserializer.deserialze(ThrowableDeserializer.java:149)
at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:688)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:396)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:300)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:573)
at cn.gov.zcy.dubbotest.controller.TestController.main(TestController.java:520)
Caused by: java.lang.IllegalArgumentException: Can not set java.net.InetSocketAddress field com.alibaba.dubbo.remoting.RemotingException.localAddress to java.lang.Exception
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:75)
at java.lang.reflect.Field.set(Field.java:764)
at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:178)
... 6 more

异常信息明确提示了"Can not set java.net.InetSocketAddress field com.alibaba.dubbo.remoting.RemotingException.localAddress to java.lang.Exception"。


2. 排查过程

接口异常信息中@type字段明确指明了该以上为RpcException,这里为什么还会报上述异常呢?带着这个问题,我们来看看com.alibaba.dubbo.remoting.RemotingException.localAddress属性到底是如何定义的呢。

public class RemotingException extends Exception {


private static final long serialVersionUID = -3160452149606778709L;


private InetSocketAddress localAddress;


private InetSocketAddress remoteAddress;

    // ...此处省略1w行代码...
}


RemotingException的属性localAddress和remoteAddress均为InetSocketAddress类型。而InetSocketAddress是jdk 1.4就已经原生提供的类了。

/**
*
* This class implements an IP Socket Address (IP address + port number)
* It can also be a pair (hostname + port number), in which case an attempt
* will be made to resolve the hostname. If resolution fails then the address
* is said to be <I>unresolved</I> but can still be used on some circumstances
* like connecting through a proxy.
* <p>
* It provides an immutable object used by sockets for binding, connecting, or
* as returned values.
* <p>
* The <i>wildcard</i> is a special local IP address. It usually means "any"
* and can only be used for {@code bind} operations.
*
* @see java.net.Socket
* @see java.net.ServerSocket
* @since 1.4
*/
public class InetSocketAddress
extends SocketAddress
{
// Private implementation class pointed to by all public methods.
private static class InetSocketAddressHolder {
// The hostname of the Socket Address
private String hostname;
// The IP address of the Socket Address
private InetAddress addr;
// The port number of the Socket Address
private int port;


private InetSocketAddressHolder(String hostname, InetAddress addr, int port) {
this.hostname = hostname;
this.addr = addr;
this.port = port;
}


private int getPort() {
return port;
}


private InetAddress getAddress() {
return addr;
}


private String getHostName() {
if (hostname != null)
return hostname;
if (addr != null)
return addr.getHostName();
return null;
}


private String getHostString() {
if (hostname != null)
return hostname;
if (addr != null) {
if (addr.holder().getHostName() != null)
return addr.holder().getHostName();
else
return addr.getHostAddress();
}
return null;
}


private boolean isUnresolved() {
return addr == null;
}


@Override
public String toString() {
if (isUnresolved()) {
return hostname + ":" + port;
} else {
return addr.toString() + ":" + port;
}
}


@Override
public final boolean equals(Object obj) {
if (obj == null || !(obj instanceof InetSocketAddressHolder))
return false;
InetSocketAddressHolder that = (InetSocketAddressHolder)obj;
boolean sameIP;
if (addr != null)
sameIP = addr.equals(that.addr);
else if (hostname != null)
sameIP = (that.addr == null) &&
hostname.equalsIgnoreCase(that.hostname);
else
sameIP = (that.addr == null) && (that.hostname == null);
return sameIP && (port == that.port);
}


@Override
public final int hashCode() {
if (addr != null)
return addr.hashCode() + port;
if (hostname != null)
return hostname.toLowerCase().hashCode() + port;
return port;
}
}


private final transient InetSocketAddressHolder holder;


private static final long serialVersionUID = 5076001401234631237L;


private static int checkPort(int port) {
if (port < 0 || port > 0xFFFF)
throw new IllegalArgumentException("port out of range:" + port);
return port;
}


private static String checkHost(String hostname) {
if (hostname == null)
throw new IllegalArgumentException("hostname can't be null");
return hostname;
}


/**
* Creates a socket address where the IP address is the wildcard address
* and the port number a specified value.
* <p>
* A valid port value is between 0 and 65535.
* A port number of {@code zero} will let the system pick up an
* ephemeral port in a {@code bind} operation.
* <p>
* @param port The port number
* @throws IllegalArgumentException if the port parameter is outside the specified
* range of valid port values.
*/
public InetSocketAddress(int port) {
this(InetAddress.anyLocalAddress(), port);
}


/**
*
* Creates a socket address from an IP address and a port number.
* <p>
* A valid port value is between 0 and 65535.
* A port number of {@code zero} will let the system pick up an
* ephemeral port in a {@code bind} operation.
* <P>
* A {@code null} address will assign the <i>wildcard</i> address.
* <p>
* @param addr The IP address
* @param port The port number
* @throws IllegalArgumentException if the port parameter is outside the specified
* range of valid port values.
*/
public InetSocketAddress(InetAddress addr, int port) {
holder = new InetSocketAddressHolder(
null,
addr == null ? InetAddress.anyLocalAddress() : addr,
checkPort(port));
}


/**
*
* Creates a socket address from a hostname and a port number.
* <p>
* An attempt will be made to resolve the hostname into an InetAddress.
* If that attempt fails, the address will be flagged as <I>unresolved</I>.
* <p>
* If there is a security manager, its {@code checkConnect} method
* is called with the host name as its argument to check the permission
* to resolve it. This could result in a SecurityException.
* <P>
* A valid port value is between 0 and 65535.
* A port number of {@code zero} will let the system pick up an
* ephemeral port in a {@code bind} operation.
* <P>
* @param hostname the Host name
* @param port The port number
* @throws IllegalArgumentException if the port parameter is outside the range
* of valid port values, or if the hostname parameter is <TT>null</TT>.
* @throws SecurityException if a security manager is present and
* permission to resolve the host name is
* denied.
* @see #isUnresolved()
*/
public InetSocketAddress(String hostname, int port) {
checkHost(hostname);
InetAddress addr = null;
String host = null;
try {
addr = InetAddress.getByName(hostname);
} catch(UnknownHostException e) {
host = hostname;
}
holder = new InetSocketAddressHolder(host, addr, checkPort(port));
}


// private constructor for creating unresolved instances
private InetSocketAddress(int port, String hostname) {
holder = new InetSocketAddressHolder(hostname, null, port);
}


/**
*
* Creates an unresolved socket address from a hostname and a port number.
* <p>
* No attempt will be made to resolve the hostname into an InetAddress.
* The address will be flagged as <I>unresolved</I>.
* <p>
* A valid port value is between 0 and 65535.
* A port number of {@code zero} will let the system pick up an
* ephemeral port in a {@code bind} operation.
* <P>
* @param host the Host name
* @param port The port number
* @throws IllegalArgumentException if the port parameter is outside
* the range of valid port values, or if the hostname
* parameter is <TT>null</TT>.
* @see #isUnresolved()
* @return a {@code InetSocketAddress} representing the unresolved
* socket address
* @since 1.5
*/
public static InetSocketAddress createUnresolved(String host, int port) {
return new InetSocketAddress(checkPort(port), checkHost(host));
}


/**
* @serialField hostname String
* @serialField addr InetAddress
* @serialField port int
*/
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("hostname", String.class),
new ObjectStreamField("addr", InetAddress.class),
new ObjectStreamField("port", int.class)};


private void writeObject(ObjectOutputStream out)
throws IOException
{
// Don't call defaultWriteObject()
ObjectOutputStream.PutField pfields = out.putFields();
pfields.put("hostname", holder.hostname);
pfields.put("addr", holder.addr);
pfields.put("port", holder.port);
out.writeFields();
}


private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
// Don't call defaultReadObject()
ObjectInputStream.GetField oisFields = in.readFields();
final String oisHostname = (String)oisFields.get("hostname", null);
final InetAddress oisAddr = (InetAddress)oisFields.get("addr", null);
final int oisPort = oisFields.get("port", -1);


// Check that our invariants are satisfied
checkPort(oisPort);
if (oisHostname == null && oisAddr == null)
throw new InvalidObjectException("hostname and addr " +
"can't both be null");


InetSocketAddressHolder h = new InetSocketAddressHolder(oisHostname,
oisAddr,
oisPort);
UNSAFE.putObject(this, FIELDS_OFFSET, h);
}


private void readObjectNoData()
throws ObjectStreamException
{
throw new InvalidObjectException("Stream data required");
}


private static final long FIELDS_OFFSET;
private static final sun.misc.Unsafe UNSAFE;
static {
try {
sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
FIELDS_OFFSET = unsafe.objectFieldOffset(
InetSocketAddress.class.getDeclaredField("holder"));
UNSAFE = unsafe;
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}


/**
* Gets the port number.
*
* @return the port number.
*/
public final int getPort() {
return holder.getPort();
}


/**
*
* Gets the {@code InetAddress}.
*
* @return the InetAdress or {@code null} if it is unresolved.
*/
public final InetAddress getAddress() {
return holder.getAddress();
}


/**
* Gets the {@code hostname}.
* Note: This method may trigger a name service reverse lookup if the
* address was created with a literal IP address.
*
* @return the hostname part of the address.
*/
public final String getHostName() {
return holder.getHostName();
}


/**
* Returns the hostname, or the String form of the address if it
* doesn't have a hostname (it was created using a literal).
* This has the benefit of <b>not</b> attempting a reverse lookup.
*
* @return the hostname, or String representation of the address.
* @since 1.7
*/
public final String getHostString() {
return holder.getHostString();
}


/**
* Checks whether the address has been resolved or not.
*
* @return {@code true} if the hostname couldn't be resolved into
* an {@code InetAddress}.
*/
public final boolean isUnresolved() {
return holder.isUnresolved();
}


/**
* Constructs a string representation of this InetSocketAddress.
* This String is constructed by calling toString() on the InetAddress
* and concatenating the port number (with a colon). If the address
* is unresolved then the part before the colon will only contain the hostname.
*
* @return a string representation of this object.
*/
@Override
public String toString() {
return holder.toString();
}


/**
* Compares this object against the specified object.
* The result is {@code true} if and only if the argument is
* not {@code null} and it represents the same address as
* this object.
* <p>
* Two instances of {@code InetSocketAddress} represent the same
* address if both the InetAddresses (or hostnames if it is unresolved) and port
* numbers are equal.
* If both addresses are unresolved, then the hostname and the port number
* are compared.
*
* Note: Hostnames are case insensitive. e.g. "FooBar" and "foobar" are
* considered equal.
*
* @param obj the object to compare against.
* @return {@code true} if the objects are the same;
* {@code false} otherwise.
* @see java.net.InetAddress#equals(java.lang.Object)
*/
@Override
public final boolean equals(Object obj) {
if (obj == null || !(obj instanceof InetSocketAddress))
return false;
return holder.equals(((InetSocketAddress) obj).holder);
}


/**
* Returns a hashcode for this socket address.
*
* @return a hash code value for this socket address.
*/
@Override
public final int hashCode() {
return holder.hashCode();
}
}

仔细阅读InetSocketAddress的代码,我们发现其address和port属性并无set方法。其对象实例是通过constructor传参的方式直接生成的。


小编写了一段测试代码:

RemotingException e = new RemotingException(new InetSocketAddress("127.0.0.1", 11111), new InetSocketAddress("127.0.0.1", 22222), "xxxxx");
String s = JSON.toJSONString(e);
System.out.println(s);
System.out.println("================");
RemotingException e1 = JSON.parseObject(s, RemotingException.class);
System.out.println(e1);

可以看到,序列化没问题,但是反序列化时输出了跟RpcException反序列化一样的错误信息

{"@type":"com.alibaba.dubbo.remoting.RemotingException","localAddress":{"address":"127.0.0.1","port":11111},"localizedMessage":"xxxxx","message":"xxxxx","remoteAddress":{"address":"127.0.0.1","port":22222},"stackTrace":[{"className":"cn.gov.zcy.dubbotest.controller.TestController","fileName":"TestController.java","lineNumber":516,"methodName":"main","nativeMethod":false}]}
================
Exception in thread "main" com.alibaba.fastjson.JSONException: set property error, com.alibaba.dubbo.remoting.RemotingException#localAddress
at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:183)
at com.alibaba.fastjson.parser.deserializer.ThrowableDeserializer.deserialze(ThrowableDeserializer.java:149)
at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:688)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:396)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:300)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:573)
at cn.gov.zcy.dubbotest.controller.TestController.main(TestController.java:520)
Caused by: java.lang.IllegalArgumentException: Can not set java.net.InetSocketAddress field com.alibaba.dubbo.remoting.RemotingException.localAddress to java.lang.Exception
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:75)
at java.lang.reflect.Field.set(Field.java:764)
at com.alibaba.fastjson.parser.deserializer.FieldDeserializer.setValue(FieldDeserializer.java:178)
... 6 more

3. 继续探究解决方法

实际上InetSocketAddress和InetAddress都不能用作Java Bean,也不能进行字符串反序列化。这就是今天这个问题的本质了。那有什么办法可以避免这个问题呢?

第一个想到思路的就是fastjson的反序列化是忽略转换失败的字段,但是很遗憾,目前fastjson并不支持,具体信息小伙伴可以看github issue说明:https://github.com/alibaba/fastjson/issues/854


第二个想到的思路是忽略不匹配的字段,及当json key找不到映射关系时不进行转换。

RpcException exception = JSON.parseObject(result.getBytes(), RpcException.class, Feature.IgnoreNotMatch);
System.out.println(exception);
这里的"Feature.IgnoreNotMatch"就是做忽略不匹配字段使用的。

但是,实际上RpcException的json key是匹配的,怎么进行忽略呢?小编的解决方法很简单,直接使用字符串替换将localAddress替换为localAddress0。同理,将remoteAddress替换为remoteAddress0,人为制造了字段不匹配。


4. 反思

当然了,小编今天讲到的解决方法也只是小编不得已而为之,至于dubbo框架为什么在RemotingException类中使用InetSocketAddress小编暂时还不得而知。也许在将来小编会找到更优的解决方法,到时小编再拿出来分享给小伙伴们。




文章转载自编程阁楼,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论