commit 3661cfa8e7f7d3924dc43236ff78db6a46ca1dcf Author: lisang <1186733841@qq.com> Date: Thu Jun 27 21:04:09 2024 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..328d131 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +info.text \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a9bc182 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# 手写RPC框架 + +> RPC:让你像是在做本地调用一样去调用一个远程服务器上的方法 + +## RPC相关图示 + +RPC简单示意图 +![RPC简单示意图](./doc/rpc简示图.png) + +RPC时序图 +![RPC时序图](./doc/rpc时序图.png) + +手写RPC简易流程图 +![手写RPC简易流程图](./doc/rpc简易流程.png) + +## RPC需要解决的内容 + +- 必须要有服务暴露 +- 必须要有远程代理对象 +- 通信 +- 序列化与反序列化 +- IO \ No newline at end of file diff --git a/doc/rpc时序图.png b/doc/rpc时序图.png new file mode 100644 index 0000000..9d39470 Binary files /dev/null and b/doc/rpc时序图.png differ diff --git a/doc/rpc简易流程.png b/doc/rpc简易流程.png new file mode 100644 index 0000000..893d737 Binary files /dev/null and b/doc/rpc简易流程.png differ diff --git a/doc/rpc简示图.png b/doc/rpc简示图.png new file mode 100644 index 0000000..eae8712 Binary files /dev/null and b/doc/rpc简示图.png differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..7efab74 --- /dev/null +++ b/pom.xml @@ -0,0 +1,35 @@ + + + + 4.0.0 + + com.shockkid + rpc + pom + 1.0 + + rpc-api + rpc-server + rpc-client + + + rpc + + http://www.example.com + + + UTF-8 + 1.7 + 1.7 + + + + + junit + junit + 4.11 + test + + + diff --git a/rpc-api/pom.xml b/rpc-api/pom.xml new file mode 100644 index 0000000..6c25f69 --- /dev/null +++ b/rpc-api/pom.xml @@ -0,0 +1,29 @@ + + + + 4.0.0 + + com.shockkid + rpc-api + 1.0 + + rpc-api + + http://www.example.com + + + UTF-8 + 1.7 + 1.7 + + + + + junit + junit + 4.11 + test + + + diff --git a/rpc-api/src/main/java/com/shockkid/App.java b/rpc-api/src/main/java/com/shockkid/App.java new file mode 100644 index 0000000..b9c882f --- /dev/null +++ b/rpc-api/src/main/java/com/shockkid/App.java @@ -0,0 +1,13 @@ +package com.shockkid; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +} diff --git a/rpc-api/src/main/java/com/shockkid/HelloServer.java b/rpc-api/src/main/java/com/shockkid/HelloServer.java new file mode 100644 index 0000000..4446cd6 --- /dev/null +++ b/rpc-api/src/main/java/com/shockkid/HelloServer.java @@ -0,0 +1,9 @@ +package com.shockkid; + +/** + * @auther lisang + * @date 2024/6/27 17:02 + **/ +public interface HelloServer { + public String sayHello(String content); +} diff --git a/rpc-api/src/main/java/com/shockkid/RpcRequest.java b/rpc-api/src/main/java/com/shockkid/RpcRequest.java new file mode 100644 index 0000000..3f114fc --- /dev/null +++ b/rpc-api/src/main/java/com/shockkid/RpcRequest.java @@ -0,0 +1,57 @@ +package com.shockkid; + +import java.io.Serializable; +import java.util.Arrays; + +/** + * @auther lisang + * @date 2024/6/27 16:59 + **/ +public class RpcRequest implements Serializable { + private String className; + private String methodName; + private Object[] parameters; + private Class[] types; + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public Object[] getParameters() { + return parameters; + } + + public void setParameters(Object[] parameters) { + this.parameters = parameters; + } + + public Class[] getTypes() { + return types; + } + + public void setTypes(Class[] types) { + this.types = types; + } + + @Override + public String toString() { + return "RpcRequest{" + + "className='" + className + '\'' + + ", methodName='" + methodName + '\'' + + ", parameters=" + Arrays.toString(parameters) + + ", types=" + Arrays.toString(types) + + '}'; + } +} diff --git a/rpc-api/src/test/java/com/shockkid/AppTest.java b/rpc-api/src/test/java/com/shockkid/AppTest.java new file mode 100644 index 0000000..77525fe --- /dev/null +++ b/rpc-api/src/test/java/com/shockkid/AppTest.java @@ -0,0 +1,20 @@ +package com.shockkid; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/rpc-client/pom.xml b/rpc-client/pom.xml new file mode 100644 index 0000000..a116f69 --- /dev/null +++ b/rpc-client/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + com.shockkid + rpc-client + 1.0 + + rpc-client + + http://www.example.com + + + UTF-8 + 1.7 + 1.7 + + + + + com.shockkid + rpc-api + 1.0 + + + junit + junit + 4.11 + test + + + diff --git a/rpc-client/src/main/java/com/shockkid/Client.java b/rpc-client/src/main/java/com/shockkid/Client.java new file mode 100644 index 0000000..c02b7c1 --- /dev/null +++ b/rpc-client/src/main/java/com/shockkid/Client.java @@ -0,0 +1,13 @@ +package com.shockkid; + +/** + * Hello world! + */ +public class Client { + public static void main(String[] args) { + RpcProxyClient rpcProxyClient = new RpcProxyClient(); + HelloServer helloServer = rpcProxyClient.clientProxy(HelloServer.class, "localhost", 8081); + Object obj = helloServer.sayHello("lisang"); + System.out.println(obj.toString()); + } +} diff --git a/rpc-client/src/main/java/com/shockkid/RemoteInvocationHandler.java b/rpc-client/src/main/java/com/shockkid/RemoteInvocationHandler.java new file mode 100644 index 0000000..e908323 --- /dev/null +++ b/rpc-client/src/main/java/com/shockkid/RemoteInvocationHandler.java @@ -0,0 +1,30 @@ +package com.shockkid; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +/** + * @auther lisang + * @date 2024/6/27 19:51 + **/ +public class RemoteInvocationHandler implements InvocationHandler { + private String host; + private int port; + + public RemoteInvocationHandler(String host, int port) { + this.host = host; + this.port = port; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + RpcRequest request = new RpcRequest(); + request.setClassName(method.getDeclaringClass().getName()); + request.setMethodName(method.getName()); + request.setParameters(args); + request.setTypes(method.getParameterTypes()); + RpcNetTransport rpcNetTransport = new RpcNetTransport(host, port); + return rpcNetTransport.send(request); + } + +} diff --git a/rpc-client/src/main/java/com/shockkid/RpcNetTransport.java b/rpc-client/src/main/java/com/shockkid/RpcNetTransport.java new file mode 100644 index 0000000..4646e2f --- /dev/null +++ b/rpc-client/src/main/java/com/shockkid/RpcNetTransport.java @@ -0,0 +1,66 @@ +package com.shockkid; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.Socket; + +/** + * @auther lisang + * @date 2024/6/27 19:56 + **/ +public class RpcNetTransport { + private String host; + private int port; + + public RpcNetTransport(String host, int port) { + this.host = host; + this.port = port; + } + + private Socket newSocket() throws IOException { + System.out.println("开始建立一个连接"); + Socket socket = null; + socket = new Socket(host, port); + return socket; + } + + public Object send(RpcRequest request) { + Socket socket = null; + Object result = null; + ObjectOutputStream os = null; + ObjectInputStream is = null; + try { + socket = newSocket(); + os = new ObjectOutputStream(socket.getOutputStream()); + os.writeObject(request); + is = new ObjectInputStream(socket.getInputStream()); + result = is.readObject(); + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } finally { + if (os != null) { + try { + os.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (is != null) { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return result; + } +} diff --git a/rpc-client/src/main/java/com/shockkid/RpcProxyClient.java b/rpc-client/src/main/java/com/shockkid/RpcProxyClient.java new file mode 100644 index 0000000..98c7248 --- /dev/null +++ b/rpc-client/src/main/java/com/shockkid/RpcProxyClient.java @@ -0,0 +1,15 @@ +package com.shockkid; + +import java.lang.reflect.Proxy; + +/** + * @auther lisang + * @date 2024/6/27 20:07 + **/ +public class RpcProxyClient { + public T clientProxy(Class interfacecls, String host, int port) { + return (T) Proxy.newProxyInstance(interfacecls.getClassLoader(), + new Class[]{interfacecls}, + new RemoteInvocationHandler(host, port)); + } +} diff --git a/rpc-client/src/test/java/com/shockkid/AppTest.java b/rpc-client/src/test/java/com/shockkid/AppTest.java new file mode 100644 index 0000000..77525fe --- /dev/null +++ b/rpc-client/src/test/java/com/shockkid/AppTest.java @@ -0,0 +1,20 @@ +package com.shockkid; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/rpc-server/pom.xml b/rpc-server/pom.xml new file mode 100644 index 0000000..50cdcb7 --- /dev/null +++ b/rpc-server/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + com.shockkid + rpc-server + 1.0 + + rpc-server + + http://www.example.com + + + UTF-8 + 1.7 + 1.7 + + + + + com.shockkid + rpc-api + 1.0 + + + junit + junit + 4.11 + test + + + diff --git a/rpc-server/src/main/java/com/shockkid/HelloServerImpl.java b/rpc-server/src/main/java/com/shockkid/HelloServerImpl.java new file mode 100644 index 0000000..049b83f --- /dev/null +++ b/rpc-server/src/main/java/com/shockkid/HelloServerImpl.java @@ -0,0 +1,12 @@ +package com.shockkid; + +/** + * @auther lisang + * @date 2024/6/27 17:03 + **/ +public class HelloServerImpl implements HelloServer { + @Override + public String sayHello(String content) { + return "shockkid " + content + " say hello"; + } +} diff --git a/rpc-server/src/main/java/com/shockkid/ProcessorHandler.java b/rpc-server/src/main/java/com/shockkid/ProcessorHandler.java new file mode 100644 index 0000000..ade77cc --- /dev/null +++ b/rpc-server/src/main/java/com/shockkid/ProcessorHandler.java @@ -0,0 +1,54 @@ +package com.shockkid; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.Socket; + +/** + * @auther lisang + * @date 2024/6/27 17:12 + **/ +public class ProcessorHandler implements Runnable { + Socket socket = null; + Object service = null; + + public ProcessorHandler(Socket socket, Object service) { + this.socket = socket; + this.service = service; + } + + @Override + public void run() { + ObjectInputStream is = null; + ObjectOutputStream os = null; + + try { + is = new ObjectInputStream(socket.getInputStream()); + RpcRequest rpcRequest = (RpcRequest) is.readObject(); + Object result = invoke(rpcRequest); + + os = new ObjectOutputStream(socket.getOutputStream()); + os.writeObject(result); + os.flush(); + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + private Object invoke(RpcRequest request) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Class clazz = Class.forName(request.getClassName()); + Object[] args = request.getParameters(); + Method method = clazz.getMethod(request.getMethodName(), request.getTypes()); + Object obj = method.invoke(service, args); + return obj; + } +} diff --git a/rpc-server/src/main/java/com/shockkid/RpcProxyServer.java b/rpc-server/src/main/java/com/shockkid/RpcProxyServer.java new file mode 100644 index 0000000..eaff08a --- /dev/null +++ b/rpc-server/src/main/java/com/shockkid/RpcProxyServer.java @@ -0,0 +1,30 @@ +package com.shockkid; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * @auther lisang + * @date 2024/6/27 17:06 + **/ +public class RpcProxyServer { + private ExecutorService executorService = Executors.newCachedThreadPool(); + + public void publisher(Object server,int prot) { + ServerSocket serverSocket = null; + try { + serverSocket = new ServerSocket(prot); + while (true) { + Socket socket = serverSocket.accept(); + executorService.execute(new ProcessorHandler(socket, server)); + + + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/rpc-server/src/main/java/com/shockkid/Server.java b/rpc-server/src/main/java/com/shockkid/Server.java new file mode 100644 index 0000000..9767bd1 --- /dev/null +++ b/rpc-server/src/main/java/com/shockkid/Server.java @@ -0,0 +1,12 @@ +package com.shockkid; + +/** + * Hello world! + */ +public class Server { + public static void main(String[] args) { + HelloServer helloServer = new HelloServerImpl(); + RpcProxyServer rpcProxyServer = new RpcProxyServer(); + rpcProxyServer.publisher(helloServer, 8081); + } +} diff --git a/rpc-server/src/test/java/com/shockkid/AppTest.java b/rpc-server/src/test/java/com/shockkid/AppTest.java new file mode 100644 index 0000000..77525fe --- /dev/null +++ b/rpc-server/src/test/java/com/shockkid/AppTest.java @@ -0,0 +1,20 @@ +package com.shockkid; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/src/main/java/com/shockkid/App.java b/src/main/java/com/shockkid/App.java new file mode 100644 index 0000000..b9c882f --- /dev/null +++ b/src/main/java/com/shockkid/App.java @@ -0,0 +1,13 @@ +package com.shockkid; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +} diff --git a/src/test/java/com/shockkid/AppTest.java b/src/test/java/com/shockkid/AppTest.java new file mode 100644 index 0000000..77525fe --- /dev/null +++ b/src/test/java/com/shockkid/AppTest.java @@ -0,0 +1,20 @@ +package com.shockkid; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +}