对于远程调用来说,最高境界就是让使用者不知道此处代码是远程调用一样,感觉就是调用本地的某个普通方法,封装底层调用的细节,这是我们服务消费端最后要做的事情
比如,消费者需要调用此接口的方法
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进行封装
ProxyFactory.java
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容器完成这些代理的完成,这样做的好处不言而喻
这边的代码可以直接查看源码~