jannal(无名小宝)

没有失败,只有缓慢的成功

导航

Dubbo之泛化调用

dubbo版本

  1. dubbo版本2.6.7

泛化调用

  1. 泛化调用就是服务消费者端因为某种原因并没有该服务接口,此时通过GenericServiceinvoke需要调用的服务方法

    • 比如其他语言调用java的接口
    • consumer没有provider所定义的接口
  2. 泛接口调用方式主要用于客户端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的服务测试框架或者Mock框架等,可通过GenericService调用所有服务实现。

    public interface GenericService {
        //$invoke的三个参数分别为,方法名,方法参数类型数组,方法参数数组
        Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException;
    }
    
  3. Dubbo泛化有三种不同的序列化方式

    • generic = true:对参数使用 PojoUtils 进行序列化
    • generic = bean : 对参数使用 JavaBean 方式进行序列化
    • generic = nativejava:对参数使用 nativejava 方式进行序列化

Dubbo2.6.x案例

  1. 接口

    public interface DemoService {
    
        String sayHello(String name);
    
        SayResponseDTO sayHello(SayRequestDTO sayRequestDTO);
    
    }
    
  2. consumer配置 generic="true"

    <dubbo:reference id="demoService"
                     check="false"
                     interface="cn.jannal.dubbo.facade.DemoService"
                     group="group0"
                     generic="true"
                     version="1.0.0"/>
    
  3. 调用代码

    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新特性

  1. 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);
    
  2. 对 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();
    

源码解析

  1. 基于dubbo2.6.7版本

  2. Dubbo的泛化调用泛化实现依赖于两个过滤器来完成

    • Consumer端:GenericImplFilter完成了消费者端的泛化功能
    • Provider端:GenericFilter完成了提供者端的泛化功能
  3. 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);
        }
    
    }
    
  4. 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);
        }
    
    }
    

posted on 2022-02-08 10:30  jannal  阅读(510)  评论(0编辑  收藏  举报