I. Callers of services
Overview of service invokers:
The invoker package in the remoting package is the service caller, including configuration, bean proxy, load balancing strategy, invocation scheme, etc.
II. Generating Agent
2.1 @XxlRpcReference
Let's first look at the @XxlRpcReference annotation, which defines some of the strategies used by service callers.
@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface XxlRpcReference { //Communication mode, default netty NetEnum netType() default NetEnum.NETTY; //Serialization, default hessian Serializer.SerializeEnum serializer() default Serializer.SerializeEnum.HESSIAN; // Call mode, default sync CallType callType() default CallType.SYNC; // Load balancing strategy, default round LoadBalance loadBalance() default LoadBalance.ROUND; // version String version() default ""; // timeout long timeout() default 1000; // Service Provider Address, which you can configure on your own String address() default ""; // token is used for validation String accessToken() default ""; //XxlRpcInvokeCallback invokeCallback() ; }
2.2 XxlRpcSpringInvokerFactory class
If you use spring, you can use rpc in the form of annotations + XxlRpc Spring InvokerFactory.
First of all, look at the inheritance relationship of this kind:
public class XxlRpcSpringInvokerFactory extends InstantiationAwareBeanPostProcessorAdapter implements InitializingBean,DisposableBean, BeanFactoryAware
Inherit the InstantiationAwareBean PostProcessor Adapter and implement the InitializingBean, Disposable Bean and BeanFactoryAware interfaces.
Talk about the timing of their calls (in order):
BeanFactoryAware: Inject beanfactory for use. You can use beanfactory to get beans in spring. He has a setBeanFactory method on his side. InitializingBean: Call the afterPropertiesSet method after the bean instantiates the properties InstantiationAwareBeanPostProcessor Adapter: Enhance bean s after instantiation. Call the postProcessAfterInstantiation method. Disposable Bean: The destroy method is called when the bean is destroyed.
The order of execution is clear.
setBeanFactory() ----> afterPropertiesSet() ---->postProcessAfterInstantiation() ---->destroy().
It can be said that the first three methods are related to service caller initialization. Let's look next to each other.
2.2.1 setBeanFactory
This is very simple. It refers to the beanfactory as a member variable.
private BeanFactory beanFactory; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; }
2.2.2 afterPropertiesSet
@Override public void afterPropertiesSet() throws Exception { // start invoker factory xxlRpcInvokerFactory = new XxlRpcInvokerFactory(serviceRegistryClass, serviceRegistryParam); xxlRpcInvokerFactory.start(); }
Create the XxlRpcInvokerFactory object and call the start method. Next, let's look at this class.
2.2.2.1 XxlRpcProviderFactory
XxlRpcProviderFactory class mainly does service registration, client thread pool response initialization and callback thread pool initialization, client start, stop work.
Let's look at the start method:
The corresponding registry object is created according to the configuration, and the start method of the registry is invoked. There is no detailed analysis here. The registry start method is mainly about creating registry client and creating daemon threads to refresh the list of access service providers.
public void start() throws Exception { // start registry if (serviceRegistryClass != null) { // Creating Registry Objects serviceRegistry = serviceRegistryClass.newInstance(); // Call the start method of the registry object serviceRegistry.start(serviceRegistryParam); } }
Next, look at the stop method:
public void stop() throws Exception { // Stop registry stop registry client if (serviceRegistry != null) { serviceRegistry.stop(); } // Stop callback executes stop callback method if (stopCallbackList.size() > 0) { for (BaseCallback callback: stopCallbackList) { try { callback.run(); } catch (Exception e) { logger.error(e.getMessage(), e); } } } // Stop Callback ThreadPool Closes Thread Pool stopCallbackThreadPool(); }
2.2.3 postProcessAfterInstantiation
Look at this code first, and add comments at the key points. It's very simple and easy to understand.
@Override public boolean postProcessAfterInstantiation(final Object bean, final String beanName) throws BeansException { // collection final Set<String> serviceKeyList = new HashSet<>(); // parse XxlRpcReferenceBean ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() { @Override public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { // Does the field have XxlRpcReference annotations if (field.isAnnotationPresent(XxlRpcReference.class)) { // valid gets the type of field Class iface = field.getType(); // Field is not an exception thrown by the interface if (!iface.isInterface()) { throw new XxlRpcException("xxl-rpc, reference(XxlRpcReference) must be interface."); } // Get annotation information. XxlRpcReference rpcReference = field.getAnnotation(XxlRpcReference.class); // Init reference beans create reference beans, set some required parameters, and set the parameters in the annotations to the reference bean object. XxlRpcReferenceBean referenceBean = new XxlRpcReferenceBean( rpcReference.netType(), rpcReference.serializer().getSerializer(), rpcReference.callType(), rpcReference.loadBalance(), iface, rpcReference.version(), rpcReference.timeout(), rpcReference.address(), rpcReference.accessToken(), null, xxlRpcInvokerFactory ); // Call the getObject method, which mainly uses the generation of proxy objects. Object serviceProxy = referenceBean.getObject(); // set bean field.setAccessible(true); //Assign a value to this field field.set(bean, serviceProxy); logger.info(">>>>>>>>>>> xxl-rpc, invoker factory init reference bean success. serviceKey = {}, bean.field = {}.{}", XxlRpcProviderFactory.makeServiceKey(iface.getName(), rpcReference.version()), beanName, field.getName()); // collection generates service key based on the full class name and version of the interface, and looks at it with the service provider String serviceKey = XxlRpcProviderFactory.makeServiceKey(iface.getName(), rpcReference.version()); serviceKeyList.add(serviceKey); } } }); // mult discovery performs service discovery, caching the service provider of this key locally if (xxlRpcInvokerFactory.getServiceRegistry() != null) { try { xxlRpcInvokerFactory.getServiceRegistry().discovery(serviceKeyList); } catch (Exception e) { logger.error(e.getMessage(), e); } } return super.postProcessAfterInstantiation(bean, beanName); }
This method mainly finds out the member variables with XxlRpcReference annotation. Then parse the parameters in the XxlRpcReference annotation, create the XxlRpcReference Bean object, generate the proxy object, and assign the proxy object to the field, so that when we use the service, the proxy object is really called. Next we'll look at the XxlRpcReferenceBean class.
2.2.3.1 XxlRpcReferenceBean
Proxy objects can be generated from this class, which is what we often call client stub.
First, let's look at the fields and constructions of this class.
private NetEnum netType; private Serializer serializer; // Serialization approach, hession private CallType callType; // Call mode private LoadBalance loadBalance; //Load Balancing Strategy private Class<?> iface; // Interface class object private String version; // Edition private long timeout = 1000; //timeout private String address; // Service Provider Address private String accessToken;// token private XxlRpcInvokeCallback invokeCallback; private XxlRpcInvokerFactory invokerFactory; // invoker factory public XxlRpcReferenceBean(NetEnum netType, Serializer serializer, CallType callType, LoadBalance loadBalance, Class<?> iface, String version, long timeout, String address, String accessToken, XxlRpcInvokeCallback invokeCallback, XxlRpcInvokerFactory invokerFactory ) { this.netType = netType; this.serializer = serializer; this.callType = callType; this.loadBalance = loadBalance; this.iface = iface; this.version = version; this.timeout = timeout; this.address = address; this.accessToken = accessToken; this.invokeCallback = invokeCallback; this.invokerFactory = invokerFactory; // valid if (this.netType==null) { throw new XxlRpcException("xxl-rpc reference netType missing."); } if (this.serializer==null) { throw new XxlRpcException("xxl-rpc reference serializer missing."); } if (this.callType==null) { throw new XxlRpcException("xxl-rpc reference callType missing."); } if (this.loadBalance==null) { throw new XxlRpcException("xxl-rpc reference loadBalance missing."); } if (this.iface==null) { throw new XxlRpcException("xxl-rpc reference iface missing."); } if (this.timeout < 0) { this.timeout = 0; } if (this.invokerFactory == null) { this.invokerFactory = XxlRpcInvokerFactory.getInstance(); } // init Client initClient(); }
At the end of the construction, an initClient method is called. This method is mainly to create client objects and initialize parameters.
Then let's look at the service caller core: generation of proxy objects
The getObject method is to generate a proxy object for a service. Using technology is the proxy of jdk.
public Object getObject() { return Proxy.newProxyInstance(Thread.currentThread() .getContextClassLoader(), new Class[] { iface }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // method param generates some call parameters String className = method.getDeclaringClass().getName(); // iface.getName() String varsion_ = version; // Method name String methodName = method.getName(); //Method parameter type Class<?>[] parameterTypes = method.getParameterTypes(); // Method parameter values Object[] parameters = args; // filter for generic // This is to determine whether it is a generalized call. If it is a generalized call, the parameters are configured according to the user, such as which class you want to call, method name, parameter type, parameter value. All these are necessary. // Set up if (className.equals(XxlRpcGenericService.class.getName()) && methodName.equals("invoke")) { Class<?>[] paramTypes = null; if (args[3]!=null) { String[] paramTypes_str = (String[]) args[3]; if (paramTypes_str.length > 0) { paramTypes = new Class[paramTypes_str.length]; for (int i = 0; i < paramTypes_str.length; i++) { paramTypes[i] = ClassUtil.resolveClass(paramTypes_str[i]); } } } className = (String) args[0]; varsion_ = (String) args[1]; methodName = (String) args[2]; parameterTypes = paramTypes; parameters = (Object[]) args[4]; } // This is to confirm what kind of method you came from. // filter method like "Object.toString()" if (className.equals(Object.class.getName())) { logger.info(">>>>>>>>>>> xxl-rpc proxy class-method not support [{}#{}]", className, methodName); throw new XxlRpcException("xxl-rpc proxy class-method not support"); } // The choice of service provider address is based on load balancing strategy String finalAddress = address; if (finalAddress==null || finalAddress.trim().length()==0) { if (invokerFactory!=null && invokerFactory.getServiceRegistry()!=null) { // discovery String serviceKey = XxlRpcProviderFactory.makeServiceKey(className, varsion_); TreeSet<String> addressSet = invokerFactory.getServiceRegistry().discovery(serviceKey); // load balance if (addressSet==null || addressSet.size()==0) { // pass } else if (addressSet.size()==1) { // When it's just one, choose the first one. finalAddress = addressSet.first(); } else { // load balancing finalAddress = loadBalance.xxlRpcInvokerRouter.route(serviceKey, addressSet); } } } // The last call address or null throws an exception if (finalAddress==null || finalAddress.trim().length()==0) { throw new XxlRpcException("xxl-rpc reference bean["+ className +"] address empty"); } // Request request parameter blocking XxlRpcRequest xxlRpcRequest = new XxlRpcRequest(); xxlRpcRequest.setRequestId(UUID.randomUUID().toString()); xxlRpcRequest.setCreateMillisTime(System.currentTimeMillis()); xxlRpcRequest.setAccessToken(accessToken); xxlRpcRequest.setClassName(className); xxlRpcRequest.setMethodName(methodName); xxlRpcRequest.setParameterTypes(parameterTypes); xxlRpcRequest.setParameters(parameters); // send is invoked according to the invocation policy if (CallType.SYNC == callType) { // future-response set XxlRpcFutureResponse futureResponse = new XxlRpcFutureResponse(invokerFactory, xxlRpcRequest, null); try { // do invoke client.asyncSend(finalAddress, xxlRpcRequest); // future get XxlRpcResponse xxlRpcResponse = futureResponse.get(timeout, TimeUnit.MILLISECONDS); if (xxlRpcResponse.getErrorMsg() != null) { throw new XxlRpcException(xxlRpcResponse.getErrorMsg()); } return xxlRpcResponse.getResult(); } catch (Exception e) { logger.info(">>>>>>>>>>> xxl-rpc, invoke error, address:{}, XxlRpcRequest{}", finalAddress, xxlRpcRequest); throw (e instanceof XxlRpcException)?e:new XxlRpcException(e); } finally{ // future-response remove futureResponse.removeInvokerFuture(); } } else if (CallType.FUTURE == callType) { // future-response set XxlRpcFutureResponse futureResponse = new XxlRpcFutureResponse(invokerFactory, xxlRpcRequest, null); try { // invoke future set XxlRpcInvokeFuture invokeFuture = new XxlRpcInvokeFuture(futureResponse); XxlRpcInvokeFuture.setFuture(invokeFuture); // do invoke client.asyncSend(finalAddress, xxlRpcRequest); return null; } catch (Exception e) { logger.info(">>>>>>>>>>> xxl-rpc, invoke error, address:{}, XxlRpcRequest{}", finalAddress, xxlRpcRequest); // future-response remove futureResponse.removeInvokerFuture(); throw (e instanceof XxlRpcException)?e:new XxlRpcException(e); } } else if (CallType.CALLBACK == callType) { // get callback XxlRpcInvokeCallback finalInvokeCallback = invokeCallback; XxlRpcInvokeCallback threadInvokeCallback = XxlRpcInvokeCallback.getCallback(); if (threadInvokeCallback != null) { finalInvokeCallback = threadInvokeCallback; } if (finalInvokeCallback == null) { throw new XxlRpcException("xxl-rpc XxlRpcInvokeCallback(CallType="+ CallType.CALLBACK.name() +") cannot be null."); } // future-response set XxlRpcFutureResponse futureResponse = new XxlRpcFutureResponse(invokerFactory, xxlRpcRequest, finalInvokeCallback); try { client.asyncSend(finalAddress, xxlRpcRequest); } catch (Exception e) { logger.info(">>>>>>>>>>> xxl-rpc, invoke error, address:{}, XxlRpcRequest{}", finalAddress, xxlRpcRequest); // future-response remove futureResponse.removeInvokerFuture(); throw (e instanceof XxlRpcException)?e:new XxlRpcException(e); } return null; } else if (CallType.ONEWAY == callType) { client.asyncSend(finalAddress, xxlRpcRequest); return null; } else { throw new XxlRpcException("xxl-rpc callType["+ callType +"] invalid"); } } }); }
Although this method seems very long, it is not very troublesome. It mainly does these things:
1. Get some parameters of the request, including which class you want to adjust, which method, method parameter type, method parameter value.
2. If it is a generic call (you specify the fields in 1) class, method name, method parameter type, method parameter should be according to your specified.
3. Choosing the address of the service provider, according to the load balancing strategy, you have the lowest priority in your annotations (which is a bit confusing).
4. Encapsulate request parameters, encapsulate classes, method names, method parameter types, method parameter values, and a unique uuid into Request object entities.
5. Call according to the calling strategy.
summary
This article mainly talks about the general process of xxl-rpc service caller generation, from configuration to registry connection to generate reference beans to generate proxy objects. Through this article, we can understand that the RPC service caller actually generates a call object in this service. There is no in-depth explanation of the details, such as call strategy, client network communication, load balancing and so on. These follow-up chapters will be explained in detail.