├── .gitignore ├── src ├── main │ └── java │ │ └── edu │ │ └── ouc │ │ └── rpc │ │ ├── interceptor │ │ ├── Interceptor.java │ │ ├── RpcMethodInvocation.java │ │ ├── MethodInvocation.java │ │ ├── DefaultMethodInvocation.java │ │ └── InterceptorChain.java │ │ ├── aop │ │ └── ConsumerHook.java │ │ ├── model │ │ ├── RpcException.java │ │ ├── RpcResponse.java │ │ └── RpcRequest.java │ │ ├── async │ │ ├── ResponseCallbackListener.java │ │ └── ResponseFuture.java │ │ ├── context │ │ └── RpcContext.java │ │ ├── RpcProvider.java │ │ └── RpcConsumer.java └── test │ └── java │ └── edu │ └── ouc │ └── rpc │ ├── demo │ ├── service │ │ ├── UserConsumerHook.java │ │ ├── UserService.java │ │ ├── UserServiceListener.java │ │ ├── User.java │ │ └── UserServiceImpl.java │ └── test │ │ ├── ServerTest.java │ │ └── ClientTest.java │ └── interceptor │ ├── MethodFilterInterceptor.java │ └── TimeInterceptor.java ├── resource ├── build.sh └── RPC-Framework ├── pom.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /target 3 | *.class 4 | *.project 5 | *.classpath 6 | /.settings 7 | -------------------------------------------------------------------------------- /src/main/java/edu/ouc/rpc/interceptor/Interceptor.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.interceptor; 2 | 3 | public interface Interceptor { 4 | 5 | Object intercept(MethodInvocation invocation) throws Exception; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/edu/ouc/rpc/aop/ConsumerHook.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.aop; 2 | 3 | import edu.ouc.rpc.model.RpcRequest; 4 | 5 | /** 6 | * @author wqx 7 | */ 8 | public interface ConsumerHook { 9 | public void before(RpcRequest request); 10 | public void after(RpcRequest request); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/edu/ouc/rpc/model/RpcException.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.model; 2 | 3 | /** 4 | * 自定义异常 5 | * 6 | * @author wqx 7 | * 8 | */ 9 | public class RpcException extends RuntimeException { 10 | 11 | private static final long serialVersionUID = -2157872157006208360L; 12 | 13 | public RpcException(String msg){ 14 | super(msg); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/edu/ouc/rpc/async/ResponseCallbackListener.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.async; 2 | 3 | /** 4 | * 监听器 5 | * 6 | * @author wqx 7 | * 8 | */ 9 | public interface ResponseCallbackListener { 10 | 11 | public void onResponse(Object response); 12 | 13 | public void onTimeout(); 14 | 15 | public void onException(Exception e); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/edu/ouc/rpc/interceptor/RpcMethodInvocation.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.interceptor; 2 | 3 | /** 4 | * RpcMethodInvocation 5 | * 6 | * @author wqx 7 | * 8 | */ 9 | public interface RpcMethodInvocation extends MethodInvocation { 10 | 11 | /** 12 | * 是否是rpc调用 13 | * 14 | * @return 15 | */ 16 | public boolean isRpcInvocation(); 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /resource/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CUR=$(pwd) 4 | 5 | ## reset dest folder 6 | OUTPUT_PATH=${CUR}/dest 7 | rm -rf $OUTPUT_PATH 8 | mkdir -p ${OUTPUT_PATH}/bin 9 | mkdir -p ${OUTPUT_PATH}/libs 10 | 11 | ## 获取依赖库和需要编译的java文件 12 | export libs=`find libs -name "*.jar" |xargs|sed "s/ /:/g"` 13 | export jfiles=`find src -name "*.java" |xargs|sed "s/ / /g"` 14 | 15 | ## 编译 16 | opt="-d $OUTPUT_PATH/bin -cp ${libs} -encoding utf-8" 17 | javac $opt ${jfiles} 18 | 19 | ## 打包 20 | cd ${OUTPUT_PATH}/bin 21 | jar -cvf ${CUR}/dest/mylib.jar * 22 | cd ${CUR}/src 23 | jar -cvf ${CUR}/dest/mylib-source.jar * -------------------------------------------------------------------------------- /src/test/java/edu/ouc/rpc/demo/service/UserConsumerHook.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.demo.service; 2 | 3 | import edu.ouc.rpc.aop.ConsumerHook; 4 | import edu.ouc.rpc.context.RpcContext; 5 | import edu.ouc.rpc.model.RpcRequest; 6 | 7 | public class UserConsumerHook implements ConsumerHook{ 8 | @Override 9 | public void before(RpcRequest request) { 10 | RpcContext.addAttribute("hook key","this is pass by hook"); 11 | } 12 | 13 | @Override 14 | public void after(RpcRequest request) { 15 | System.out.println("I have finished Rpc calling."); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/edu/ouc/rpc/interceptor/MethodInvocation.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.interceptor; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | /** 6 | * 抽象出方法的执行 7 | * 8 | * @author wqx 9 | * 10 | */ 11 | public interface MethodInvocation { 12 | 13 | /** 14 | * 获取方法 15 | * 16 | * @return 17 | */ 18 | public Method getMethod(); 19 | 20 | /** 21 | * 22 | * @return 23 | */ 24 | public Object[] getParameters(); 25 | 26 | /** 27 | * invoke next Interceptor 28 | * 29 | * @return 30 | * @throws Exception 31 | */ 32 | Object executeNext() throws Exception; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/edu/ouc/rpc/interceptor/MethodFilterInterceptor.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.interceptor; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | import edu.ouc.rpc.model.RpcException; 7 | 8 | public class MethodFilterInterceptor implements Interceptor { 9 | 10 | private Set exclusions = new HashSet<>(); 11 | 12 | public MethodFilterInterceptor(Set exclusions){ 13 | this.exclusions = exclusions; 14 | } 15 | @Override 16 | public Object intercept(MethodInvocation invocation) throws Exception { 17 | String methodName = invocation.getMethod().getName(); 18 | if(exclusions.contains(methodName)){ 19 | throw new RpcException("method " + methodName + " is not allowed!"); 20 | } 21 | return invocation.executeNext(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/edu/ouc/rpc/interceptor/TimeInterceptor.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.interceptor; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | import edu.ouc.rpc.context.RpcContext; 7 | 8 | /** 9 | * TimeInterceptor:记录方法执行时间 10 | * 11 | * @author wqx 12 | * 13 | */ 14 | public class TimeInterceptor implements Interceptor { 15 | 16 | @Override 17 | public Object intercept(MethodInvocation invocation) throws Exception { 18 | long start = System.currentTimeMillis(); 19 | Object retVal = null; 20 | try{ 21 | retVal = invocation.executeNext(); 22 | }finally{ 23 | long end = System.currentTimeMillis(); 24 | System.out.println("execute time :" + (end-start) + "ms."); 25 | RpcContext.addAttribute("time", (end-start)); 26 | } 27 | return retVal; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/edu/ouc/rpc/demo/test/ServerTest.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.demo.test; 2 | 3 | import java.io.IOException; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | import edu.ouc.rpc.RpcProvider; 8 | import edu.ouc.rpc.demo.service.UserService; 9 | import edu.ouc.rpc.demo.service.UserServiceImpl; 10 | import edu.ouc.rpc.interceptor.MethodFilterInterceptor; 11 | 12 | public class ServerTest { 13 | 14 | private static int port = 8888; 15 | 16 | public static void main(String[] args) throws IOException { 17 | UserService userService = new UserServiceImpl(); 18 | 19 | Set exclusions = new HashSet<>(); 20 | exclusions.add("getMap"); 21 | 22 | RpcProvider.interceptorChain.addLast("methodFilter", 23 | new MethodFilterInterceptor(exclusions)); 24 | 25 | RpcProvider.publish(userService, port); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/edu/ouc/rpc/model/RpcResponse.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.model; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 响应对象 7 | * 8 | * @author wqx 9 | * 10 | */ 11 | public class RpcResponse implements Serializable{ 12 | 13 | static private final long serialVersionUID = -4364536436151723421L; 14 | 15 | //响应实体 16 | private Object responseBody; 17 | 18 | //错误信息 19 | private String errorMsg; 20 | 21 | public String getErrorMsg() { 22 | return errorMsg; 23 | } 24 | 25 | public void setErrorMsg(String errorMsg) { 26 | this.errorMsg = errorMsg; 27 | } 28 | 29 | public Object getResponseBody() { 30 | return responseBody; 31 | } 32 | 33 | public void setResponseBody(Object responseBody) { 34 | this.responseBody = responseBody; 35 | } 36 | 37 | 38 | public boolean isError(){ 39 | return errorMsg == null ? false:true; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/edu/ouc/rpc/context/RpcContext.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.context; 2 | 3 | import java.util.Collections; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | /** 8 | * rpc上下文 9 | * 10 | * @author wqx 11 | * 12 | */ 13 | public class RpcContext { 14 | 15 | public static ThreadLocal> context = new ThreadLocal >(){ 16 | @Override 17 | protected Map initialValue() { 18 | Map m = new HashMap(); 19 | return m; 20 | } 21 | }; 22 | 23 | public static void addAttribute(String key, Object value){ 24 | context.get().put(key,value); 25 | } 26 | 27 | public static Object getAttribute(String key){ 28 | return context.get().get(key); 29 | } 30 | 31 | public static Map getAttributes(){ 32 | return Collections.unmodifiableMap(context.get()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/edu/ouc/rpc/demo/service/UserService.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.demo.service; 2 | 3 | import java.io.IOException; 4 | import java.util.Map; 5 | 6 | import edu.ouc.rpc.model.RpcException; 7 | 8 | /** 9 | * 测试用业务接口 10 | * 11 | * @author wqx 12 | * 13 | */ 14 | public interface UserService { 15 | 16 | /** 17 | * 基本链路测试 18 | * 19 | * @return 20 | */ 21 | public String test(); 22 | 23 | /** 24 | * 自定义业务类型测试 25 | * 26 | * @param userId 27 | * @return 28 | */ 29 | public User queryUserById(int userId); 30 | 31 | /** 32 | * 异常测试 33 | * 34 | * @throws IOException 35 | */ 36 | public Object exceptionTest() throws RpcException; 37 | 38 | /** 39 | * 上下文测试,透明传输数据 40 | */ 41 | public Map rpcContextTest(); 42 | 43 | /** 44 | * 超时测试 45 | */ 46 | public boolean timeoutTest(); 47 | 48 | /** 49 | * hook测试 50 | * @return 51 | */ 52 | public Map getMap(); 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/edu/ouc/rpc/demo/service/UserServiceListener.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.demo.service; 2 | 3 | 4 | import java.util.concurrent.CountDownLatch; 5 | import java.util.concurrent.TimeUnit; 6 | import edu.ouc.rpc.async.ResponseCallbackListener; 7 | 8 | 9 | public class UserServiceListener implements ResponseCallbackListener { 10 | private CountDownLatch latch = new CountDownLatch(1); 11 | 12 | private Object response; 13 | 14 | public Object getResponse() throws InterruptedException { 15 | latch.await(10, TimeUnit.SECONDS); 16 | if(response == null) 17 | throw new RuntimeException("The response doesn't come back."); 18 | return response; 19 | } 20 | @Override 21 | public void onResponse(Object response) { 22 | System.out.println("This method is call when response arrived"); 23 | this.response = response; 24 | latch.countDown(); 25 | } 26 | 27 | @Override 28 | public void onTimeout() { 29 | throw new RuntimeException("This call has taken time more than timeout value"); 30 | } 31 | 32 | @Override 33 | public void onException(Exception e) { 34 | throw new RuntimeException(e); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/edu/ouc/rpc/demo/service/User.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.demo.service; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * 测试用的自定义业务类型 8 | * 9 | * @author wqx 10 | * 11 | */ 12 | public class User implements java.io.Serializable{ 13 | 14 | private static final long serialVersionUID = 493399440916323966L; 15 | 16 | private Integer id; 17 | 18 | private String name; 19 | 20 | private List childs; 21 | 22 | 23 | public void addChild(User child){ 24 | if(childs == null){ 25 | childs = new ArrayList(); 26 | } 27 | childs.add(child); 28 | } 29 | public User(int id, String name){ 30 | this.id = id; 31 | this.name = name; 32 | } 33 | public User(int id, String name, List childs){ 34 | this.id = id; 35 | this.name = name; 36 | this.childs = childs; 37 | } 38 | 39 | public Integer getId() { 40 | return id; 41 | } 42 | 43 | public String getName() { 44 | return name; 45 | } 46 | 47 | public List getChilds() { 48 | return childs; 49 | } 50 | 51 | public void setId(Integer id) { 52 | this.id = id; 53 | } 54 | 55 | public void setName(String name) { 56 | this.name = name; 57 | } 58 | 59 | public void setChilds(List childs) { 60 | this.childs = childs; 61 | } 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/edu/ouc/rpc/async/ResponseFuture.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.async; 2 | 3 | import java.util.concurrent.ExecutionException; 4 | import java.util.concurrent.Future; 5 | import java.util.concurrent.TimeUnit; 6 | import java.util.concurrent.TimeoutException; 7 | 8 | import edu.ouc.rpc.model.RpcResponse; 9 | 10 | 11 | public class ResponseFuture { 12 | public static ThreadLocal> futureThreadLocal = new ThreadLocal>(); 13 | 14 | public static Object getResponse(long timeout) throws InterruptedException { 15 | if (null == futureThreadLocal.get()) { 16 | throw new RuntimeException("Thread [" + Thread.currentThread() + "] have not set the response future!"); 17 | } 18 | 19 | try { 20 | RpcResponse response =futureThreadLocal.get().get(timeout, TimeUnit.MILLISECONDS); 21 | if (response.isError()) { 22 | throw new RuntimeException(response.getErrorMsg()); 23 | } 24 | return response.getResponseBody(); 25 | } catch (ExecutionException e) { 26 | throw new RuntimeException(e); 27 | } catch (TimeoutException e) { 28 | throw new RuntimeException("Time out", e); 29 | } 30 | } 31 | 32 | public static void setFuture(Future future){ 33 | futureThreadLocal.set(future); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | org.topgunviper 6 | learn_rpc 7 | 0.0.1-SNAPSHOT 8 | 9 | 10 | UTF-8 11 | 12 | 13 | 14 | 15 | io.netty 16 | netty-all 17 | 4.0.23.Final 18 | 19 | 20 | junit 21 | junit 22 | 4.12 23 | 24 | test 25 | 26 | 27 | de.ruedigermoeller 28 | fst 29 | 1.58 30 | 31 | 32 | com.alibaba 33 | fastjson 34 | 1.2.3 35 | 36 | 37 | com.google.protobuf 38 | protobuf-java 39 | 2.5.0 40 | 41 | 42 | commons-collections 43 | commons-collections 44 | 3.2 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/test/java/edu/ouc/rpc/demo/service/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.demo.service; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import edu.ouc.rpc.context.RpcContext; 7 | import edu.ouc.rpc.model.RpcException; 8 | 9 | /** 10 | * 测试业务接口实现类 11 | * 12 | * @author wqx 13 | * 14 | */ 15 | public class UserServiceImpl implements UserService { 16 | 17 | public String test() { 18 | return "hello client, this is rpc server."; 19 | } 20 | 21 | public User queryUserById(int userId) { 22 | User parent = new User(100,"小明爸爸"); 23 | User child = new User(101,"小明同学"); 24 | parent.addChild(child); 25 | return parent; 26 | } 27 | 28 | public Object exceptionTest() throws RpcException { 29 | throw new RpcException("exception occur in server!!!"); 30 | } 31 | 32 | @Override 33 | public Map rpcContextTest() { 34 | Map map = new HashMap(); 35 | map.put("server","hahaha"); 36 | if(RpcContext.getAttributes() != null ) 37 | map.putAll(RpcContext.getAttributes()); 38 | return map; 39 | } 40 | 41 | @Override 42 | public boolean timeoutTest() { 43 | try { 44 | //模拟长时间执行 45 | Thread.sleep(10 * 1000); 46 | } catch (InterruptedException e) {} 47 | return true; 48 | } 49 | 50 | @Override 51 | public Map getMap() { 52 | Map newMap = new HashMap(); 53 | newMap.put("name","getMap"); 54 | newMap.putAll(RpcContext.getAttributes()); 55 | return newMap; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/edu/ouc/rpc/model/RpcRequest.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.Map; 5 | 6 | 7 | /** 8 | * 封装请求参数 9 | * 10 | * @author wqx 11 | * 12 | */ 13 | public class RpcRequest implements Serializable 14 | { 15 | /** 16 | * 17 | */ 18 | private static final long serialVersionUID = -7102839100899303105L; 19 | 20 | //接口名 21 | private String interfaceName; 22 | 23 | //方法名 24 | private String methodName; 25 | 26 | //参数类型 27 | private Class[] parameterTypes; 28 | 29 | //参数列表 30 | private Object[] args; 31 | 32 | private Map context; 33 | 34 | public RpcRequest(String interfaceName, String methodName,Class[] parameterTypes,Object[] args, Map context) 35 | { 36 | this.interfaceName = interfaceName; 37 | this.methodName = methodName; 38 | this.parameterTypes = parameterTypes; 39 | this.args = args; 40 | this.context = context; 41 | } 42 | 43 | public String getInterfaceName() { 44 | return interfaceName; 45 | } 46 | 47 | public void setInterfaceName(String interfaceName) { 48 | this.interfaceName = interfaceName; 49 | } 50 | 51 | public String getMethodName() { 52 | return methodName; 53 | } 54 | 55 | public void setMethodName(String methodName) { 56 | this.methodName = methodName; 57 | } 58 | 59 | public Class[] getParameterTypes() { 60 | return parameterTypes; 61 | } 62 | 63 | public void setParameterTypes(Class[] parameterTypes) { 64 | this.parameterTypes = parameterTypes; 65 | } 66 | 67 | public Object[] getArgs() { 68 | return args; 69 | } 70 | 71 | public void setArgs(Object[] args) { 72 | this.args = args; 73 | } 74 | 75 | public Map getContext() { 76 | return context; 77 | } 78 | 79 | public void setContext(Map context) { 80 | this.context = context; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /resource/RPC-Framework: -------------------------------------------------------------------------------- 1 | 2 | 一个简单的RPC框架 3 | 4 | RPC(Remote Procedure Call )——远程过程调用,它是一种通过网络从远程计算机程序上请求服务, 5 | 而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。 6 | 在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。 7 | 8 | 框架——让编程人员便捷地使用框架所提供的功能,由于RPC的特性,聚焦于应用的分布式服务化开发, 9 | 所以成为一个对开发人员无感知的接口代理,显然是RPC框架优秀的设计。 10 | 题目要求 11 | 12 | 1.要成为框架:对于框架的使用者,隐藏RPC实现。 13 | 14 | 2.网络模块可以自己编写,如果要使用IO框架,要求使用netty-4.0.23.Final。 15 | 16 | 3.支持异步调用,提供future、callback的能力。 17 | 18 | 4.能够传输基本类型、自定义业务类型、异常类型(要在客户端抛出)。 19 | 20 | 5.要处理超时场景,服务端处理时间较长时,客户端在指定时间内跳出本次调用。 21 | 22 | 6.提供RPC上下文,客户端可以透传数据给服务端。 23 | 24 | 7.提供Hook,让开发人员进行RPC层面的AOP。 25 | 26 | 注:为了降低第一题的难度,RPC框架不需要注册中心,客户端识别-DSIP的JVM参数来获取服务端IP。 27 | 衡量标准 28 | 29 | 满足所有要求。 性能测试。 30 | 目录结构与打包规范 31 | 32 | 测试时会运行rpc-use-demo中的测试用例,测试的demo包由测试工具做好。 33 | 34 | 参赛者必须以com.alibaba.middleware.race.rpc.api.impl.RpcConsumerImpl为全类名,继承com.alibaba.middleware.race.rpc.api.RpcConsumer,并覆写所有的public方法。 35 | 36 | 参赛者必须以com.alibaba.middleware.race.rpc.api.impl.RpcProviderImpl为全类名,继承com.alibaba.middleware.race.rpc.api.RpcProvider,并覆写所有的public方法。 37 | 38 | 参赛者依赖公共maven中心库上的三方包,即可看到一个示例的demo,按照对应的包名,在自己的工程中建立对应的类(包名、类名一致)。 39 | 三方库里的代码起到提示的作用,可以作为参考,不要在最终的pom中依赖。 40 | 41 | 42 | 所以最终参赛者需要打出一个rpc-api的jar包,供测试工程调用。 (注意,参考完rpc-api的示例后,请从pom依赖中将其删除,避免依赖冲突) 43 | 44 | 测试Demo工程请参考Taocode SVN上的代码。 45 | 示例Api pom依赖 46 | 47 | 48 | 49 | com.alibaba.middleware.race 50 | 51 | rpc-api 52 | 53 | 1.0 54 | 55 | 56 | 测试用例:http://code.taobao.org/p/race/src/trunk/rpc-framework/rpc-use-demo/src/main/java/com/alibaba/middleware/race/rpc/demo/test/ 57 | 58 | 59 | /********************************************************************/ 60 | 61 | 我要做什么? 62 | 63 | 题干主要提供了两样东西:rpc-api, test-demo 64 | rpc-api是一个空实现例子,参赛选手需要从Maven中心仓库将这个空实现的源代码下载下来,然后建立一个新项目,将rpc-api中题干中要求实现的类(com.alibaba.middleware.race.rpc.api.impl.RpcConsumerImpl,com.alibaba.middleware.race.rpc.api.impl.RpcProviderImpl)进行实现。rpc-api中提供的代码仅仅作为参考,不过其中的Api都需要在自己的工程对应的类里实现。 65 | 组委会测试的方式是通过选手提供的build.sh脚本,将选手实现的rpc-api打包成一个jar然后得到选手实现的类进行测试。 66 | 67 | 68 | test-demo是做什么的? 69 | 70 | test-demo是测试用例,下载下来时test-demo默认依赖的是Maven中心仓库中的rpc-api. 71 | 因为rpc-api是空实现,因此默认test-demo是跑不通的. 72 | 选手下载了rpc-api的源码后,自己建立了工程并且实现了需要实现的方法后,通过"一定"的方法使得test-demo依赖自己实现的rpc-api从而可以测试自己的代码。 73 | 原则上来说test-demo与题干无关。 74 | 75 | 那么我到底要做什么? 76 | 77 | 通过Maven把rpc-api的源码下载下来,使用"一定方法"导入到IDE内,然后把代码中题干要求完成的函数实现。 78 | 然后从SVN上把test-demo的代码下载下来,使用"一定方法"把test-demo的依赖改成自己改写过的rpc-api,然后进行测试。 79 | 测试通过后,编写build.sh脚本用来打包自己的rpc-api实现。(原则上如果你不需要测试的话,你完全可以不需要test-demo就可以完成rpc这道题目) 80 | -------------------------------------------------------------------------------- /src/main/java/edu/ouc/rpc/interceptor/DefaultMethodInvocation.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.interceptor; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.List; 5 | 6 | import edu.ouc.rpc.RpcConsumer; 7 | import edu.ouc.rpc.context.RpcContext; 8 | import edu.ouc.rpc.model.RpcRequest; 9 | 10 | /** 11 | * MethodInvocation��Ĭ��ʵ�� 12 | * 13 | * @author wqx 14 | * 15 | */ 16 | public class DefaultMethodInvocation implements RpcMethodInvocation { 17 | 18 | private Object target; 19 | private Object proxy; 20 | private Method method; 21 | private Object[] parameters; 22 | 23 | private boolean isRpcInvocation; 24 | 25 | //拦截器 26 | private List interceptors; 27 | 28 | //当前Interceptor索引值,范围:0-interceptors.size()-1 29 | private int currentIndex = -1; 30 | 31 | public DefaultMethodInvocation(Object target,Object proxy,Method method,Object[] parameters, List interceptors, boolean isRpcInvocation){ 32 | this.target = target; 33 | this.proxy = proxy; 34 | this.method = method; 35 | this.parameters = parameters; 36 | this.interceptors = interceptors; 37 | this.isRpcInvocation = isRpcInvocation; 38 | } 39 | 40 | @Override 41 | public Object executeNext() throws Exception { 42 | if(this.currentIndex == this.interceptors.size() - 1){ 43 | if(this.isRpcInvocation){ 44 | RpcRequest request = new RpcRequest(target.getClass().getName(), method.getName(),method.getParameterTypes(),parameters 45 | ,RpcContext.getAttributes()); 46 | return ((RpcConsumer)target).sendRequest(request); 47 | }else{ 48 | method.setAccessible(true); 49 | return method.invoke(target, parameters); 50 | } 51 | } 52 | Object interceptor = this.interceptors.get(++this.currentIndex); 53 | return ((Interceptor)interceptor).intercept(this); 54 | } 55 | 56 | //getter setter 57 | public Object getTarget() { 58 | return target; 59 | } 60 | public Object getProxy() { 61 | return proxy; 62 | } 63 | @Override 64 | public Method getMethod() { 65 | return method; 66 | } 67 | @Override 68 | public Object[] getParameters() { 69 | return parameters; 70 | } 71 | public List getInterceptors() { 72 | return interceptors; 73 | } 74 | public int getCurrentIndex() { 75 | return currentIndex; 76 | } 77 | public void setTarget(Object target) { 78 | this.target = target; 79 | } 80 | public void setProxy(Object proxy) { 81 | this.proxy = proxy; 82 | } 83 | public void setMethod(Method method) { 84 | this.method = method; 85 | } 86 | public void setParameters(Object[] parameters) { 87 | this.parameters = parameters; 88 | } 89 | public void setInterceptors(List interceptors) { 90 | this.interceptors = interceptors; 91 | } 92 | public void setCurrentIndex(int currentIndex) { 93 | this.currentIndex = currentIndex; 94 | } 95 | 96 | @Override 97 | public boolean isRpcInvocation() { 98 | // TODO Auto-generated method stub 99 | return isRpcInvocation; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/edu/ouc/rpc/interceptor/InterceptorChain.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.interceptor; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.HashSet; 6 | import java.util.List; 7 | import java.util.NoSuchElementException; 8 | import java.util.Set; 9 | 10 | import org.apache.commons.collections.CollectionUtils; 11 | 12 | public class InterceptorChain { 13 | 14 | private List interceptors; 15 | 16 | private Set registeredNames; 17 | 18 | public InterceptorChain(){ 19 | interceptors = new ArrayList(); 20 | registeredNames = new HashSet(); 21 | } 22 | 23 | public void addLast(String name, Interceptor interceptor){ 24 | synchronized(this){ 25 | checkDuplicateName(name); 26 | Entry entry = new Entry(name,interceptor); 27 | register(interceptors.size(), entry); 28 | } 29 | } 30 | public void addFirst(String name, Interceptor interceptor){ 31 | synchronized(this){ 32 | checkDuplicateName(name); 33 | Entry entry = new Entry(name,interceptor); 34 | register(0, entry); 35 | } 36 | } 37 | 38 | public void addBefore(String baseName, String name, Interceptor interceptor){ 39 | synchronized(this){ 40 | checkDuplicateName(name); 41 | int index = getInterceptorIndex(baseName); 42 | if(index == -1) 43 | throw new NoSuchElementException(baseName); 44 | Entry entry = new Entry(name,interceptor); 45 | register(index, entry); 46 | } 47 | } 48 | 49 | public void addAfter(String baseName, String name, Interceptor interceptor){ 50 | synchronized(this){ 51 | checkDuplicateName(name); 52 | int index = getInterceptorIndex(baseName); 53 | if(index == -1) 54 | throw new NoSuchElementException(baseName); 55 | Entry entry = new Entry(name,interceptor); 56 | register(index+1, entry); 57 | } 58 | } 59 | 60 | private int getInterceptorIndex(String name) { 61 | List interceptors = this.interceptors; 62 | for(int i = 0; i < interceptors.size(); i++){ 63 | if(interceptors.get(i).name.equals(name)){ 64 | return i; 65 | } 66 | } 67 | return -1; 68 | } 69 | private void register(int index, Entry entry){ 70 | interceptors.add(index, entry); 71 | registeredNames.add(entry.name); 72 | } 73 | private void checkDuplicateName(String name) { 74 | if (registeredNames.contains(name)) { 75 | throw new IllegalArgumentException("Duplicate interceptor name: " + name); 76 | } 77 | } 78 | @SuppressWarnings("unchecked") 79 | public List getInterceptors() { 80 | if(!CollectionUtils.isEmpty(this.interceptors)){ 81 | List list = new ArrayList<>(this.interceptors.size()); 82 | for(Entry entry: this.interceptors){ 83 | list.add(entry.interceptor); 84 | } 85 | return Collections.unmodifiableList(list); 86 | }else{ 87 | return (List) CollectionUtils.EMPTY_COLLECTION; 88 | } 89 | } 90 | static class Entry{ 91 | String name; 92 | Interceptor interceptor; 93 | 94 | public Entry(String name, Interceptor interceptor) { 95 | super(); 96 | this.name = name; 97 | this.interceptor = interceptor; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/edu/ouc/rpc/RpcProvider.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc; 2 | 3 | import java.io.IOException; 4 | import java.io.ObjectInputStream; 5 | import java.io.ObjectOutputStream; 6 | import java.lang.reflect.Method; 7 | import java.net.ServerSocket; 8 | import java.net.Socket; 9 | import java.util.List; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | 13 | import org.apache.commons.collections.CollectionUtils; 14 | 15 | import edu.ouc.rpc.context.RpcContext; 16 | import edu.ouc.rpc.interceptor.DefaultMethodInvocation; 17 | import edu.ouc.rpc.interceptor.Interceptor; 18 | import edu.ouc.rpc.interceptor.InterceptorChain; 19 | import edu.ouc.rpc.interceptor.MethodInvocation; 20 | import edu.ouc.rpc.model.RpcRequest; 21 | import edu.ouc.rpc.model.RpcResponse; 22 | 23 | public final class RpcProvider { 24 | 25 | 26 | public final static InterceptorChain interceptorChain = new InterceptorChain(); 27 | 28 | private static int nThreads = Runtime.getRuntime().availableProcessors() * 2; 29 | private static ExecutorService handlerPool = Executors.newFixedThreadPool(nThreads); 30 | 31 | public static void publish(final Object service, final int port) throws IOException{ 32 | if (service == null) 33 | throw new IllegalArgumentException("service can not be null."); 34 | 35 | ServerSocket server = new ServerSocket(port); 36 | System.out.println("server started!!!"); 37 | while(true){ 38 | Socket socket = server.accept();//监听请求--阻塞 39 | //异步处理 40 | handlerPool.submit(new Handler(service,socket,interceptorChain.getInterceptors())); 41 | } 42 | } 43 | static class Handler implements Runnable{ 44 | 45 | private Object service; 46 | 47 | private Socket socket; 48 | 49 | List interceptors; 50 | 51 | public Handler(Object service,Socket socket,List interceptors){ 52 | this.service = service; 53 | this.socket = socket; 54 | this.interceptors = interceptors; 55 | } 56 | public void run() { 57 | try{ 58 | ObjectInputStream in = null; 59 | ObjectOutputStream out = null; 60 | RpcResponse response = new RpcResponse(); 61 | try { 62 | in = new ObjectInputStream(socket.getInputStream()); 63 | out = new ObjectOutputStream(socket.getOutputStream()); 64 | 65 | Object req = in.readObject(); 66 | if(req instanceof RpcRequest){ 67 | RpcRequest rpcRequest = (RpcRequest)req; 68 | //关联客户端传来的上下文 69 | RpcContext.context.set(rpcRequest.getContext()); 70 | Method method = service.getClass().getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes()); 71 | Object retVal = null; 72 | 73 | MethodInvocation methodInvocation; 74 | List chain = interceptorChain.getInterceptors(); 75 | if(!CollectionUtils.isEmpty(chain)){ 76 | //执行拦截器链 77 | methodInvocation = new DefaultMethodInvocation(service,null,method,rpcRequest.getArgs(),chain, false); 78 | retVal = methodInvocation.executeNext(); 79 | }else{ 80 | retVal = method.invoke(service, rpcRequest.getArgs()); 81 | } 82 | response.setResponseBody(retVal); 83 | out.writeObject(response); 84 | } 85 | } catch (Exception e) { 86 | response.setErrorMsg(e.getMessage()); 87 | response.setResponseBody(e); 88 | out.writeObject(response); 89 | }finally{ 90 | in.close(); 91 | out.close(); 92 | } 93 | }catch(Exception e){} 94 | } 95 | } 96 | public InterceptorChain getInterceptorChain() { 97 | return interceptorChain; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rpc-race 2 | 一个简单的RPC框架 3 | 4 | RPC(Remote Procedure Call )——远程过程调用,它是一种通过网络从远程计算机程序上请求服务, 5 | 而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。 6 | 在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。 7 | 8 | 基本要求来自于:阿里巴巴中间15年中间件性能挑战赛,要求如下: 9 | 10 | 1.要成为框架:对于框架的使用者,隐藏RPC实现。 11 | 12 | 2.网络模块可以自己编写,如果要使用IO框架,要求使用netty-4.0.23.Final。 13 | 14 | 3.能够传输基本类型、自定义业务类型、异常类型(要在客户端抛出)。 15 | 16 | 4.支持异步调用,提供future、callback的能力。 17 | 18 | 5.要处理超时场景,服务端处理时间较长时,客户端在指定时间内跳出本次调用。 19 | 20 | 6.提供RPC上下文,客户端可以透传数据给服务端。 21 | 22 | 7.提供Hook,让开发人员进行RPC层面的AOP。 23 | 24 | 25 | #### 传输基本类型、自定义业务类型、异常类型(要在客户端抛出) 26 | 测试用例: 27 | ``` 28 | // client端 29 | public class ClientTest { 30 | 31 | private static String host = "127.0.0.1"; 32 | private static int port = 8888; 33 | 34 | public static void main(String[] args) { 35 | 36 | UserService userService = (UserService) RpcBuilder.buildRpcClient(UserService.class, host, port); 37 | 38 | Object msg = null; 39 | try{ 40 | msg = userService.test(); 41 | System.out.println("msg:" + msg); 42 | }catch(Exception e){ 43 | System.out.println("errorMsg:" + e); 44 | } 45 | } 46 | } 47 | //Server端 48 | public class ServerTest { 49 | 50 | private static int port = 8888; 51 | 52 | public static void main(String[] args) throws IOException { 53 | UserService userService = new UserServiceImpl(); 54 | RpcBuilder.buildRpcServer(userService,port); 55 | } 56 | } 57 | 测试用的业务接口UserService: 58 | /** 59 | * 测试用业务接口 60 | * 61 | * @author wqx 62 | * 63 | */ 64 | public interface UserService { 65 | 66 | /** 67 | * 基本链路测试 68 | * 69 | * @return 70 | */ 71 | public String test(); 72 | 73 | /** 74 | * 自定义业务类型测试 75 | * 76 | * @param userId 77 | * @return 78 | */ 79 | public User queryUserById(int userId); 80 | 81 | /** 82 | * 异常测试 83 | * 84 | * @throws IOException 85 | */ 86 | public Object exceptionTest() throws RpcException; 87 | } 88 | 89 | 业务实现UserServiceImpl类: 90 | 91 | /** 92 | * 测试业务接口实现类 93 | * 94 | * @author wqx 95 | * 96 | */ 97 | public class UserServiceImpl implements UserService { 98 | 99 | public String test() { 100 | return "hello client, this is rpc server."; 101 | } 102 | 103 | public User queryUserById(int userId) { 104 | User parent = new User(100,"小明爸爸"); 105 | User child = new User(101,"小明同学"); 106 | parent.addChild(child); 107 | return parent; 108 | } 109 | 110 | public Object exceptionTest() throws RpcException { 111 | throw new RpcException("exception occur in server!!!"); 112 | } 113 | } 114 | /** 115 | * 测试用的自定义业务类型 116 | * 117 | * @author wqx 118 | * 119 | */ 120 | public class User implements java.io.Serializable{ 121 | 122 | private static final long serialVersionUID = 493399440916323966L; 123 | 124 | private Integer id; 125 | 126 | private String name; 127 | 128 | private List childs; 129 | 130 | 131 | public void addChild(User child){ 132 | if(childs == null){ 133 | childs = new ArrayList(); 134 | } 135 | childs.add(child); 136 | } 137 | //。。。getter setter 138 | } 139 | ``` 140 | 141 | 测试test方法:预期输出: 142 | ``` 143 | msg : hello client, this is rpc server. 144 | ``` 145 | 测试exceptionTest方法: 146 | 147 | ``` 148 | msg = userService.exceptionTest(); 149 | 输出: 150 | errorMsg:edu.ouc.rpc.RpcException: exception occur in server!!! 151 | ``` 152 | 测试queryUserById方法: 153 | ``` 154 | msg = userService.queryUserById(0); 155 | if(msg instanceof User){ 156 | System.out.println("parent:" + ((User)msg).getName()); 157 | System.out.println("child:" + ((User)msg).getChilds().get(0).getName()); 158 | } 159 | 输出: 160 | parent:小明爸爸 161 | child:小明同学 162 | ``` 163 | -------------------------------------------------------------------------------- /src/test/java/edu/ouc/rpc/demo/test/ClientTest.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc.demo.test; 2 | 3 | 4 | import java.util.Map; 5 | 6 | import org.junit.Assert; 7 | import org.junit.Ignore; 8 | import org.junit.Test; 9 | 10 | import edu.ouc.rpc.RpcConsumer; 11 | import edu.ouc.rpc.async.ResponseFuture; 12 | import edu.ouc.rpc.context.RpcContext; 13 | import edu.ouc.rpc.demo.service.User; 14 | import edu.ouc.rpc.demo.service.UserConsumerHook; 15 | import edu.ouc.rpc.demo.service.UserService; 16 | import edu.ouc.rpc.demo.service.UserServiceListener; 17 | import edu.ouc.rpc.interceptor.TimeInterceptor; 18 | 19 | public class ClientTest { 20 | 21 | 22 | private static String host = "127.0.0.1"; 23 | private static int port = 8888; 24 | 25 | private static UserService userService; 26 | 27 | private static int TIMEOUT = 3000; 28 | 29 | private static RpcConsumer consumer; 30 | static { 31 | consumer = new RpcConsumer(); 32 | 33 | consumer.getInterceptorChain().addLast("time", new TimeInterceptor()); 34 | 35 | userService = (UserService)consumer.targetHostPort(host, port) 36 | .interfaceClass(UserService.class) 37 | .timeout(TIMEOUT) 38 | //.hook(new UserConsumerHook()) 39 | .newProxy(); 40 | 41 | } 42 | 43 | /** 44 | * 基本调用链路测试 45 | */ 46 | @Ignore 47 | @Test 48 | public void test() { 49 | Assert.assertEquals(userService.test(), "hello client, this is rpc server."); 50 | } 51 | /** 52 | * 测试异常情况 53 | */ 54 | @Ignore 55 | @Test 56 | public void testException(){ 57 | Object msg = null; 58 | try{ 59 | msg = userService.exceptionTest(); 60 | }catch(Exception e){ 61 | Assert.assertEquals(e.getMessage(),"exception occur in server!!!"); 62 | } 63 | } 64 | /** 65 | * 测试自定义业务类 66 | */ 67 | @Ignore 68 | @Test 69 | public void testQueryUser() { 70 | Object msg = null; 71 | try{ 72 | msg = userService.queryUserById(0); 73 | Assert.assertEquals(((User)msg).getName(),"小明爸爸"); 74 | Assert.assertEquals(((User)msg).getChilds().get(0).getName(),"小明同学"); 75 | }catch(Exception e){ 76 | } 77 | } 78 | @Ignore 79 | @Test 80 | public void testRpcContext(){ 81 | RpcContext.addAttribute("client","huhuhu"); 82 | Map res = userService.rpcContextTest(); 83 | Assert.assertEquals(res.get("server"),"hahaha"); 84 | Assert.assertEquals(res.get("client"),"huhuhu"); 85 | } 86 | @Ignore 87 | @Test 88 | public void testAsyncCall(){ 89 | consumer.asynCall("test"); 90 | //立即返回 91 | String nullValue = userService.test(); 92 | System.out.println(nullValue); 93 | Assert.assertEquals(null, nullValue); 94 | try { 95 | String result = (String) ResponseFuture.getResponse(TIMEOUT); 96 | Assert.assertEquals("hello client, this is rpc server.", result); 97 | } catch (InterruptedException e) { 98 | e.printStackTrace(); 99 | } finally { 100 | consumer.cancelAsyn("test"); 101 | } 102 | } 103 | @Ignore 104 | @Test 105 | public void testCallback() { 106 | UserServiceListener listener = new UserServiceListener(); 107 | consumer.asynCall("test", listener); 108 | String nullStr = userService.test(); 109 | Assert.assertEquals(null, nullStr); 110 | try { 111 | String str = (String)listener.getResponse(); 112 | Assert.assertEquals("hello client, this is rpc server.", str); 113 | } catch (InterruptedException e) { 114 | } 115 | } 116 | @Ignore 117 | @Test 118 | public void timeoutTest(){ 119 | long beginTime = System.currentTimeMillis(); 120 | try { 121 | boolean result = userService.timeoutTest(); 122 | } catch (Exception e) { 123 | long period = System.currentTimeMillis() - beginTime; 124 | System.out.println("period:" + period); 125 | Assert.assertTrue(period < 3100); 126 | } 127 | } 128 | @Ignore 129 | @Test 130 | public void testConsumerHook() { 131 | Map resultMap = userService.getMap(); 132 | Assert.assertTrue(resultMap.containsKey("hook key")); 133 | Assert.assertTrue(resultMap.containsValue("this is pass by hook")); 134 | } 135 | 136 | @Test 137 | public void testInterceptorChain(){ 138 | try{ 139 | Map resultMap = userService.getMap(); 140 | }catch(Exception e){ 141 | System.out.println(e); 142 | Assert.assertEquals("method getMap is not allowed!", e.getMessage()); 143 | } 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/edu/ouc/rpc/RpcConsumer.java: -------------------------------------------------------------------------------- 1 | package edu.ouc.rpc; 2 | 3 | import java.io.IOException; 4 | import java.io.ObjectInputStream; 5 | import java.io.ObjectOutputStream; 6 | import java.lang.reflect.InvocationHandler; 7 | import java.lang.reflect.Method; 8 | import java.lang.reflect.Proxy; 9 | import java.net.Socket; 10 | import java.util.LinkedHashSet; 11 | import java.util.List; 12 | import java.util.Set; 13 | import java.util.concurrent.Callable; 14 | import java.util.concurrent.ExecutorService; 15 | import java.util.concurrent.Executors; 16 | import java.util.concurrent.Future; 17 | import java.util.concurrent.TimeUnit; 18 | import java.util.concurrent.TimeoutException; 19 | 20 | import org.apache.commons.collections.CollectionUtils; 21 | 22 | import edu.ouc.rpc.aop.ConsumerHook; 23 | import edu.ouc.rpc.async.ResponseCallbackListener; 24 | import edu.ouc.rpc.async.ResponseFuture; 25 | import edu.ouc.rpc.context.RpcContext; 26 | import edu.ouc.rpc.interceptor.DefaultMethodInvocation; 27 | import edu.ouc.rpc.interceptor.Interceptor; 28 | import edu.ouc.rpc.interceptor.InterceptorChain; 29 | import edu.ouc.rpc.interceptor.MethodInvocation; 30 | import edu.ouc.rpc.model.RpcException; 31 | import edu.ouc.rpc.model.RpcRequest; 32 | import edu.ouc.rpc.model.RpcResponse; 33 | 34 | public final class RpcConsumer implements InvocationHandler{ 35 | 36 | private String host; 37 | 38 | private int port; 39 | 40 | private Class interfaceClass; 41 | 42 | private int TIMEOUT; 43 | 44 | private InterceptorChain interceptorChain = new InterceptorChain(); 45 | 46 | private static ConsumerHook DEFAULT_HOOK; 47 | static{ 48 | DEFAULT_HOOK = new ConsumerHook(){ 49 | @Override 50 | public void before(RpcRequest request) { 51 | //NOOP 52 | } 53 | @Override 54 | public void after(RpcRequest request) { 55 | //NOOP 56 | } 57 | }; 58 | } 59 | //钩子 60 | private ConsumerHook hook = DEFAULT_HOOK; 61 | 62 | private static int nThreads = Runtime.getRuntime().availableProcessors() * 2; 63 | 64 | private static ExecutorService handlerPool = Executors.newFixedThreadPool(nThreads); 65 | 66 | /** 67 | * 存放当前线程正在执行的异步方法 68 | */ 69 | private static final ThreadLocal> asyncMethods = new ThreadLocal>(){ 70 | @Override 71 | public Set initialValue() 72 | { 73 | return new LinkedHashSet(); 74 | } 75 | }; 76 | public RpcConsumer targetHostPort(String host, int port){ 77 | this.host = host; 78 | this.port = port; 79 | return this; 80 | } 81 | public RpcConsumer interfaceClass(Class interfaceClass) { 82 | this.interfaceClass = interfaceClass; 83 | return this; 84 | } 85 | public RpcConsumer timeout(int timeout){ 86 | this.TIMEOUT = timeout; 87 | return this; 88 | } 89 | public RpcConsumer hook(ConsumerHook hook){ 90 | this.hook = hook; 91 | return this; 92 | } 93 | public InterceptorChain getInterceptorChain(){ 94 | return interceptorChain; 95 | } 96 | public Object newProxy(){ 97 | return Proxy.newProxyInstance(RpcConsumer.class.getClassLoader(), new Class[]{this.interfaceClass}, this); 98 | } 99 | 100 | /** 101 | * 异步方法调用 102 | * 103 | * @param methodName 104 | */ 105 | public void asynCall(String methodName) { 106 | asynCall(methodName, null); 107 | } 108 | 109 | /** 110 | * 异步方法,支持callback 111 | * 112 | * @param methodName 113 | * @param callbackListener 114 | */ 115 | public void asynCall(final String methodName, T callbackListener) { 116 | //记录异步方法调用 117 | asyncMethods.get().add(methodName); 118 | RpcRequest request = new RpcRequest(interfaceClass.getName(), methodName,null,null,RpcContext.getAttributes()); 119 | 120 | //构造并提交FutureTask异步任务 121 | Future f = null; 122 | try { 123 | f = doInvoke(request); 124 | } catch (Exception e) {} 125 | 126 | RpcResponse response; 127 | 128 | if(callbackListener != null){ 129 | try { 130 | //阻塞 131 | response = (RpcResponse) f.get(TIMEOUT,TimeUnit.MILLISECONDS); 132 | if(response.isError()){ 133 | //执行回调方法 134 | callbackListener.onException(new RpcException(response.getErrorMsg())); 135 | }else{ 136 | callbackListener.onResponse(response.getResponseBody()); 137 | } 138 | } catch(TimeoutException e){ 139 | callbackListener.onTimeout(); 140 | }catch (Exception e) {} 141 | }else{ 142 | //client端将从ResponseFuture中获取结果 143 | ResponseFuture.setFuture(f); 144 | } 145 | hook.after(request); 146 | } 147 | 148 | public void cancelAsyn(String methodName) { 149 | asyncMethods.get().remove(methodName); 150 | } 151 | 152 | /** 153 | * 拦截目标方法->序列化method对象->发起socket连接 154 | */ 155 | @Override 156 | public Object invoke(Object proxy, Method method, 157 | Object[] args) throws Throwable { 158 | //如果是异步方法,立即返回null 159 | if(asyncMethods.get().contains(method.getName())) return null; 160 | Object retVal = null; 161 | MethodInvocation methodInvocation; 162 | 163 | List chain = this.interceptorChain.getInterceptors(); 164 | 165 | if(!CollectionUtils.isEmpty(chain)){ 166 | //执行拦截器链 167 | methodInvocation = new DefaultMethodInvocation(this, null, method, args, chain, true); 168 | retVal = methodInvocation.executeNext(); 169 | }else{ 170 | retVal = sendRequest(new RpcRequest(interfaceClass.getName(),method.getName(), method.getParameterTypes(),args,RpcContext.getAttributes())); 171 | } 172 | return retVal; 173 | } 174 | public Object sendRequest(final RpcRequest request) throws TimeoutException{ 175 | Object retVal; 176 | 177 | RpcResponse rpcResp = null; 178 | try{ 179 | Future response = doInvoke(request); 180 | //获取异步结果 181 | rpcResp = (RpcResponse)response.get(TIMEOUT,TimeUnit.MILLISECONDS); 182 | }catch(TimeoutException e){ 183 | throw e; 184 | }catch(Exception e){} 185 | 186 | if(!rpcResp.isError()){ 187 | retVal = rpcResp.getResponseBody(); 188 | }else{ 189 | throw new RpcException(rpcResp.getErrorMsg()); 190 | } 191 | hook.after(request); 192 | return retVal; 193 | } 194 | private Future doInvoke(final RpcRequest request) throws IOException, ClassNotFoundException{ 195 | //插入钩子 196 | hook.before(request); 197 | //构造并提交FutureTask异步任务 198 | Future retVal = (Future) handlerPool.submit(new Callable(){ 199 | @Override 200 | public RpcResponse call() throws Exception { 201 | Object res = null; 202 | try{ 203 | //创建连接,获取输入输出流 204 | Socket socket = new Socket(host,port); 205 | try{ 206 | ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream()); 207 | ObjectInputStream in = new ObjectInputStream(socket.getInputStream()); 208 | try{ 209 | //发送 210 | out.writeObject(request); 211 | //接受server端的返回信息---阻塞 212 | res = in.readObject(); 213 | }finally{ 214 | out.close(); 215 | in.close(); 216 | } 217 | }finally{ 218 | socket.close(); 219 | } 220 | }catch(Exception e){ 221 | throw e; 222 | } 223 | return (RpcResponse)res; 224 | } 225 | }); 226 | return retVal; 227 | } 228 | } 229 | --------------------------------------------------------------------------------