Dubbo之泛化调用
dubbo版本
- dubbo版本2.6.7
泛化调用
-
泛化调用就是服务消费者端因为某种原因并没有该服务接口,此时通过
GenericService
来invoke
需要调用的服务方法- 比如其他语言调用java的接口
consumer
没有provider
所定义的接口
-
泛接口调用方式主要用于客户端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的服务测试框架或者Mock框架等,可通过GenericService调用所有服务实现。
public interface GenericService { //$invoke的三个参数分别为,方法名,方法参数类型数组,方法参数数组 Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException; }
-
Dubbo泛化有三种不同的序列化方式
- generic = true:对参数使用 PojoUtils 进行序列化
- generic = bean : 对参数使用 JavaBean 方式进行序列化
- generic = nativejava:对参数使用 nativejava 方式进行序列化
Dubbo2.6.x案例
-
接口
public interface DemoService { String sayHello(String name); SayResponseDTO sayHello(SayRequestDTO sayRequestDTO); }
-
consumer配置
generic="true"
<dubbo:reference id="demoService" check="false" interface="cn.jannal.dubbo.facade.DemoService" group="group0" generic="true" version="1.0.0"/>
-
调用代码
GenericService genericService = (GenericService) context.getBean("demoService"); Object result = genericService.$invoke( "sayHello", new String[]{"java.lang.String"}, new Object[]{"World"}); System.out.println(result); Map<String, Object> paramsMap = new HashMap<String, Object>(); paramsMap.put("username", "jannal"); //如果参数时POJO,则使用map即可,如果返回的是POJO,如果返回POJO将自动转成Map //{result=Hello jannal, response from provider: 10.0.75.1:20882, class=cn.jannal.dubbo.facade.dto.SayResponseDTO} Object result2 = genericService.$invoke( "sayHello", new String[]{"cn.jannal.dubbo.facade.dto.SayRequestDTO"}, new Object[]{paramsMap}); System.out.println("返回:" + result2);
Dubbo2.7.x新特性
-
2.7.12 之后, 对于Dubbo泛化调用,提供一种新的方式:直接传递字符串来完成一次调用。即用户可以直接传递参数对象的json字符串来完成一次Dubbo泛化调用
//设置generic为gson (这个是必须的,否则会报java.lang.String cannot be cast to cn.jannal.dubbo.facade.dto.SayRequestDTO) RpcContext.getContext().setAttachment("generic", "gson"); Object result3 = genericService.$invoke( "sayHello", new String[]{SayRequestDTO.class.getName()}, new Object[]{"{'username':'jannal'}"}); //{result=Version:V1,Hello jannal, response from provider: 172.16.117.33:20882, class=cn.jannal.dubbo.facade.dto.SayResponseDTO} log.info("返回结果:{}", result3);
-
对 Google Protobuf 对象进行泛化调用
<dubbo:reference id="demoService" check="true" interface="cn.jannal.dubbo.facade.DemoService" generic="protobuf-json" protocol="dubbo"/> GenericService genericService = (GenericService) context.getBean("demoService"); //protobuf的泛化调用只允许传递一个类型为String的json对象来代表请求参数 Map<String, Object> helloRequest = new HashMap<>(); helloRequest.put("name", "jannal"); genericService.$invoke("sayHello", new String[]{HelloRequest.class.getCanonicalName()}, new Object[]{JSONObject.toJSONString(helloRequest)}); context.stop();
源码解析
-
基于dubbo2.6.7版本
-
Dubbo的泛化调用和泛化实现依赖于两个过滤器来完成
- Consumer端:GenericImplFilter完成了消费者端的泛化功能
- Provider端:GenericFilter完成了提供者端的泛化功能
-
GenericImplFilter:当消费者进行调用的是泛化实现时,会将参数信息按照指定的序列化方式进行序列化后进行泛化调用。当消费者进行泛化调用时,会将参数信息进行序列化后进行泛化调用。
/** * GenericImplInvokerFilter * 1. 用于consumer实现泛化调用,实现序列化的检查和处理 */ @Activate(group = Constants.CONSUMER, value = Constants.GENERIC_KEY, order = 20000) public class GenericImplFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(GenericImplFilter.class); //泛化调用参数类型 private static final Class<?>[] GENERIC_PARAMETER_TYPES = new Class<?>[]{String.class, String[].class, Object[].class}; @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { String generic = invoker.getUrl().getParameter(Constants.GENERIC_KEY); if (ProtocolUtils.isGeneric(generic) && !Constants.$INVOKE.equals(invocation.getMethodName()) && invocation instanceof RpcInvocation) { //获取泛化调用的参数 :调用方法名、调用参数类型、调用参数值等 RpcInvocation invocation2 = (RpcInvocation) invocation; String methodName = invocation2.getMethodName(); Class<?>[] parameterTypes = invocation2.getParameterTypes(); Object[] arguments = invocation2.getArguments(); String[] types = new String[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { types[i] = ReflectUtils.getName(parameterTypes[i]); } Object[] args; // 判断序列化方式,进行序列化。如果是 Bean 序列化方式,则使用JavaBeanSerializeUtil 进行序列化 if (ProtocolUtils.isBeanGenericSerialization(generic)) { args = new Object[arguments.length]; for (int i = 0; i < arguments.length; i++) { args[i] = JavaBeanSerializeUtil.serialize(arguments[i], JavaBeanAccessor.METHOD); } } else { // 否则(generic = true || nativejava) 使用PojoUtils 进行序列化 args = PojoUtils.generalize(arguments); } // 设置调用方法为 $invoke、参数类型为GENERIC_PARAMETER_TYPES,并设置参数具体值。 // 目的是为了让 GenericFilter 能识别出这次调用是泛化调用。 invocation2.setMethodName(Constants.$INVOKE); invocation2.setParameterTypes(GENERIC_PARAMETER_TYPES); invocation2.setArguments(new Object[]{methodName, types, args}); // 进行泛化调用 Result result = invoker.invoke(invocation2); // 如果泛化调用没有异常, 则将结果集反序列化后返回。 if (!result.hasException()) { Object value = result.getValue(); try { Method method = invoker.getInterface().getMethod(methodName, parameterTypes); // 对结果进行反序列化 if (ProtocolUtils.isBeanGenericSerialization(generic)) { if (value == null) { return new RpcResult(value); } else if (value instanceof JavaBeanDescriptor) { return new RpcResult(JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) value)); } else { throw new RpcException( "The type of result value is " + value.getClass().getName() + " other than " + JavaBeanDescriptor.class.getName() + ", and the result is " + value); } } else { return new RpcResult(PojoUtils.realize(value, method.getReturnType(), method.getGenericReturnType())); } } catch (NoSuchMethodException e) { throw new RpcException(e.getMessage(), e); } } else if (result.getException() instanceof GenericException) { // 返回异常是 GenericException 类型,则说明是泛化异常而非调用过程中异常。进行处理 GenericException exception = (GenericException) result.getException(); try { String className = exception.getExceptionClass(); Class<?> clazz = ReflectUtils.forName(className); Throwable targetException = null; Throwable lastException = null; try { targetException = (Throwable) clazz.newInstance(); } catch (Throwable e) { lastException = e; for (Constructor<?> constructor : clazz.getConstructors()) { try { targetException = (Throwable) constructor.newInstance(new Object[constructor.getParameterTypes().length]); break; } catch (Throwable e1) { lastException = e1; } } } if (targetException != null) { try { Field field = Throwable.class.getDeclaredField("detailMessage"); if (!field.isAccessible()) { field.setAccessible(true); } field.set(targetException, exception.getExceptionMessage()); } catch (Throwable e) { logger.warn(e.getMessage(), e); } result = new RpcResult(targetException); } else if (lastException != null) { throw lastException; } } catch (Throwable e) { throw new RpcException("Can not deserialize exception " + exception.getExceptionClass() + ", message: " + exception.getExceptionMessage(), e); } } return result; } // 判断消费者是否开启了泛化调用,调用方法名为 $invoke && invocation参数有三个 && generic 参数满足三种泛化方式之一 if (invocation.getMethodName().equals(Constants.$INVOKE) && invocation.getArguments() != null && invocation.getArguments().length == 3 && ProtocolUtils.isGeneric(generic)) { Object[] args = (Object[]) invocation.getArguments()[2]; if (ProtocolUtils.isJavaGenericSerialization(generic)) { for (Object arg : args) { if (!(byte[].class == arg.getClass())) { error(generic, byte[].class.getName(), arg.getClass().getName()); } } } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { for (Object arg : args) { if (!(arg instanceof JavaBeanDescriptor)) { error(generic, JavaBeanDescriptor.class.getName(), arg.getClass().getName()); } } } ((RpcInvocation) invocation).setAttachment( Constants.GENERIC_KEY, invoker.getUrl().getParameter(Constants.GENERIC_KEY)); } //普通调用并返回 return invoker.invoke(invocation); } private void error(String generic, String expected, String actual) throws RpcException { throw new RpcException( "Generic serialization [" + generic + "] only support message type " + expected + " and your message type is " + actual); } }
-
GenericFilter:判断如果是泛化操作第一步则是按照序列化方式进行反序列化,并进行服务调用
/** * GenericInvokerFilter. * 1. 用于provider,实现泛化调用,实现序列化的检查和处理 */ @Activate(group = Constants.PROVIDER, order = -20000) public class GenericFilter implements Filter { @Override public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException { if (inv.getMethodName().equals(Constants.$INVOKE) && inv.getArguments() != null && inv.getArguments().length == 3 && !invoker.getInterface().equals(GenericService.class)) { //调用方法名 String name = ((String) inv.getArguments()[0]).trim(); //调用方法参数 String[] types = (String[]) inv.getArguments()[1]; //参数值 Object[] args = (Object[]) inv.getArguments()[2]; try { //反射获取方法实例 Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types); Class<?>[] params = method.getParameterTypes(); if (args == null) { args = new Object[params.length]; } // 获取泛化调用的泛化类型 String generic = inv.getAttachment(Constants.GENERIC_KEY); // 泛化类型为空,则从上下文获取 if (StringUtils.isBlank(generic)) { generic = RpcContext.getContext().getAttachment(Constants.GENERIC_KEY); } // generic 为空 || 默认情况,则使用 generic=true 的方式 if (StringUtils.isEmpty(generic) || ProtocolUtils.isDefaultGenericSerialization(generic)) { args = PojoUtils.realize(args, params, method.getGenericParameterTypes()); } else if (ProtocolUtils.isJavaGenericSerialization(generic)) { // generic = nativjava 的方式 for (int i = 0; i < args.length; i++) { if (byte[].class == args[i].getClass()) { try { UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream((byte[]) args[i]); args[i] = ExtensionLoader.getExtensionLoader(Serialization.class) .getExtension(Constants.GENERIC_SERIALIZATION_NATIVE_JAVA) .deserialize(null, is).readObject(); } catch (Exception e) { throw new RpcException("Deserialize argument [" + (i + 1) + "] failed.", e); } } else { throw new RpcException( "Generic serialization [" + Constants.GENERIC_SERIALIZATION_NATIVE_JAVA + "] only support message type " + byte[].class + " and your message type is " + args[i].getClass()); } } } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { // generic = bean的方式 for (int i = 0; i < args.length; i++) { if (args[i] instanceof JavaBeanDescriptor) { args[i] = JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) args[i]); } else { throw new RpcException( "Generic serialization [" + Constants.GENERIC_SERIALIZATION_BEAN + "] only support message type " + JavaBeanDescriptor.class.getName() + " and your message type is " + args[i].getClass().getName()); } } } // 进行服务调用。传递给下一个 filter,最后进行服务调用 Result result = invoker.invoke(new RpcInvocation(method, args, inv.getAttachments())); // 对结果集进行反序列化并返回 if (result.hasException() && !(result.getException() instanceof GenericException)) { return new RpcResult(new GenericException(result.getException())); } if (ProtocolUtils.isJavaGenericSerialization(generic)) { try { UnsafeByteArrayOutputStream os = new UnsafeByteArrayOutputStream(512); ExtensionLoader.getExtensionLoader(Serialization.class) .getExtension(Constants.GENERIC_SERIALIZATION_NATIVE_JAVA) .serialize(null, os).writeObject(result.getValue()); return new RpcResult(os.toByteArray()); } catch (IOException e) { throw new RpcException("Serialize result failed.", e); } } else if (ProtocolUtils.isBeanGenericSerialization(generic)) { return new RpcResult(JavaBeanSerializeUtil.serialize(result.getValue(), JavaBeanAccessor.METHOD)); } else { return new RpcResult(PojoUtils.generalize(result.getValue())); } } catch (NoSuchMethodException e) { throw new RpcException(e.getMessage(), e); } catch (ClassNotFoundException e) { throw new RpcException(e.getMessage(), e); } } return invoker.invoke(inv); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!