基于Netty的RPC简单框架实现(二):RPC服务端

来源:互联网 发布:sql like 编辑:程序博客网 时间:2024/05/01 13:21

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 RpcRequestAtomicInteger 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 hereSystem.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 cliente.printStackTrace();} catch (SecurityException e) {e.printStackTrace();} catch (IllegalAccessException e) {// TODO return IllegalAccessException to cliente.printStackTrace();} catch (IllegalArgumentException e) {// TODO return IllegalArgumentException to cliente.printStackTrace();} catch (InvocationTargetException e) {// TODO return Exception to cliente.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 testMethod01beforeInvoke testMethod01beforeInvoke testMethod01beforeInvoke testMethod01Send 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 testMethod01afterInvoke testMethod01afterInvoke testMethod01beforeInvoke testMethod01beforeInvoke testMethod01afterInvoke testMethod01Send 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 testMethod01afterInvoke testMethod01beforeInvoke testMethod01afterInvoke testMethod01beforeInvoke testMethod01beforeInvoke testMethod01Send response id = 10 result = QWERTY back to client. Thread[pool-1-thread-1,5,main]afterInvoke testMethod01Send response id = 9 result = QWERTY back to client. Thread[pool-1-thread-4,5,main]afterInvoke testMethod01Send response id = 7 result = QWERTY back to client. Thread[pool-1-thread-2,5,main]afterInvoke testMethod01Send response id = 8 result = QWERTY back to client. Thread[pool-1-thread-3,5,main]afterInvoke testMethod01
可见,共有4个Handler线程在工作,并且都正确的调用了被请求的方法,设置的Hook也受到了正确的回调。


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 宝宝读幼儿园哭怎么办 幼儿上课爱讲话怎么办 孩子上课总是乱跑怎么办 孩子听不懂老师讲课怎么办 高一上课听不懂怎么办 上课学生纪律差怎么办 一年级学生认字少怎么办 孩子上课做不住怎么办 幼儿园小孩上课乱跑怎么办 孩子挑食不爱吃饭怎么办 幼儿园孩子不听指令怎么办 1岁宝宝多动症怎么办 3岁宝宝胆小怎么办 爱挑食的孩子怎么办 小孩上课经常发呆怎么办 小孩不爱吃饭挑食怎么办 一年级小孩学习不好怎么办 宝宝上课坐不住怎么办 八个月婴儿拉稀怎么办 八个月孕妇拉稀怎么办 孩子好动爱喊怎么办 八个月小孩发烧怎么办 孩子好动怎么办学龄前教育 小孩好动症该怎么办 小孩子好动症该怎么办 怀孕5个月胎死亡怎么办 小孩多动调皮怎么办 手心老是出汗是怎么办 孩子吃饭特别慢怎么办 小孩子老想睡觉怎么办 孩子下午上课犯困怎么办 小孩子有多动症该怎么办 初中写作业犯困怎么办 孩子晚上学习困怎么办 小孩子容易兴奋激动怎么办 中考时过度兴奋怎么办 小孩兴奋不睡觉怎么办 孩子突然反常不听话怎么办? 婴儿亢奋不睡觉怎么办 宝宝听力筛查没通过怎么办 7个多月宝宝缺钙怎么办