本节commit源码地址:11e4aca
服务端实现--反射调用
服务端如果收到请求就创建一个线程来处理调用,利用线程池创建线程,对多线程情况进行处理(Java线程池学习请戳:https://blog.csdn.net/suifeng3051/article/details/49443835)
public class RpcServer {
private final ExecutorService threadPool;
private static final Logger logger = LoggerFactory.getLogger(RpcServer.class);
public RpcServer(){
int corePoolSize = 5;
int maximumPoolSize = 50;
long keepAliveTime = 60;
/**
* 设置上限为100个线程的阻塞队列
*/
BlockingQueue<Runnable> workingQueue = new ArrayBlockingQueue<>(100);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
/**
* 创建线程池实例
*/
threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workingQueue, threadFactory);
}
}
利用ServerSocket监听与客户端发出请求的一致端口,连接到客户端Socket,循环接收请求。由于我们暂时是简化实现,服务端只提供一个接口的调用服务(此处服务具体来说就是HelloServiceImpl这个实现类进行的处理操作),将服务作为参数传入进行注册后直接使用即可。
public void register(Object service, int port){
try(ServerSocket serverSocket = new ServerSocket(port)){
logger.info("服务器正在启动……");
Socket socket;
//当未接收到连接请求时,accept()会一直阻塞
while ((socket = serverSocket.accept()) != null){
logger.info("客户端连接!IP:" + socket.getInetAddress());
threadPool.execute(new WorkerThread(socket, service));
}
}catch (IOException e){
logger.info("连接时有错误发生:" + e);
}
}
这里向工作线程WorkerThread传入了socket和服务实例service,WorkerThread实现了Runnable接口,用于接收来自客户端的RpcRequest对象,利用反射机制完成调用处理,生成RpcResponse对象,通过Socket返回给客户端。值得注意的是getClass()方法是获得调用该方法的对象的类,这里即HelloServiceImpl类,而不是创建对象时用的引用类HelloService;具体run()如下:
public void run() {
try (ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream())) {
RpcRequest rpcRequest = (RpcRequest)objectInputStream.readObject();
//利用反射原理找到远程所需调用的方法
Method method = service.getClass().getMethod(rpcRequest.getMethodName(), rpcRequest.getParamTypes());
//invoke(obj实例对象,obj可变参数)
Object returnObject = method.invoke(service, rpcRequest.getParameters());
objectOutputStream.writeObject(RpcResponse.success(returnObject));
objectOutputStream.flush();
}catch (IOException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e){
logger.info("调用或发送时有错误发生:" + e);
}
}
Demo测试
在服务端,我们已经实现了HelloService的实现类HelloServiceImpl,因此只需要创建一个RpcServer把这个实现类注册进去,启动服务端,监听请求进行处理即可。
public class TestServer {
public static void main(String[] args) {
HelloService helloService = new HelloServiceImpl();
RpcServer rpcServer = new RpcServer();
//注册HelloServiceImpl服务
rpcServer.register(helloService, 9000);
}
}
客户端方面,我们需要通过动态代理生成代理对象,并且调用接口方法,动态代理就会自动帮我们向服务端发送请求,返回结果的
public class TestClient {
public static void main(String[] args) {
//接口与代理对象之间的中介对象
RpcClientProxy proxy = new RpcClientProxy("127.0.0.1", 9000);
//创建代理对象
HelloService helloService = proxy.getProxy(HelloService.class);
//接口方法的参数对象
HelloObject object = new HelloObject(12, "This is test message");
//由动态代理可知,代理对象调用hello()实际会执行invoke()
String res = helloService.hello(object);
System.out.println(res);
}
}
先启动服务端,再启动客户端,如下输出:
服务端:
服务器正在启动...
客户端连接!IP:127.0.0.1
接收到:This is a message
客户端:
这是调用的返回值:id=12
第一阶段到此结束,假设了只有一个接口服务,客户端已知服务端地址,对RPC框架进行了简单实现,大致逻辑走向如图: