Zacard's Notes

dubbo遇到的坑

背景

目前项目使用dubbo做服务化,同时开启dubbo的consumer端的参数校验。

遇到的坑一

第一个遇到的就是参数分组校验的bug了,这个其实网上很多人提过。

具体原因就是内部类使用_连接符代替实际应该使用的$。具体看如下代码(传送门):

1
2
3
4
5
6
7
8
9
public void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception {
String methodClassName = clazz.getName() + "_" + toUpperMethoName(methodName);
Class<?> methodClass = null;
try {
methodClass = Class.forName(methodClassName, false, Thread.currentThread().getContextClassLoader());
} catch (ClassNotFoundException e) {
}
// 以下代码略
}

这段代码的作用就是加载dubbo service中的分组校验group的内部类,再根据具体的group进行参数校验。但是内部类的连接符却用了_连接。按理说这个问题应该很容易被发现且很容易被测试出来,不应该出现在dubbo身上。

于是抱着怀疑的的心态查看该类的提交记录。果然,作者是故意这么改的(传送门):

JValidator在类名生成的类名有$,有frozen class异常

个人不是很理解这句话。。。且这么改留下了一个明显的bug。可能由于当时dubbo已经不再维护,作者也没有对此修改有进一步的说明。只好将此处的修改还原为使用$连接。实际测试与使用都没有出现异常,就此作罢。

遇到的坑二

随着系统的愈加复杂,出现了dubbo调用dubbo的情况。当前端web系统的controller调用dubboService1的时候,会在controller端进行参数校验,这个时候如果校验失败,能正常抛出错误,因为根本都还没有发起RPC调用。但是当dubboService1调用dubboService2的时候,也会在dubboService1端进行参数校验,这个时候如果校验失败,会抛出异常,dubboService1会试图将此异常序列化并传回controller端。如果此异常中包含不可系列化的属性,将会抛出无法序列化异常。这个就是dubbo默认的参数校验会存在的问题。例如以下错误:

1
2
java.lang.RuntimeException: Serialized class com.example.dubboService2_method1 must implement java.io.Serializable
Java field: private final java.lang.Object org.hibernate.validator.internal.engine.ConstraintViolationImpl.rootBean

解决办法:dubbo提供了自定义参数校验的扩展点,并且我们只要自定义一个可正常序列化的异常即可。例如以下代码:

DubboValidation.java:

1
2
3
4
5
6
7
8
9
10
public class DubboValidation extends JValidation {
public DubboValidation() {
}
protected Validator createValidator(URL url) {
return new DubboValidator(url);
}
}

DubboValidator.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DubboValidator extends JValidator {
public DubboValidator(URL url) {
super(url);
}
public void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception {
try{
super.validate(methodName, parameterTypes, arguments);
}catch (Exception e){
throw new MyBusinessException("DUBBO参数校验失败", e.getMessage());
}
}
}

MyBUsinessException.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class MyBusinessException extends RpcException {
public static final String DEFAULT_FAULT_CODE = "X0001";
private String xCode;
private String message;
public MyBusinessException(String message){
this(DEFAULT_FAULT_CODE, message);
}
public MyBusinessException(String xCode, String message) {
this(xCode, message, new Throwable());
}
public MyBusinessException(String xCode, String message, String internalMessage) {
this(xCode, message, internalMessage, null);
}
public MyBusinessException(String code, String message, Throwable throwable) {
this(code, message, throwable.getMessage(), throwable);
}
public MyBusinessException(String xCode, String message, String internalMessage, Throwable throwable) {
super(RpcException.BIZ_EXCEPTION, "[" + xCode + "] - " + message + internalMessage, throwable);
this.message = message;
this.xCode = xCode;
}
public String getXCode() {
return xCode;
}
public void setXCode(String xCode) {
this.xCode = xCode;
}
public String getMessageWithoutCode(){
return message;
}
@Override
public String getMessage() {
return "[" + xCode + "]" + " - " + message;
}
public void setMessage(String message) {
this.message = message;
}
}

同时需要在classpath:META-INF/下新建如下目录:dubbo/,并且新建一个文件com.alibaba.dubbo.validation.Validation,然后写入以下内容:

dubboValidation=com.xkeshi.webkits.dubbovalidation.DubboValidation

其实以上就是java spi的类加载方式。

遇到的坑三

dubbo provider服务提供者不要有方法重载!不然默认的dubbo协议说使用的Hessian序列化方式将有可能无法正确找到重载的方法!(客户端开启参数校验的时候)

坚持原创技术分享,您的支持将鼓励我继续创作!

热评文章