RPC源码实现&解析 java
RPC源码实现&解析
写在前面:最近复习的时候,RPC协议不咋清楚,就去学习了下,看了博客还是不太懂,就跟着这一篇手动实现了下,在此记录下过程
实现过程
原理讲解:
- RPC的全称是Remote Producedure Call-->远程过程调用,通俗的来讲就是客户端调用服务端的方法,并获取返回的结果
- 如图
- 不通过http(在应用层,很慢),使用socket编程实现
- 然后服务端和客户端设计如下:
- 服务端:
- 注册中心,维护着注册的方法,需要提供方法名和需要的参数才能调用-->反射机制
- 服务器:产生一个监听的服务器,有请求打过来就从线程池里抓线程去执行(非阻塞式)
- 方法接口&实现类:服务器端需要有可能被调用的方法的具体实现
- 客户端
- 客户端实体(Socket实现,通过Scoket通信来实现参数传送以及服务器运行结果的获取)
- 方法接口:其实也不需要接口,服务器上维护的方法池是kv结构的,k的类型是String,所以只需要接口的名字;本文用类的反射机制直接获取Name(这里有疑问,需要进一步查证,因为存在一个调用的过程)-->//20221013 22:25更新,发现不行,设置代理类的时候需要接口类文件,但是实现类确实是不需要的
- 服务端:
具体实现代码
- 方法接口
package RPC;
public interface ServiceProducer {
String sendData(String data);
}
- 方法具体实现:
package RPC;
public class ServiceProducerImpl implements ServiceProducer{
@Override
public String sendData(String data) {
return "hello world:"+data;
}
}
- 客户端
package RPC;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;
public class RPCClient<T> {
/**
* <T> T T表示泛型类型,<T>表示声明该方法是泛型方法
* 本来类上面声明了<T> 方法上可以不用再声明,但是static方法不属于类的一部分,所以需要手动声明
* Class<?>表示未知类型的类
* @param serviceInterface
* @param addr
* @param <T>
* @return
*/
public static <T> T getRemoteProxyObj(final Class<?> serviceInterface,final InetSocketAddress addr){
//1.将本地的接口调用转换成JDK的动态代理,在动态代理中实现接口的远程调用
return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(),new Class<?>[]{serviceInterface},
new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = null;
ObjectOutputStream output = null;
ObjectInputStream input = null;
//2.创建Socket客户端,根据指定地址连接远程服务提供者
try {
socket = new Socket();
socket.connect(addr);//链接服务器
output = new ObjectOutputStream(socket.getOutputStream());//获取输出流
output.writeUTF(serviceInterface.getName());//字节流写出
output.writeUTF(method.getName());//调用方法的名字
output.writeObject(method.getParameterTypes());
output.writeObject(args);
//4.同步阻塞等待服务器返回应答,获取应答后返回
input = new ObjectInputStream(socket.getInputStream());//对接,获取输入流
return input.readObject();
}finally {
if(socket != null){
socket.close();
}
if(output !=null){
output.close();
}
if(input!=null){
input.close();
}
}
}
}
);
}
}
- 注册中心接口
package RPC;
import java.io.IOException;
public interface Server {
void start() throws IOException;
void register(Class serviceInterface,Class impl);
boolean isRunning();
int getPort();
void stop();
}
- 注册中心&服务器端(由于服务器端只是线程,写在一起了)
package RPC;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.*;
public class ServiceCenter implements Server {//注册中心
private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());//用于管理和实现多线程
private static final HashMap<String,Class> serviceRegistry = new HashMap<String,Class>();
private static boolean isRunning = false;
private static int port;
public ServiceCenter(int port){
ServiceCenter.port = port;
}
@Override
public void start() throws IOException {
ServerSocket server = new ServerSocket();
server.bind(new InetSocketAddress(port));//绑定端口,创建服务端
System.out.println("Server Start .....");
try{
while(true){
executor.execute(new ServiceTask(server.accept()));//丢到线程里去执行, server接受到的是client的socket
}
}finally{
server.close();
}
}
@Override
public void register(Class serviceInterface, Class impl) {
serviceRegistry.put(serviceInterface.getName(),impl);//传入接口的名字
}
@Override
public boolean isRunning() {
return isRunning;
}
@Override
public int getPort() {
return port;
}
@Override
public void stop() {
isRunning = false;
executor.shutdown();
}
private static class ServiceTask implements Runnable{//包装程序,将服务器接收的信息转换成线程提交给executor执行
Socket client = null;
public ServiceTask(Socket client){
this.client = client;
}
@Override
public void run() {
ObjectOutputStream output = null;
ObjectInputStream input = null;
try{
input = new ObjectInputStream(client.getInputStream());
String serviceName = input.readUTF();
String methodName = input.readUTF();
Class<?>[] parameterTypes = (Class<?>[])input.readObject();
Object[] arguments = (Object[])input.readObject();//接收完毕
Class serviceClass = serviceRegistry.get(serviceName);
if(serviceClass == null){
throw new ClassNotFoundException(serviceName+"not found!");
}
Method method = serviceClass.getMethod(methodName,parameterTypes);
Object result = method.invoke(serviceClass.newInstance(),arguments);//调用相关方法并获取结果
output = new ObjectOutputStream(client.getOutputStream());
output.writeObject(result);//传回结果
}catch (Exception e){
e.printStackTrace();
}finally {
if(output!=null){
try {
output.close();
}catch (IOException e){
e.printStackTrace();
}
}
if(input!=null){
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(client!=null){
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
- 测试代码:
package RPC;
import java.io.IOException;
import java.net.InetSocketAddress;
public class RPCTest {
public static void main(String[] args) throws IOException {
new Thread(new Runnable() {
@Override
public void run() {
try {
Server serviceServer = new ServiceCenter(8888);//设置8888端口来设置服务器
serviceServer.register(ServiceProducer.class, ServiceProducerImpl.class);
serviceServer.start();
}catch(IOException e){
e.printStackTrace();
}
}
}).start();//丢进线程,客户端也在此方法内测试
ServiceProducer service = RPCClient.getRemoteProxyObj(ServiceProducer.class,new InetSocketAddress("localhost",8888));
System.out.println(service);
System.out.println(service.sendData("test"));
}
}
以上
希望对后来者有所帮助,有说的不对的地方还请指出/抱拳