Dubbo calls the proxy class asynchronously

Dubbo is used in the project, of course, it is called synchronously by default. One day I thought that since it is calling a remote service, it should be able to be called asynchronously, which may increase concurrency and reduce time. So I looked it up online, and sure enough it works. After configuring the remote service to be asynchronous, call it like this:

// return null immediately after calling  
        Person person=demoServer2.getPerson("www", 13);  
        System.err.println("The immediate return is null:"+person);  
        //Get the called Future reference, when the result is returned, it will be notified and set to this Future.  
        Future<Person> pFuture = RpcContext.getContext().getFuture();  
        //If Person has returned, get the return value directly, otherwise the thread waits, and after Person returns, the thread will be woken up by notify.  
        person = pFuture.get();  
        System.out.println("Returned value"+person);  
        System.out.println(person);  

See: https://blog.csdn.net/qh_java/article/details/53055421

But I feel that it is not very convenient to use this way, and I have to get a future every time I call it. Then I thought that hibernate seems to have a proxy mode, that is, the object queried is actually a proxy object. When the method of the proxy object is called, the sql query to the object is actually executed.

So I thought that I could follow this idea and return a proxy result after each asynchronous call. When the method of the proxy result is called, execute future.get() to get the returned result. code show as below:

1. First write a proxy class for a remote service.

package com.csair.csm.web.proxy;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.Future;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import com.alibaba.dubbo.rpc.RpcContext;

/**
 * Service proxy class, used to provide a proxy for asynchronous services
 *<br>!!! The returned object called using the proxy service is not null, if it is actually null, calling any method of the returned object will return null
 * @author abo
 */
public class ProxyService implements MethodInterceptor {
	
	/**
	 * servant
	 */
	private Object service;

	private ProxyService(Object service) {
		this.service = service;
	}
	
	/**
	 * Create a proxy service
	 * @param service
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <S> S create(S service) {
		// 1. Tool class
		Enhancer en = new Enhancer ();
		// 2. Set the parent class
		en.setSuperclass (service.getClass ());
		// 3. Set the callback function
		en.setCallback(new ProxyService(service));
		// 4. Create a subclass (proxy object)
		return (S) en.create();
	}
	
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		// execute the server method
		Object result = method.invoke(service, args);
		// get return
		Future<Object> future = RpcContext.getContext().getFuture();
		if (future == null) {
			// future is null, indicating that it is a synchronous call and returns directly
			return result;
		}
		// return type
		Class<?> returnClass = method.getReturnType();
		if (Modifier.isFinal(returnClass.getModifiers())) {
			// final class returns directly
			return future.get();
		}
		// return proxy result
		return ProxyServiceResult.create(future, returnClass);
	}
	
}

This class is used to provide a proxy for the remote service. When calling the method of the proxy service, the method of the original asynchronous service is called first, and then a proxy result is returned to the caller.

However, this also has some shortcomings, that is, if the return of the asynchronous service is null, because the actual result is not known when the proxy result is created, the proxy result will not be null. The other is that there is no proxy for final classes, and you can only wait and return the actual result immediately. To solve this problem, the current thought is to wrap the results of asynchronous services with a Result class.

2. Then there is a proxy class that serves the result.

package com.csair.csm.web.proxy;

import java.lang.reflect.Method;
import java.util.concurrent.Future;

import org.slf4j.Logger;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import com.csair.csm.servicelog.LoggerUtils;

/**
 * A proxy for the return value of the asynchronous service
 *<br>!!! The proxy object is not null, if it is actually null, calling any of its methods will return null
 * @author abo
 */
public class ProxyServiceResult implements MethodInterceptor {
	
	/**
	 * Return of asynchronous call
	 */
	private Future<Object> resultFuture;
	
	/**
	 * log
	 */
	private static Logger logger = LoggerUtils.getCommonLogger();
	
	private ProxyServiceResult(Future<Object> resultFuture) {
		this.resultFuture = resultFuture;
	}
	
	/**
	 * Create a proxy result
	 * @param resultFuture
	 * @param resultClass
	 * @return
	 */
	public static Object create(Future<Object> resultFuture, Class<?> resultClass) {
		// 1. Tool class
		Enhancer en = new Enhancer ();
		// 2. Set the parent class
		en.setSuperclass (resultClass);
		// 3. Set the callback function
		en.setCallback(new ProxyServiceResult(resultFuture));
		// 4. Create a subclass (proxy object)
		return en.create();
	}
	
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		// get return value from future
		Object result = resultFuture.get();
		if (result == null) {
			// If the result is null, the call will throw an exception and return null directly
			logger.warn("The actual return result of the remote asynchronous service is null, and the proxy result is not null.");
			return null;
		}
		// call again
		return method.invoke(result, args);
	}

}

This is used to provide a proxy for the return result of calling an asynchronous service. When calling the method of the proxy result, it will get the actual result from the future, and then call the method on the actual result.

This is done, just use it like this:

        /**
    	 * Log service
	 */
	@GuardedBy("itself")
        @Reference(timeout = 60000, async = true) // asynchronous service
	private LoggerService loggerService;
	
	/**
	 * Execute after initialization, replace asynchronous service with proxy service
	 */
	@PostConstruct
	public void init() {
		this.loggerService = ProxyService.create(this.loggerService);
	}

First configure the remote service to be asynchronous (async = true), and then replace the original service with a proxy service in @PostConstruct. Then you can use it normally as you would use a sync service.

Because I am not familiar with the cglib agent, the agent class is just rewritten by imitating the example on the Internet. If there is something badly written, I hope someone will criticize and correct it.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325984277&siteId=291194637