Dubbo: Generalized Implementation of Call Features

1. Overview

This article shares the implementation of generalization. Let's take a look. User Guide - Generalized Implementation Definition:

Pan-interface implementations are mainly used when there is no API interface and model class elements on the server side. All POJO s in parameters and return values are represented by Map. They are usually used for framework integration. For example, a universal remote service Mock framework can be implemented to process all service requests through the implementation of GenericService interface.

Note that the consumer provider does not have API interfaces and model class elements. That is to say, Dubbo needs to do two things in the implementation of generalization:

Generalization implementations apply to service providers, and generalization references apply to service consumers, just the opposite.

  • There is no API interface, so a generic service interface is provided. com.alibaba.dubbo.rpc.service.GenericService .
    • A generalized implementation implements only one service.
    • By implementing the $invoke(method, parameterTypes, args) method, all requests for the service are processed.
    • Specific ways of use, we are in the ___________ "2. Examples" Medium-sized.
  • There are no model class elements, so method parameters and method returns need to be transformed if POJO (e.g. User and Order, etc.):
    • Service consumers, turn POJO into Map, and then invoke service providers. (Transparency)
    • Service provider, return to Map.
    • If the service consumer receives a return value of Map, it is converted to POJO and returned.
    • (The Map here is just an example. In fact, we will see another way of conversion in the following section. (Transparency)

The overall process is as follows:

2. Examples

Service Provider

In dubbo-generic-service-demo-provider We provide examples. Let's pick the key points.

(1) Implementing GenericService interface in Java code:

public class DemoServiceImpl implements GenericService {

    @Override
    public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException {
        if ("sayHello".equals(method)) {
            return "Welcome " + args[0];
        }
        return "unknown method";
    }

}

(2) Implementation of Spring Configuration Declaration Service:

<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />

<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" generic="true" />
  • Interface Configuration Item to generalize the implemented service interface. With this configuration, service consumers can obtain the addresses of all providers of the service from the registry, including the addresses of the services implemented by generalization.
  • generic configuration item, default to false, does not use configuration item. There are currently two configuration item values that turn on the functionality of generalization implementations:

Service consumers

In dubbo-generic-service-demo-consumer We provide examples. Let's pick the key points.

The calling code is as follows:

DemoService demoService = (DemoService) context.getBean("demoService"); // get remote service proxy
Object result = demoService.say01("NIHAO");
System.out.println("result: " + result);
  • Just like our ordinary service consumers, invoking service providers, attention is exactly the same.

3. Generic ImplFilter for Service Consumers

com.alibaba.dubbo.rpc.filter.GenericImplFilter To implement the Filter interface, service consumers call the Filter in a generalized way. The code is as follows:

  1: @Activate(group = Constants.CONSUMER, value = Constants.GENERIC_KEY, order = 20000)
  2: public class GenericImplFilter implements Filter {
  3: 
  4:     private static final Logger logger = LoggerFactory.getLogger(GenericImplFilter.class);
  5: 
  6:     private static final Class<?>[] GENERIC_PARAMETER_TYPES = new Class<?>[]{String.class, String[].class, Object[].class};
  7: 
  8:     @Override
  9:     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
 10:         // Get the `generic'configuration item
 11:         String generic = invoker.getUrl().getParameter(Constants.GENERIC_KEY);
 12: 
 13:         // Calls to generalize implementations
 14:         if (ProtocolUtils.isGeneric(generic)
 15:                 && !Constants.$INVOKE.equals(invocation.getMethodName())
 16:                 && invocation instanceof RpcInvocation) {
 17:             RpcInvocation invocation2 = (RpcInvocation) invocation;
 18:             String methodName = invocation2.getMethodName();
 19:             Class<?>[] parameterTypes = invocation2.getParameterTypes();
 20:             Object[] arguments = invocation2.getArguments();
 21: 
 22:             // Get an array of parameter types
 23:             String[] types = new String[parameterTypes.length];
 24:             for (int i = 0; i < parameterTypes.length; i++) {
 25:                 types[i] = ReflectUtils.getName(parameterTypes[i]);
 26:             }
 27: 
 28:             Object[] args;
 29:             // [Step 1] bean, serialization parameter, method parameter => JavaBean Descriptor
 30:             if (ProtocolUtils.isBeanGenericSerialization(generic)) {
 31:                 args = new Object[arguments.length];
 32:                 for (int i = 0; i < arguments.length; i++) {
 33:                     args[i] = JavaBeanSerializeUtil.serialize(arguments[i], JavaBeanAccessor.METHOD);
 34:                 }
 35:             // [Step 1] `true', serialization parameter, only Map => POJO
 36:             } else {
 37:                 args = PojoUtils.generalize(arguments);
 38:             }
 39: 
 40:             // Modify the name of the calling method to `invoke'.`
 41:             invocation2.setMethodName(Constants.$INVOKE);
 42:             // Set the parameter type of the calling method to `GENERIC_PARAMETER_TYPES'.`
 43:             invocation2.setParameterTypes(GENERIC_PARAMETER_TYPES);
 44:             // Set the parameter array of the calling method, which is the method name, the parameter type array, and the parameter array.
 45:             invocation2.setArguments(new Object[]{methodName, types, args});
 46: 
 47:             // [Step 2] RPC calls
 48:             Result result = invoker.invoke(invocation2);
 49: 
 50:             // [Step 3] Normal results of deserialization
 51:             if (!result.hasException()) {
 52:                 Object value = result.getValue();
 53:                 try {
 54:                     // [Step 3] bean, deserialization result, JavaBean Descriptor => result
 55:                     if (ProtocolUtils.isBeanGenericSerialization(generic)) {
 56:                         if (value == null) {
 57:                             return new RpcResult(null);
 58:                         } else if (value instanceof JavaBeanDescriptor) {
 59:                             return new RpcResult(JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) value));
 60:                         } else { // Must be a JavaBeanDescriptor return
 61:                             throw new RpcException(
 62:                                     new StringBuilder(64)
 63:                                             .append("The type of result value is ")
 64:                                             .append(value.getClass().getName())
 65:                                             .append(" other than ")
 66:                                             .append(JavaBeanDescriptor.class.getName())
 67:                                             .append(", and the result is ")
 68:                                             .append(value).toString());
 69:                         }
 70:                     } else {
 71:                         // Get the corresponding Method method Method object
 72:                         Method method = invoker.getInterface().getMethod(methodName, parameterTypes);
 73:                         //[Step 3] `true', deserialization results, only Map => POJO
 74:                         return new RpcResult(PojoUtils.realize(value, method.getReturnType(), method.getGenericReturnType()));
 75:                     }
 76:                 } catch (NoSuchMethodException e) {
 77:                     throw new RpcException(e.getMessage(), e);
 78:                 }
 79:             // [Step 3] Deserialization of anomalous results
 80:             } else if (result.getException() instanceof GenericException) {
 81:                 GenericException exception = (GenericException) result.getException();
 82:                 try {
 83:                     String className = exception.getExceptionClass();
 84:                     Class<?> clazz = ReflectUtils.forName(className);
 85:                     Throwable targetException = null;
 86:                     Throwable lastException = null;
 87:                     // Create the original exception
 88:                     try {
 89:                         targetException = (Throwable) clazz.newInstance();
 90:                     } catch (Throwable e) {
 91:                         lastException = e;
 92:                         for (Constructor<?> constructor : clazz.getConstructors()) {
 93:                             try {
 94:                                 targetException = (Throwable) constructor.newInstance(new Object[constructor.getParameterTypes().length]);
 95:                                 break;
 96:                             } catch (Throwable e1) {
 97:                                 lastException = e1;
 98:                             }
 99:                         }
100:                     }
101:                     // Details of setting exceptions
102:                     if (targetException != null) {
103:                         try {
104:                             Field field = Throwable.class.getDeclaredField("detailMessage");
105:                             if (!field.isAccessible()) {
106:                                 field.setAccessible(true);
107:                             }
108:                             field.set(targetException, exception.getExceptionMessage());
109:                         } catch (Throwable e) {
110:                             logger.warn(e.getMessage(), e);
111:                         }
112:                         // Create a new exception RpcResult object
113:                         result = new RpcResult(targetException);
114:                     // Failed to create original exception, throw exception
115:                     } else if (lastException != null) {
116:                         throw lastException;
117:                     }
118:                 } catch (Throwable e) { // If an exception occurs, it is wrapped as a RpcException exception and thrown.
119:                     throw new RpcException("Can not deserialize exception " + exception.getExceptionClass() + ", message: " + exception.getExceptionMessage(), e);
120:                 }
121:             }
122:             // Returns the RpcResult result
123:             return result;
124:         }
125: 
126:         // Eliminate code.... Call for generic references
127: 
128:          // Ordinary calls
129:         return invoker.invoke(invocation);
130:     }
131: 
132:     // ... Eliminate get/setting
133: 
134: }
  • Overall, there are some similarities to the GenericFilter of service providers.
  • Using Dubbo SPI Adaptive mechanism, automatic loading, service consumers only, and generic configuration items.
  • Line 126: Eliminate calls to generalize references, in the ____________ Perfect Dubbo Source Analysis: Generalized Reference to Call Characteristics (2) Detailed analysis.
  • Line 129: If it's a normal call (a call with non-generic references), call the Invoker#invoke(invocation) method, continue the invocation of the filter chain, and finally invoke the Service service.
  • (viii) Main Play (viii) Main Play (viii)
  • Lines 14 to 16: Judgment is a call to a generalized implementation
  • Lines 22 to 26: Get an array of parameter types.
  • ========== [Step 1: Sequencing parameters]==========
  • Lines 29 to 34: generic = bean, calling the JavaBeanSerializeUtil#serialize(JavaBeanDescriptor) method, serializing parameters, that is, method parameters => JavaBeanDescriptor.
  • Lines 35 to 38: generic = true, call PojoUtils#generalize(Object[] objs Method, serialization parameter, only POJO => Map.
  • ========== [Step 2: RPC calls]==========
  • Line 41: The method to set RpcInvocation is called $invoke.
  • Line 42: Set the method parameter type of RpcInvocation to GENERIC_PARAMETER_TYPES.
  • Line 43: Set the parameter array of RpcInvocation to methodName types.
  • Line 48: By setting up RpcInvocation as above, we can invoke the Invoker#invoke(invocation) method to enable RPC to invoke services implemented in generalization.
  • ========== [Step 3: Deserialize normal results]==========
  • Lines 55 to 69: generic = bean, calling the JavaBeanSerializeUtil serialize (JavaBeanDescriptor) method, and deserializing the result, that is, JavaBeanDescriptor => POJO.
  • Lines 71 to 74: generic = true, call the PojoUtils # realization (pojo, type, genericType) method, and the result of deserialization is only Map => POJO.
  • Note that after the deserialization, a new RpcResult is created.
  • ========== [Step 3: Deserializing the Abnormal Results]==========
  • Lines 87 to 100: Create the original exception targetException based on the GenericException exception.
  • Lines 101 to 111: Set exception details to targetException.
  • Line 113: Create a new exception RpcResult object.
  • Lines 114 to 117: Creating the original exception failed, throwing an exception lastException.

Keywords: Dubbo Java Spring

Added by doreg28 on Wed, 04 Sep 2019 15:33:47 +0300