服务在调用外部接口时抛出异常,问题已经有人提出,新版本也已修复https://github.com/apache/incubator-dubbo/issues/712
下面我来一步步分析该问题出现的原因
2019-04-18 10:45:37,095 WARN [Thread-12] com.alibaba.dubbo.validation.support.jvalidation.JValidator:getMethodParameterBean:158 [DUBBO] .......provider.RiderBlackListProvider_RiderBlackListParameter_java.lang.String class is frozen, dubbo version: 2.5.6.2-D-RELEASE, current host: 192.168.96.162
java.lang.RuntimeException: .......provider.RiderBlackListProvider_RiderBlackListParameter_java.lang.String class is frozen
at javassist.CtClassType.checkModify(CtClassType.java:309) ~[javassist-3.21.0-GA.jar:?]
at javassist.CtField.setModifiers(CtField.java:240) ~[javassist-3.21.0-GA.jar:?]
at javassist.compiler.Javac.compileField(Javac.java:135) ~[javassist-3.21.0-GA.jar:?]
at javassist.compiler.Javac.compile(Javac.java:93) ~[javassist-3.21.0-GA.jar:?]
at javassist.CtField.make(CtField.java:162) ~[javassist-3.21.0-GA.jar:?]
at com.alibaba.dubbo.validation.support.jvalidation.JValidator.getMethodParameterBean(JValidator.java:145) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.validation.support.jvalidation.JValidator.validate(JValidator.java:242) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.validation.filter.ValidationFilter.invoke(ValidationFilter.java:49) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke(FutureFilter.java:54) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.filter.ConsumerContextFilter.invoke(ConsumerContextFilter.java:48) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at ....dubbo.filter.ConsumerTrackingFilter.invoke(ConsumerTrackingFilter.java:30) [dubbo-integration-0.0.2-20180420.072844-18.jar:0.0.2-SNAPSHOT]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.listener.ListenerInvokerWrapper.invoke(ListenerInvokerWrapper.java:74) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:77) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.invoke(AbstractClusterInvoker.java:229) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(MockClusterInvoker.java:72) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler.invoke(InvokerInvocationHandler.java:74) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.common.bytecode.proxy1.riderBlackList(proxy1.java) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.gallant.dispatch.RiderBlacklistFilterTest$1.run(RiderBlacklistFilterTest.java:35) [test-classes/:?]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]
2019-04-18 10:45:37,097 WARN [Thread-13] com.alibaba.dubbo.validation.support.jvalidation.JValidator:getMethodParameterBean:158 [DUBBO] by java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: ".../provider/RiderBlackListProvider_RiderBlackListParameter_java/lang/String", dubbo version: 2.5.6.2-D-RELEASE, current host: 192.168.96.162
javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: ".../provider/RiderBlackListProvider_RiderBlackListParameter_java/lang/String"
at javassist.ClassPool.toClass(ClassPool.java:1170) ~[javassist-3.21.0-GA.jar:?]
at javassist.ClassPool.toClass(ClassPool.java:1113) ~[javassist-3.21.0-GA.jar:?]
at javassist.ClassPool.toClass(ClassPool.java:1071) ~[javassist-3.21.0-GA.jar:?]
at javassist.CtClass.toClass(CtClass.java:1275) ~[javassist-3.21.0-GA.jar:?]
at com.alibaba.dubbo.validation.support.jvalidation.JValidator.getMethodParameterBean(JValidator.java:149) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.validation.support.jvalidation.JValidator.validate(JValidator.java:242) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.validation.filter.ValidationFilter.invoke(ValidationFilter.java:49) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke(FutureFilter.java:54) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.filter.ConsumerContextFilter.invoke(ConsumerContextFilter.java:48) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at ....dubbo.filter.ConsumerTrackingFilter.invoke(ConsumerTrackingFilter.java:30) [dubbo-integration-0.0.2-20180420.072844-18.jar:0.0.2-SNAPSHOT]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.listener.ListenerInvokerWrapper.invoke(ListenerInvokerWrapper.java:74) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:77) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.invoke(AbstractClusterInvoker.java:229) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(MockClusterInvoker.java:72) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler.invoke(InvokerInvocationHandler.java:74) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.common.bytecode.proxy1.riderBlackList(proxy1.java) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.gallant.dispatch.RiderBlacklistFilterTest$1.run(RiderBlacklistFilterTest.java:35) [test-classes/:?]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]
Caused by: java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: ".../provider/RiderBlackListProvider_RiderBlackListParameter_java/lang/String"
at java.lang.ClassLoader.defineClass1(Native Method) ~[?:1.8.0_131]
at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ~[?:1.8.0_131]
at java.lang.ClassLoader.defineClass(ClassLoader.java:642) ~[?:1.8.0_131]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_131]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_131]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_131]
at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_131]
at javassist.ClassPool.toClass2(ClassPool.java:1183) ~[javassist-3.21.0-GA.jar:?]
at javassist.ClassPool.toClass(ClassPool.java:1164) ~[javassist-3.21.0-GA.jar:?]
... 24 more
2019-04-18 10:45:37,090 WARN [Thread-14] com.alibaba.dubbo.validation.support.jvalidation.JValidator:getMethodParameterBean:158 [DUBBO] cannot add, dubbo version: 2.5.6.2-D-RELEASE, current host: 192.168.96.162
javassist.CannotCompileException: cannot add
at javassist.CtClassType.addField(CtClassType.java:1310) ~[javassist-3.21.0-GA.jar:?]
at javassist.CtClass.addField(CtClass.java:1100) ~[javassist-3.21.0-GA.jar:?]
at com.alibaba.dubbo.validation.support.jvalidation.JValidator.getMethodParameterBean(JValidator.java:147) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.validation.support.jvalidation.JValidator.validate(JValidator.java:242) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.validation.filter.ValidationFilter.invoke(ValidationFilter.java:49) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter.invoke(FutureFilter.java:54) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.filter.ConsumerContextFilter.invoke(ConsumerContextFilter.java:48) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at ....dubbo.filter.ConsumerTrackingFilter.invoke(ConsumerTrackingFilter.java:30) [dubbo-integration-0.0.2-20180420.072844-18.jar:0.0.2-SNAPSHOT]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:69) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.listener.ListenerInvokerWrapper.invoke(ListenerInvokerWrapper.java:74) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke(FailoverClusterInvoker.java:77) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.invoke(AbstractClusterInvoker.java:229) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(MockClusterInvoker.java:72) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler.invoke(InvokerInvocationHandler.java:74) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.alibaba.dubbo.common.bytecode.proxy1.riderBlackList(proxy1.java) [dubbo-2.5.6.2-D-RELEASE.jar:2.5.6.2-D-RELEASE]
at com.gallant.dispatch.RiderBlacklistFilterTest$1.run(RiderBlacklistFilterTest.java:35) [test-classes/:?]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]
老规矩,跟着堆栈看源码
查看javassist的ClassPool源码,frozen问题正是该处抛出,那么getCache获取到之后发现类处于冻结状态,所以抛出了异常
private static Object getMethodParameterBean(Class<?> clazz, Method method, Object[] args) {
...
try {
//获取class名称
String parameterClassName = generateMethodParameterClassName(clazz, method);
Class<?> parameterClass;
try {
//根据名称获取class,如果获取失败则创建并缓存
parameterClass = (Class<?>) Class.forName(parameterClassName, true, clazz.getClassLoader());
} catch (ClassNotFoundException e) {
ClassPool pool = ClassGenerator.getClassPool(clazz.getClassLoader());
CtClass ctClass = pool.makeClass(parameterClassName);
...创建,拼接class内容
//创建class字节码成功后并冻结
parameterClass = ctClass.toClass();
}
...
}
//创建class
public synchronized CtClass makeClass(String classname, CtClass superclass)
throws RuntimeException
{
checkNotFrozen(classname);
CtClass clazz = new CtNewClass(classname, this, false, superclass);
cacheCtClass(classname, clazz, true);
return clazz;
}
//如果已冻结则抛出异常
void checkNotFrozen(String classname) throws RuntimeException {
CtClass clazz = getCached(classname);
if (clazz == null) {
if (!childFirstLookup && parent != null) {
try {
clazz = parent.get0(classname, true);
}
catch (NotFoundException e) {}
if (clazz != null)
throw new RuntimeException(classname
+ " is in a parent ClassPool. Use the parent.");
}
}
else
if (clazz.isFrozen())
throw new RuntimeException(classname
+ ": frozen class (cannot edit)");
}
创建CtNewClass对象的wasFrozen状态是false状态,缓存class文件,在文件创建的时候如果再次发起请求进入校验模块,那么从缓存中获取的class对象校验其状态便一定会是冻结状态。当然,前提是反射获取类失败的情况
我们看到反射获取类时使用的是接口class的classloader。创建类是使用的ctClass.toClass(),其实就是当前线程的classloader。
那么如果当前线程的classloader与接口的classloader不匹配那么就会出现ClassNotFoundException异常导致再次重复创建。所以就出现了我们看到的异常。
public static ClassLoader getClassLoader(Class<?> cls) {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
} catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back to system class loader...
}
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = cls.getClassLoader();
}
return cl;
}
public Class toClass(CtClass clazz) throws CannotCompileException {
// Some subclasses of ClassPool may override toClass(CtClass,ClassLoader).
// So we should call that method instead of toClass(.., ProtectionDomain).
return toClass(clazz, getClassLoader());
}
public ClassLoader getClassLoader() {
return getContextClassLoader();
}
static ClassLoader getContextClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
/**
* 问题复现
* @author 会灰翔的灰机
* @date 2019/4/18
*/
public class RiderBlacklistFilterTest extends UnitTestBase {
@Resource
private RiderBlackListProvider riderBlackListProvider;
@Test
public void riderBlackList(){
int size = 30;
CyclicBarrier cb = new CyclicBarrier(size);
for (int i=0;i<size;i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
cb.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(this + ":" + System.currentTimeMillis());
riderBlackListProvider.riderBlackList("123");
}
}).start();
}
}
}