1.RPC服务端的实现思路
相对于客户端而言,服务端要简单不少。基本思想就是,创建RPC服务端的时候,创建一个RPC请求队列和一定数量的Handler线程。Handler线程都持有服务端提供服务的Interface的类类型和实际供方法调用的对象(实现了提供服务的Interface),各线程只需要不断从RPC请求队列中取出请求,然后用供方法调用的对象来调用所请求的方法,最后将调用的结果通过Netty发送回客户端即可。
2.RPC服务端的具体实现
(1).RpcRequest
在具体实现RPC服务端之前,先定义RpcRequest类。
package com.maigo.rpc.context;
public class RpcRequest
{
int id;
String methodName;
Object[] args;
public RpcRequest(int id, String methodName, Object[] args)
{
this.id = id;
this.methodName = methodName;
this.args = args;
}
public int getId()
{
return id;
}
public String getMethodName()
{
return methodName;
}
public Object[] getArgs()
{
return args;
}
}
RpcRequest表示了一个RPC调用请求。id用于区分多次不同的调用,methodName为请求调用的方法名,args为参数。
(2).RpcServerBuilder
RpcServerBuilder是创建RpcServer的工厂类
package com.maigo.rpc.server;
import com.maigo.rpc.aop.RpcInvokeHook;
public class RpcServerBuilder
{
private Class<?> interfaceClass;
private Object serviceProvider;
private int port;
private int threads;
private RpcInvokeHook rpcInvokeHook;
public static RpcServerBuilder create()
{
return new RpcServerBuilder();
}
/**
* set the interface to provide service
* @param interfaceClass
*/
public RpcServerBuilder serviceInterface(Class<?> interfaceClass)
{
this.interfaceClass = interfaceClass;
return this;
}
/**
* set the real object to provide service
*/
public RpcServerBuilder serviceProvider(Object serviceProvider)
{
this.serviceProvider = serviceProvider;
return this;
}
/**
* set the port to bind
*/
public RpcServerBuilder bind(int port)
{
this.port = port;
return this;
}
/**
* set the count of threads to handle request from client. (default availableProcessors)
*/
public RpcServerBuilder threads(int threadCount)
{
this.threads = threadCount;
return this;
}
/**
* set the hook of the method invoke in server
*/
public RpcServerBuilder hook(RpcInvokeHook rpcInvokeHook)
{
this.rpcInvokeHook = rpcInvokeHook;
return this;
}
public RpcServer build()
{
if(threads <= 0)
threads = Runtime.getRuntime().availableProcessors();
RpcServer rpcServer = new RpcServer(interfaceClass, serviceProvider, port,
threads, rpcInvokeHook);
return rpcServer;
}
}
API都很简单,create()创建工场,serviceInterface()设置服务接口,serviceProvider()设置供方法调用的实际对象,bind()设置绑定的端口号,threads()设置Handler线程的个数(默认为CPU核数),build()创建出RpcServer对象。
(3).RpcServer
package com.maigo.rpc.server;
import java.util.concurrent.atomic.AtomicInteger;
import com.maigo.rpc.aop.RpcInvokeHook;
import com.maigo.rpc.context.RpcRequest;
public class RpcServer
{
private Class<?> interfaceClass;
private Object serviceProvider;
private int port;
private int threads;
private RpcInvokeHook rpcInvokeHook;
private RpcServerRequestHandler rpcServerRequestHandler;
protected RpcServer(Class<?> interfaceClass, Object serviceProvider, int port, int threads,
RpcInvokeHook rpcInvokeHook)
{
this.interfaceClass = interfaceClass;
this.serviceProvider = serviceProvider;
this.port = port;
this.threads = threads;
this.rpcInvokeHook = rpcInvokeHook;
rpcServerRequestHandler = new RpcServerRequestHandler(interfaceClass,
serviceProvider, threads, rpcInvokeHook);
rpcServerRequestHandler.start();
}
public void start()
{
System.out.println("bind port:"+port + " success!");
//simulation for receive RpcRequest
AtomicInteger idGenerator = new AtomicInteger(0);
for(int i=0; i<10; i++)
{
rpcServerRequestHandler.addRequest(new RpcRequest(idGenerator.addAndGet(1),
"testMethod01", new Object[]{"qwerty"}));
}
}
public void stop()
{
//TODO add stop codes here
System.out.println("server stop success!");
}
}
RpcServer只提供了start()和stop()方法用于启动和停止RPC服务。由于启动和停止要涉及网络部分,现在先用打印输出代替。start()方法中还模拟了收到RpcRequest的情况,用于当前无网络连接的情况下测试。RpcServer的构造方法中创建了一个RpcServerRequestHandler,专门用于处理RpcRequest。
(4).RpcServerRequestHandler
专门用于处理RpcRequest的类
package com.maigo.rpc.server;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import com.maigo.rpc.aop.RpcInvokeHook;
import com.maigo.rpc.context.RpcRequest;
public class RpcServerRequestHandler
{
private Class<?> interfaceClass;
private Object serviceProvider;
private RpcInvokeHook rpcInvokeHook;
private int threads;
private ExecutorService threadPool;
private BlockingQueue<RpcRequest> requestQueue = new LinkedBlockingQueue<RpcRequest>();
public RpcServerRequestHandler(Class<?> interfaceClass, Object serviceProvider, int threads,
RpcInvokeHook rpcInvokeHook)
{
this.interfaceClass = interfaceClass;
this.serviceProvider = serviceProvider;
this.threads = threads;
this.rpcInvokeHook = rpcInvokeHook;
}
public void start()
{
threadPool = Executors.newFixedThreadPool(threads);
for(int i=0; i<threads; i++)
{
threadPool.execute(new RpcServerRequestHandleRunnable(interfaceClass,
serviceProvider, rpcInvokeHook, requestQueue));
}
}
public void addRequest(RpcRequest rpcRequest)
{
try
{
requestQueue.put(rpcRequest);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
在RpcServerRequestHandler的构造方法中,创建了1个大小为threads的线程池,并让其运行了threads个RpcServerRequestHandleRunnable。每个RpcServerRequestHandleRunnable持有相同的服务接口interfaceClass表示服务端提供哪些服务,相同的服务提供对象serviceProvider供实际方法调用,相同的请求队列requestQueue用于取出收到的方法调用请求。
(5).RpcServerRequestHandleRunnable
方法调用请求的实际执行者
package com.maigo.rpc.server;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.BlockingQueue;
import com.maigo.rpc.aop.RpcInvokeHook;
import com.maigo.rpc.context.RpcRequest;
public class RpcServerRequestHandleRunnable implements Runnable
{
private Class<?> interfaceClass;
private Object serviceProvider;
private RpcInvokeHook rpcInvokeHook;
private BlockingQueue<RpcRequest> requestQueue;
public RpcServerRequestHandleRunnable(Class<?> interfaceClass,
Object serviceProvider, RpcInvokeHook rpcInvokeHook,
BlockingQueue<RpcRequest> requestQueue)
{
this.interfaceClass = interfaceClass;
this.serviceProvider = serviceProvider;
this.rpcInvokeHook = rpcInvokeHook;
this.requestQueue = requestQueue;
}
public void run()
{
while(true)
{
try
{
RpcRequest rpcRequest = requestQueue.take();
String methodName = rpcRequest.getMethodName();
Object[] args = rpcRequest.getArgs();
int parameterCount = args.length;
Method method = null;
if(parameterCount > 0)
{
Class<?>[] parameterTypes = new Class[args.length];
for(int i=0; i<parameterCount; i++)
{
parameterTypes[i] = args[i].getClass();
}
method = interfaceClass.getMethod(methodName, parameterTypes);
}
else
{
method = interfaceClass.getMethod(methodName);
}
if(rpcInvokeHook != null)
rpcInvokeHook.beforeInvoke(methodName, args);
Object result = method.invoke(serviceProvider, args);
System.out.println("Send response id = " + rpcRequest.getId() + " result = " + result
+ " back to client. " + Thread.currentThread());
if(rpcInvokeHook != null)
rpcInvokeHook.afterInvoke(methodName, args);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
catch (NoSuchMethodException e)
{
// TODO return NoSuchMethodException to client
e.printStackTrace();
}
catch (SecurityException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
// TODO return IllegalAccessException to client
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
// TODO return IllegalArgumentException to client
e.printStackTrace();
}
catch (InvocationTargetException e)
{
// TODO return Exception to client
e.printStackTrace();
}
}
}
}
RpcServerRequestHandleRunnable不断地从请求队列requestQueue中取出方法调用请求RpcRequest,用serviceProvider调用请求的方法并向客户端返回调用结果。由于现在还未加入网络部分,向客户端返回结果暂时先用打印输出代替。在方法实际调用的前后,钩子Hook的回调得到了执行。
3.测试
RpcServer中的start()方法模拟了收到10个调用请求的情况
TestInterface testInterface = new TestInterface()
{
public String testMethod01(String string)
{
return string.toUpperCase();
}
};
RpcInvokeHook hook = new RpcInvokeHook()
{
public void beforeInvoke(String methodName, Object[] args)
{
System.out.println("beforeInvoke " + methodName);
}
public void afterInvoke(String methodName, Object[] args)
{
System.out.println("afterInvoke " + methodName);
}
};
RpcServer rpcServer = RpcServerBuilder.create()
.serviceInterface(TestInterface.class)
.serviceProvider(testInterface)
.threads(4)
.hook(hook)
.bind(3721)
.build();
rpcServer.start();
bind port:3721 success!
beforeInvoke testMethod01
beforeInvoke testMethod01
beforeInvoke testMethod01
beforeInvoke testMethod01
Send response id = 2 result = QWERTY back to client. Thread[pool-1-thread-2,5,main]
Send response id = 4 result = QWERTY back to client. Thread[pool-1-thread-4,5,main]
Send response id = 1 result = QWERTY back to client. Thread[pool-1-thread-1,5,main]
Send response id = 3 result = QWERTY back to client. Thread[pool-1-thread-3,5,main]
afterInvoke testMethod01
afterInvoke testMethod01
afterInvoke testMethod01
beforeInvoke testMethod01
beforeInvoke testMethod01
afterInvoke testMethod01
Send response id = 5 result = QWERTY back to client. Thread[pool-1-thread-1,5,main]
Send response id = 6 result = QWERTY back to client. Thread[pool-1-thread-4,5,main]
beforeInvoke testMethod01
afterInvoke testMethod01
beforeInvoke testMethod01
afterInvoke testMethod01
beforeInvoke testMethod01
beforeInvoke testMethod01
Send response id = 10 result = QWERTY back to client. Thread[pool-1-thread-1,5,main]
afterInvoke testMethod01
Send response id = 9 result = QWERTY back to client. Thread[pool-1-thread-4,5,main]
afterInvoke testMethod01
Send response id = 7 result = QWERTY back to client. Thread[pool-1-thread-2,5,main]
afterInvoke testMethod01
Send response id = 8 result = QWERTY back to client. Thread[pool-1-thread-3,5,main]
afterInvoke testMethod01
可见,共有4个Handler线程在工作,并且都正确的调用了被请求的方法,设置的Hook也受到了正确的回调。