对于远程调用来说,最高境界就是让使用者不知道此处代码是远程调用一样,感觉就是调用本地的某个普通方法,封装底层调用的细节,这是我们服务消费端最后要做的事情
比如,消费者需要调用此接口的方法
package org.laopopo.example.generic.test_2;
import org.laopopo.client.annotation.RPConsumer;
public interface HelloService {
@RPConsumer(serviceName="LAOPOPO.TEST.SAYHELLO")
String sayHello(String str);
}
对于他来说,最最简单的方式就是:
String str = helloService.sayHello("Lyncc");
这样才是最好用的RPC方式,但是如果仅仅是这样,我们肯定是调不通的,所以我们需要对helloService进行封装
HelloService helloService = ProxyFactory.factory(HelloService.class).consumer(client).timeoutMillis(3000l).newProxyInstance();
package org.laopopo.client.consumer.proxy;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.laopopo.client.annotation.RPConsumer;
import org.laopopo.client.consumer.Consumer;
import org.laopopo.common.utils.Proxies;
import org.laopopo.common.utils.UnresolvedAddress;
/**
*
* @author BazingaLyn
* @description 代理工厂类,用于对服务接口的编织
* @time 2016年9月1日
* @modifytime
*/
public class ProxyFactory<T> {
private final Class<T> interfaceClass; //编织对象
private Consumer consumer; //维护一个消费客户端
private List<UnresolvedAddress> addresses; <span style="white-space:pre"> </span>//该服务的直连url集合
private long timeoutMillis; //接口整理超时时间
private Map<String, Long> methodsSpecialTimeoutMillis; //每个方法特定的超时时间
public static <I> ProxyFactory<I> factory(Class<I> interfaceClass) {
ProxyFactory<I> factory = new ProxyFactory<>(interfaceClass);
// 初始化数据
factory.addresses = new ArrayList<UnresolvedAddress>();
return factory;
}
/**
* 设置接口对象
* @param interfaceClass
*/
private ProxyFactory(Class<T> interfaceClass) {
this.interfaceClass = interfaceClass;
}
/**
* 设置该代理工厂的唯一的消费端
* @param consumer
* @return
*/
public ProxyFactory<T> consumer(Consumer consumer) {
this.consumer = consumer;
return this;
}
/**
* 增加直连对象的url
* @param addresses
* @return
*/
public ProxyFactory<T> addProviderAddress(UnresolvedAddress... addresses) {
Collections.addAll(this.addresses, addresses);
return this;
}
/**
*
* @param timeoutMillis
* @return
*/
public ProxyFactory<T> timeoutMillis(long timeoutMillis) {
this.timeoutMillis = timeoutMillis;
return this;
}
public ProxyFactory<T> methodSpecialTimeoutMillis(String methodName, long timeoutMillis) {
methodsSpecialTimeoutMillis.put(methodName, timeoutMillis);
return this;
}
public T newProxyInstance() {
Method[] methods = interfaceClass.getMethods();
if(methods == null || methods.length == 0){
throw new UnsupportedOperationException("the interfaceClass has no any methods");
}
boolean isAnnotation = false;
for(Method method : methods){
RPConsumer consumerAnnotation = method.getAnnotation(RPConsumer.class);
if(null != consumerAnnotation){
isAnnotation = true;
}
String serviceName = consumerAnnotation.serviceName();
if(addresses != null && addresses.size() > 0){
for (UnresolvedAddress address : addresses) {
consumer.addChannelGroup(serviceName, consumer.group(address));
}
}
}
if(!isAnnotation){
throw new UnsupportedOperationException("the interfaceClass no any annotation [@RPConsumer]");
}
Object handler = new SynInvoker(consumer,timeoutMillis,methodsSpecialTimeoutMillis);
return Proxies.getDefault().newProxy(interfaceClass, handler);
}
}
基本的思路就是这样的,对调用的方法进行代理,让使用者觉得就像调用本地的方法
当然这些操作其实最优的方式就是在spring中完成,当项目使用spring管理的时候,我们可以在spring容器完成这些代理的完成,这样做的好处不言而喻
这边的代码可以直接查看源码~