├── .idea ├── .gitignore ├── compiler.xml ├── jarRepositories.xml ├── misc.xml └── vcs.xml ├── README.md ├── Simple-RPC.iml ├── Simple-RPC.ipr ├── Simple-RPC.iws ├── assets ├── Screen Shot 2022-05-29 at 14.24.21-3816076.png └── Screen Shot 2022-05-29 at 14.24.39.png ├── pom.xml ├── src ├── main │ └── java │ │ └── rpc │ │ └── tomstillcoding │ │ └── com │ │ ├── IDL │ │ ├── Hello │ │ │ ├── HelloRequest.java │ │ │ ├── HelloResponse.java │ │ │ └── HelloService.java │ │ └── Ping │ │ │ ├── PingRequest.java │ │ │ ├── PingResponse.java │ │ │ └── PingService.java │ │ └── core │ │ ├── client │ │ ├── RpcClientProxy.java │ │ └── RpcClientTransfer.java │ │ ├── codec │ │ ├── RpcRequestBody.java │ │ └── RpcResponseBody.java │ │ ├── rpc_protocol │ │ ├── RpcRequest.java │ │ └── RpcResponse.java │ │ └── server │ │ ├── RpcServer.java │ │ └── RpcServerWorker.java └── test │ └── java │ ├── client │ └── TestClient.java │ ├── server │ ├── HelloServiceImpl.java │ ├── PingServiceImpl.java │ └── TestServer.java │ └── test │ ├── Employee.java │ └── Test.java └── target ├── classes └── rpc │ └── tomstillcoding │ └── com │ ├── IDL │ ├── Hello │ │ ├── HelloRequest.class │ │ ├── HelloResponse.class │ │ └── HelloService.class │ └── Ping │ │ ├── PingRequest.class │ │ ├── PingResponse.class │ │ └── PingService.class │ └── core │ ├── client │ ├── RpcClientProxy.class │ └── RpcClientTransfer.class │ ├── codec │ ├── RpcRequestBody$RpcRequestBodyBuilder.class │ ├── RpcRequestBody.class │ ├── RpcResponseBody$RpcResponseBodyBuilder.class │ └── RpcResponseBody.class │ ├── rpc_protocol │ ├── RpcRequest$RpcRequestBuilder.class │ ├── RpcRequest.class │ ├── RpcResponse$RpcResponseBuilder.class │ └── RpcResponse.class │ └── server │ ├── RpcServer.class │ └── RpcServerWorker.class └── test-classes ├── client └── TestClient.class ├── server ├── HelloServiceImpl.class ├── PingServiceImpl.class └── TestServer.class └── test ├── Employee.class └── Test.class /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple-RPC 2 | 用Java手撸一个简单的RPC框架|一个写C++的在看完gRPC后用Java手撸了个简单RPC框架 3 | # 使用方式 4 | 用IDEA打开后配置Maven,右键点击pom.xml,重新加载项目(Reload Project) 5 | 然后直接运行服务器程序和客户端程序 6 | 7 | 带你手撸简单RPC框架|一个写C++的在看完gRPC后用Java快速手撸了个简单RPC框架 8 | 9 | # 目录 10 | 11 | 今天的视频分三部分: 12 | 13 | 1、介绍RPC是什么 14 | 15 | 2、介绍谷歌gRPC的Go语言使用 16 | 17 | 3、用Java手撸一个最简单的RPC框架 18 | 19 | 今天聊到的所有东西都会开源,在看完视频后,你可以照着文档,以及看看源代码,就能明白RPC的核心内容,也可以在这个简单RPC框架代码上补充各种组件,最后写到自己的简历上,最重要的目的是,撸一套RPC框架,能让你掌握RPC的核心功能实现。 20 | 21 | (等录完视频,我就去剪头发 做核酸 看MSI了) 22 | 23 | # 介绍RPC 24 | 25 | Screen Shot 2022-05-29 at 14.24.21 26 | 27 | # gRPC使用 28 | 29 | grpc,就像是Dubbo一样,都是RPC框架,可能你听得云里雾里的,先看看具体怎么用吧 30 | 31 | 定义hello.proto文件: 32 | 33 | proto是啥,IDL,IDL是什么,接口描述语言(Interface description language,缩写IDL),什么意思?不重要,重要的是,客户端在调用服务器的方法的时候,用IDL确定了两者的方法名、参数、返回值. 34 | 35 | 下面我们编写hello.proto文件 36 | 37 | ```protobuf 38 | syntax = "proto3"; // 版本声明,使用Protocol Buffers v3版本 39 | package pb; // 包名 40 | 41 | // 定义服务 42 | service Greeter { 43 | // SayHello 方法 44 | rpc SayHello (HelloRequest) returns (HelloResponse) {} 45 | } 46 | 47 | // 请求消息 48 | message HelloRequest { 49 | string name = 1; 50 | } 51 | 52 | // 响应消息 53 | message HelloResponse { 54 | string reply = 1; 55 | } 56 | ``` 57 | 58 | ```bash 59 | protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative hello.proto 60 | ``` 61 | 62 | 通过.proto文件,用grpc的工具,生成我们需要的库代码,然后用Go编写客户端和服务端程序。 63 | 64 | 不会Go语言?无所谓,都是面向对象语言,调用方法=方法名+参数+返回值,看得懂就够了,看不懂,花一分钟查下语法就看懂了 65 | 66 | 67 | 68 | 服务端代码:收到客户端请求,拿到需要调用的函数(SayHello)+参数(HelloRequest),调用本地的函数实现,将返回值(HelloResponse)返回给客户端 69 | 70 | ```go 71 | package main 72 | 73 | import ( 74 | "context" 75 | "fmt" 76 | "hello_server/pb" 77 | "net" 78 | 79 | "google.golang.org/grpc" 80 | ) 81 | 82 | // ----------------------- 实现pb中的接口 Start ------------------------ 83 | type server struct { 84 | pb.UnimplementedGreeterServer 85 | } 86 | func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) { 87 | return &pb.HelloResponse{Reply: "Hello " + in.Name}, nil 88 | } 89 | // ----------------------- 实现pb中的接口 End ------------------------ 90 | 91 | func main() { 92 | // 1. 监听本地的8972端口 93 | lis, err := net.Listen("tcp", ":8972") 94 | if err != nil { 95 | fmt.Printf("failed to listen: %v", err) 96 | return 97 | } 98 | 99 | // 2. 创建grpc服务器并注册服务(注册服务就是说,将pb接口的方法实现并register) 100 | rpcServer := grpc.NewServer() 101 | pb.RegisterGreeterServer(rpcServer, &server{}) 102 | 103 | // 3. 启动grpc服务器 104 | err = rpcServer.Serve(lis) 105 | if err != nil { 106 | fmt.Printf("failed to serve: %v", err) 107 | return 108 | } 109 | } 110 | ``` 111 | 112 | 客户端代码:调用函数(SayHello),传入参数(HelloRequest),获得返回值(HelloResponse) 113 | 114 | ```go 115 | package main 116 | 117 | import ( 118 | "context" 119 | "flag" 120 | "log" 121 | "time" 122 | 123 | "hello_client/pb" 124 | 125 | "google.golang.org/grpc" 126 | "google.golang.org/grpc/credentials/insecure" 127 | ) 128 | 129 | // ----------------------- 一会儿需要用到的变量 Start ------------------------ 130 | const ( 131 | defaultName = "world" 132 | ) 133 | 134 | var ( 135 | addr = flag.String("addr", "127.0.0.1:8972", "the address to connect to") 136 | name = flag.String("name", defaultName, "Name to greet") 137 | ) 138 | // ----------------------- 一会儿需要用到的变量 End ------------------------ 139 | 140 | func main() { 141 | flag.Parse() 142 | // 连接到server端,此处禁用安全传输 143 | conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials())) 144 | if err != nil { 145 | log.Fatalf("did not connect: %v", err) 146 | } 147 | defer conn.Close() 148 | 149 | // ------------------------- rpc调用 start ----------------------------------- 150 | // 创建grpcClient 151 | rpcClient := pb.NewGreeterClient(conn) 152 | 153 | // 设置上下文(没事不用管) 154 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 155 | defer cancel() 156 | 157 | // 执行rpc调用 158 | r, err := rpcClient.SayHello(ctx, &pb.HelloRequest{Name: *name}) 159 | 160 | // 错误处理 161 | if err != nil { 162 | log.Fatalf("could not greet: %v", err) 163 | } 164 | log.Printf("Greeting: %s", r.GetReply()) 165 | // ------------------------- rpc调用 end ----------------------------------- 166 | } 167 | ``` 168 | 169 | 170 | 171 | 总结: 172 | 173 | proto文件(IDL)定义方法、参数、返回值,使用工具直接生成go可以用的库。 174 | 175 | Server:创建**grpcServer对象**,注册服务(也就是自己定义的方法,针对proto文件方法的实现),**启动服务器**。 176 | 177 | Client:创建**grpcClient对象(给出服务ip+port地址)**,用grpcClient对象调用服务(也就是调用proto文件里面声明的方法),然后打印返回的结果。 178 | 179 | 180 | 181 | # 手撸Java RPC框架 182 | 183 | 因此,我们要用Java手撸一个最简单的RPC,按照上面的说法,应该要实现的是: 184 | 185 | 框架: 186 | 187 | 1. 接口文件,定义了方法名、参数、返回值,供客户端和服务端使用 188 | 2. RPC框架,让客户端和服务端可以使用框架,达到**本地调用,远端执行**的目的 189 | 190 | 测试: 191 | 192 | 1. 写一个TestServer可执行程序,注册方法的实现后,使用rpcServer进行监听并处理rpc调用 193 | 2. 写一个TestClient可执行程序,使用rpcClient进行发起rpc调用,并打印rpc调用结果 194 | 195 | 196 | 197 | Screen Shot 2022-05-29 at 14.24.21 198 | 199 | ### 第一步:写IDL文件 200 | 201 | IDL.Hello的内容直接编写完毕。 202 | 203 | 按照grpc的方式,编写接口HelloService,以及里面的消息体HelloRequest和HelloResponse,客户端和服务器都使用这同一套接口 204 | 205 | ### 第二步:编写RPC协议 206 | 207 | RpcRequest和RpcResponse都是RPC协议,RPC协议包括header和body两部分,header我们用String表示,body我们用序列化后的byte[]流表示,这里的字节流的序列化的方式可以是Java的序列化方式,可以换成JSON序列化方式,也可以用Thrift、PB序列化方式,为了简单,我们这次直接用Java的序列化方式。 208 | 209 | 然后body中被序列化的内容,因为是codec层的工作,放在了codec包中,RPCReuqest要调用一个方法,需要知道接口名、方法名、参数、参数类型,因此把这些东西放进RpcRequestBody中即可,后面把它序列化后房价RpcRequest的body字节流中;同理,RPCResponse 的body中,只需要一个被序列化后的Java Object即可。 210 | 211 | ### 第三步:分析 212 | 213 | 我们的rpcClient要执行一个函数hello,传入参数HelloRequest,然后返回HelloResponse。 214 | 215 | 整个流程就是:客户端获得rpcService对象,使用rpcService对象执行hello方法,那么rpcService底层实现就发送一条RpcRequest协议(对比HTTP协议)把:要执行的接口名+方法名+参数类型+具体参数序列化后,放进RpcRequest协议的body字节流中,然后给RpcRequest加上header,发给服务端,服务端解析出Rpc协议的body(对比HTTP协议解析body)中的接口名、方法名等,直接调用本地的接口的实现,然后将返回值包装成一条RpcResponse消息,发送给客户端即可,rpcService底层将该response消息解析,从body中拿到(也是对比HTTP解析body)返回值,然后返回给客户端。 216 | 217 | ### 第四步:客户端实现(动态代理) 218 | 219 | 客户端方面,客户端本地只有IDL.Hello中的内容,没有方法的具体实现,也就是说要调用一个没有实现的接口,显然,我们使用Java反射的动态代理特性,实例化一个接口,将调用接口方法“代理”给InvocationHandler中的invoke来执行,在Invoke中获取到接口名、方法名等包装成Rpc协议,发送给服务端,然后等待服务端返回。 220 | 221 | ### 第五步:服务端实现(反射调用) 222 | 223 | 服务端方面,本地需要实现接口的方法,然后在启动监听网络之前注册所有的接口,当消息到来的时候,根据RpcRequestBody中的接口名拿到接口对象,然后用反射的方式调用即可,将调用结果包装成RpcResponse,发送给客户端。 224 | 225 | ### 第六步:测试 226 | 227 | 编写测试用例 228 | 229 | 一共两个接口,HelloService和PingService,HelloService有两个方法:hello和hi,PingService有一个方法:ping 230 | 231 | ### 总结 232 | 233 | 事实上一个完整的RPC框架不仅包含上面的内容,还要提供很多功能,比如说 234 | 235 | 服务发现:客户端怎么找到能够调用rpc的服务器的ip和端口? 236 | 237 | 服务治理:整个rpc的运行怎么可靠,比如说客户端请求太多,一台服务器不顶用,IO打满了,再加一台服务器? 238 | 239 | 服务注册:服务器怎么才能把自己能够handle的接口告诉客户端,不然自己都不能处理,客户端调用接口,调了也是失败 240 | 241 | 编解码:比如上面body中我们是直接用的Java序列化,那要是跨平台怎么办,客户端用Java写,服务端用Go写,Go又不能处理Java序列化,对吧,而且编解码性能、编码后的字节数量也是很重要的一些东西 242 | 243 | RPC协议:协议的字段有很多的,协议版本、传输方式、序列化方式、连接个数等等 244 | 245 | 246 | 247 | 所以,今天讲的这个东西不过是一个RPC的最简化Demo,帮大家理解rpc的原理,按照多层模型去设计与实现,后面往这个架子上面搭东西,或者去学习真正的RPC框架是比较有帮助的,祝大家周末快乐! 248 | -------------------------------------------------------------------------------- /Simple-RPC.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 75 | 76 | -------------------------------------------------------------------------------- /Simple-RPC.ipr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 40 | 41 | 48 | 49 | 59 | 60 | 63 | 64 | 80 | 81 | 83 | 84 | 85 | 86 | 87 | 88 | 92 | 93 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /Simple-RPC.iws: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 67 | 68 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 119 | 120 | 121 | 122 | 123 | 124 | 139 | 140 | 141 | 142 | 143 | 144 | 198 | 199 | 202 | 203 | 204 | 205 | 206 | 214 | 215 | 221 | 222 | 228 | 229 | 230 | 241 | 242 | 243 | 244 | 247 | 248 | 249 | 254 | 255 | 259 | 260 | 265 | 266 | 270 | 271 | 274 | 275 | 279 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 317 | 318 | 344 | 352 | 360 | 385 | 386 | 387 | 392 | 393 | 418 | 419 | -------------------------------------------------------------------------------- /assets/Screen Shot 2022-05-29 at 14.24.21-3816076.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/assets/Screen Shot 2022-05-29 at 14.24.21-3816076.png -------------------------------------------------------------------------------- /assets/Screen Shot 2022-05-29 at 14.24.39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/assets/Screen Shot 2022-05-29 at 14.24.39.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | Simple-RPC 9 | 1.0-SNAPSHOT 10 | 11 | 12 | org.projectlombok 13 | lombok 14 | 1.18.24 15 | 16 | 17 | 18 | 19 | 20 | 11 21 | 11 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/main/java/rpc/tomstillcoding/com/IDL/Hello/HelloRequest.java: -------------------------------------------------------------------------------- 1 | package rpc.tomstillcoding.com.IDL.Hello; 2 | 3 | 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | public class HelloRequest implements Serializable { 12 | private String name; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/rpc/tomstillcoding/com/IDL/Hello/HelloResponse.java: -------------------------------------------------------------------------------- 1 | package rpc.tomstillcoding.com.IDL.Hello; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | 9 | @Data 10 | @AllArgsConstructor 11 | public class HelloResponse implements Serializable { 12 | private String msg; 13 | } -------------------------------------------------------------------------------- /src/main/java/rpc/tomstillcoding/com/IDL/Hello/HelloService.java: -------------------------------------------------------------------------------- 1 | package rpc.tomstillcoding.com.IDL.Hello; 2 | 3 | public interface HelloService { 4 | HelloResponse hello(HelloRequest request); 5 | HelloResponse hi(HelloRequest request); 6 | } 7 | 8 | -------------------------------------------------------------------------------- /src/main/java/rpc/tomstillcoding/com/IDL/Ping/PingRequest.java: -------------------------------------------------------------------------------- 1 | package rpc.tomstillcoding.com.IDL.Ping; 2 | 3 | 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @AllArgsConstructor 11 | public class PingRequest implements Serializable { 12 | private String name; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/rpc/tomstillcoding/com/IDL/Ping/PingResponse.java: -------------------------------------------------------------------------------- 1 | package rpc.tomstillcoding.com.IDL.Ping; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | 9 | @Data 10 | @AllArgsConstructor 11 | public class PingResponse implements Serializable { 12 | private String msg; 13 | } -------------------------------------------------------------------------------- /src/main/java/rpc/tomstillcoding/com/IDL/Ping/PingService.java: -------------------------------------------------------------------------------- 1 | package rpc.tomstillcoding.com.IDL.Ping; 2 | 3 | public interface PingService { 4 | PingResponse ping(PingRequest request); 5 | } 6 | 7 | -------------------------------------------------------------------------------- /src/main/java/rpc/tomstillcoding/com/core/client/RpcClientProxy.java: -------------------------------------------------------------------------------- 1 | package rpc.tomstillcoding.com.core.client; 2 | 3 | import rpc.tomstillcoding.com.core.codec.RpcRequestBody; 4 | import rpc.tomstillcoding.com.core.codec.RpcResponseBody; 5 | import rpc.tomstillcoding.com.core.rpc_protocol.RpcRequest; 6 | import rpc.tomstillcoding.com.core.rpc_protocol.RpcResponse; 7 | 8 | import java.io.ByteArrayInputStream; 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.ObjectInputStream; 11 | import java.io.ObjectOutputStream; 12 | import java.lang.reflect.InvocationHandler; 13 | import java.lang.reflect.Method; 14 | import java.lang.reflect.Proxy; 15 | 16 | public class RpcClientProxy implements InvocationHandler { 17 | @SuppressWarnings("unchecked") 18 | public T getService(Class clazz) { 19 | return (T) Proxy.newProxyInstance( 20 | clazz.getClassLoader(), 21 | new Class[]{clazz}, 22 | this 23 | ); 24 | } 25 | 26 | @Override 27 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 28 | 29 | // 1、将调用所需信息编码成bytes[],即有了调用编码【codec层】 30 | RpcRequestBody rpcRequestBody = RpcRequestBody.builder() 31 | .interfaceName(method.getDeclaringClass().getName()) 32 | .methodName(method.getName()) 33 | .paramTypes(method.getParameterTypes()) 34 | .parameters(args) 35 | .build(); 36 | 37 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 38 | ObjectOutputStream oos = new ObjectOutputStream(baos); 39 | oos.writeObject(rpcRequestBody); 40 | byte[] bytes = baos.toByteArray(); 41 | 42 | // 2、创建RPC协议,将Header、Body的内容设置好(Body中存放调用编码)【protocol层】 43 | RpcRequest rpcRequest = RpcRequest.builder() 44 | .header("version=1") 45 | .body(bytes) 46 | .build(); 47 | 48 | // 3、发送RpcRequest,获得RpcResponse 49 | RpcClientTransfer rpcClient = new RpcClientTransfer(); 50 | RpcResponse rpcResponse = rpcClient.sendRequest(rpcRequest); 51 | 52 | // 4、解析RpcResponse,也就是在解析rpc协议【protocol层】 53 | String header = rpcResponse.getHeader(); 54 | byte[] body = rpcResponse.getBody(); 55 | if (header.equals("version=1")) { 56 | // 将RpcResponse的body中的返回编码,解码成我们需要的对象Object并返回【codec层】 57 | ByteArrayInputStream bais = new ByteArrayInputStream(body); 58 | ObjectInputStream ois = new ObjectInputStream(bais); 59 | RpcResponseBody rpcResponseBody = (RpcResponseBody) ois.readObject(); 60 | Object retObject = rpcResponseBody.getRetObject(); 61 | return retObject; 62 | } 63 | return null; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/rpc/tomstillcoding/com/core/client/RpcClientTransfer.java: -------------------------------------------------------------------------------- 1 | package rpc.tomstillcoding.com.core.client; 2 | 3 | import rpc.tomstillcoding.com.core.rpc_protocol.RpcRequest; 4 | import rpc.tomstillcoding.com.core.rpc_protocol.RpcResponse; 5 | 6 | import java.io.IOException; 7 | import java.io.ObjectInputStream; 8 | import java.io.ObjectOutputStream; 9 | import java.net.Socket; 10 | 11 | // 传入protocol层的RpcRequest,输出protocol层的RpcResponse 12 | public class RpcClientTransfer { 13 | 14 | public RpcResponse sendRequest(RpcRequest rpcRequest) { 15 | try (Socket socket = new Socket("localhost", 9000)) { 16 | // 发送【transfer层】 17 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); 18 | ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream()); 19 | objectOutputStream.writeObject(rpcRequest); 20 | objectOutputStream.flush(); 21 | 22 | RpcResponse rpcResponse = (RpcResponse) objectInputStream.readObject(); 23 | 24 | return rpcResponse; 25 | 26 | } catch (IOException | ClassNotFoundException e) { 27 | e.printStackTrace(); 28 | return null; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/rpc/tomstillcoding/com/core/codec/RpcRequestBody.java: -------------------------------------------------------------------------------- 1 | package rpc.tomstillcoding.com.core.codec; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | @Data 9 | @Builder 10 | // 调用编码 11 | public class RpcRequestBody implements Serializable { 12 | private String interfaceName; 13 | private String methodName; 14 | private Object[] parameters; 15 | private Class[] paramTypes; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/rpc/tomstillcoding/com/core/codec/RpcResponseBody.java: -------------------------------------------------------------------------------- 1 | package rpc.tomstillcoding.com.core.codec; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @Builder 11 | // 返回值编码 12 | public class RpcResponseBody implements Serializable { 13 | private Object retObject; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/rpc/tomstillcoding/com/core/rpc_protocol/RpcRequest.java: -------------------------------------------------------------------------------- 1 | package rpc.tomstillcoding.com.core.rpc_protocol; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | @Data 9 | @Builder 10 | // Serializable:对象变成可传输的字节序列 11 | public class RpcRequest implements Serializable { 12 | // 协议头部分 13 | private String header; 14 | // 协议体部分 15 | private byte[] body; 16 | 17 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/rpc/tomstillcoding/com/core/rpc_protocol/RpcResponse.java: -------------------------------------------------------------------------------- 1 | package rpc.tomstillcoding.com.core.rpc_protocol; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | @Data 9 | @Builder 10 | // 为什么要有泛型?因为返回的对象是各种各样类型的 11 | // Serializable:对象变成可传输的字节序列 12 | public class RpcResponse implements Serializable { 13 | // 协议头部分 14 | private String header; 15 | 16 | // 协议体部分 17 | private byte[] body; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/rpc/tomstillcoding/com/core/server/RpcServer.java: -------------------------------------------------------------------------------- 1 | package rpc.tomstillcoding.com.core.server; 2 | 3 | import java.io.IOException; 4 | import java.net.ServerSocket; 5 | import java.net.Socket; 6 | import java.util.HashMap; 7 | import java.util.concurrent.*; 8 | 9 | public class RpcServer { 10 | private final ExecutorService threadPool; 11 | // interfaceName -> interfaceImplementation object 12 | private final HashMap registeredService; 13 | 14 | public RpcServer() { 15 | 16 | int corePoolSize = 5; 17 | int maximumPoolSize = 50; 18 | long keepAliveTime = 60; 19 | BlockingQueue workingQueue = new ArrayBlockingQueue<>(100); 20 | ThreadFactory threadFactory = Executors.defaultThreadFactory(); 21 | this.threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workingQueue, threadFactory); 22 | this.registeredService = new HashMap(); 23 | } 24 | 25 | // 参数service就是interface的implementation object 26 | public void register(Object service) { 27 | registeredService.put(service.getClass().getInterfaces()[0].getName(), service); 28 | } 29 | 30 | public void serve(int port) { 31 | try (ServerSocket serverSocket = new ServerSocket(port)){ 32 | System.out.println("server starting..."); 33 | Socket handleSocket; 34 | while ((handleSocket = serverSocket.accept()) != null) { 35 | System.out.println("client connected, ip:" + handleSocket.getInetAddress()); 36 | threadPool.execute(new RpcServerWorker(handleSocket, registeredService)); 37 | } 38 | } catch (IOException e) { 39 | e.printStackTrace(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/rpc/tomstillcoding/com/core/server/RpcServerWorker.java: -------------------------------------------------------------------------------- 1 | package rpc.tomstillcoding.com.core.server; 2 | 3 | import rpc.tomstillcoding.com.core.rpc_protocol.RpcRequest; 4 | import rpc.tomstillcoding.com.core.codec.RpcRequestBody; 5 | import rpc.tomstillcoding.com.core.rpc_protocol.RpcResponse; 6 | import rpc.tomstillcoding.com.core.codec.RpcResponseBody; 7 | 8 | import java.io.*; 9 | import java.lang.reflect.InvocationTargetException; 10 | import java.lang.reflect.Method; 11 | import java.net.Socket; 12 | import java.util.HashMap; 13 | 14 | public class RpcServerWorker implements Runnable{ 15 | 16 | private Socket socket; 17 | private HashMap registeredService; 18 | 19 | public RpcServerWorker(Socket socket, HashMap registeredService) { 20 | this.socket = socket; 21 | this.registeredService = registeredService; 22 | } 23 | 24 | @Override 25 | public void run() { 26 | try { 27 | ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream()); 28 | ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); 29 | 30 | // 1、Transfer层获取到RpcRequest消息【transfer层】 31 | RpcRequest rpcRequest = (RpcRequest) objectInputStream.readObject(); 32 | 33 | // 2、解析版本号,并判断【protocol层】 34 | if (rpcRequest.getHeader().equals("version=1")) { 35 | 36 | // 3、将rpcRequest中的body部分解码出来变成RpcRequestBody【codec层】 37 | byte[] body = rpcRequest.getBody(); 38 | ByteArrayInputStream bais = new ByteArrayInputStream(body); 39 | ObjectInputStream ois = new ObjectInputStream(bais); 40 | RpcRequestBody rpcRequestBody = (RpcRequestBody) ois.readObject(); 41 | 42 | // 调用服务 43 | Object service = registeredService.get(rpcRequestBody.getInterfaceName()); 44 | Method method = service.getClass().getMethod(rpcRequestBody.getMethodName(), rpcRequestBody.getParamTypes()); 45 | Object returnObject = method.invoke(service, rpcRequestBody.getParameters()); 46 | 47 | // 1、将returnObject编码成bytes[]即变成了返回编码【codec层】 48 | RpcResponseBody rpcResponseBody = RpcResponseBody.builder() 49 | .retObject(returnObject) 50 | .build(); 51 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 52 | ObjectOutputStream oos = new ObjectOutputStream(baos); 53 | oos.writeObject(rpcResponseBody); 54 | byte[] bytes = baos.toByteArray(); 55 | 56 | // 2、将返回编码作为body,加上header,生成RpcResponse协议【protocol层】 57 | RpcResponse rpcResponse = RpcResponse.builder() 58 | .header("version=1") 59 | .body(bytes) 60 | .build(); 61 | // 3、发送【transfer层】 62 | objectOutputStream.writeObject(rpcResponse); 63 | objectOutputStream.flush(); 64 | } 65 | 66 | } catch (IOException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { 67 | e.printStackTrace(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/client/TestClient.java: -------------------------------------------------------------------------------- 1 | package client; 2 | 3 | import rpc.tomstillcoding.com.IDL.Hello.HelloRequest; 4 | import rpc.tomstillcoding.com.IDL.Hello.HelloResponse; 5 | import rpc.tomstillcoding.com.IDL.Hello.HelloService; 6 | import rpc.tomstillcoding.com.core.client.RpcClientProxy; 7 | 8 | public class TestClient { 9 | public static void main(String[] args) { 10 | // 获取RpcService 11 | RpcClientProxy proxy = new RpcClientProxy(); 12 | HelloService helloService = proxy.getService(HelloService.class); 13 | // 构造出请求对象HelloRequest 14 | HelloRequest helloRequest = new HelloRequest("tom"); 15 | // rpc调用并返回结果对象HelloResponse 16 | HelloResponse helloResponse = helloService.hello(helloRequest); 17 | // 从HelloResponse中获取msg 18 | String helloMsg = helloResponse.getMsg(); 19 | // 打印msg 20 | System.out.println(helloMsg); 21 | 22 | // 调用hi方法 23 | HelloResponse hiResponse = helloService.hi(helloRequest); 24 | String hiMsg = hiResponse.getMsg(); 25 | System.out.println(hiMsg); 26 | 27 | // 调用ping方法 28 | // PingService pingService = proxy.getService(PingService.class); 29 | // PingRequest pingRequest = new PingRequest("tom"); 30 | // PingResponse pingResponse = pingService.ping(pingRequest); 31 | // String pingMsg = pingResponse.getMsg(); 32 | // System.out.println(pingMsg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/server/HelloServiceImpl.java: -------------------------------------------------------------------------------- 1 | package server; 2 | 3 | import rpc.tomstillcoding.com.IDL.Hello.HelloRequest; 4 | import rpc.tomstillcoding.com.IDL.Hello.HelloResponse; 5 | import rpc.tomstillcoding.com.IDL.Hello.HelloService; 6 | 7 | public class HelloServiceImpl implements HelloService { 8 | @Override 9 | public HelloResponse hello(HelloRequest request) { 10 | String name = request.getName(); 11 | String retMsg = "hello: " + name; 12 | HelloResponse response = new HelloResponse(retMsg); 13 | return response; 14 | } 15 | 16 | @Override 17 | public HelloResponse hi(HelloRequest request) { 18 | String name = request.getName(); 19 | String retMsg = "hi: " + name; 20 | HelloResponse response = new HelloResponse(retMsg); 21 | return response; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/server/PingServiceImpl.java: -------------------------------------------------------------------------------- 1 | package server; 2 | 3 | import rpc.tomstillcoding.com.IDL.Hello.HelloRequest; 4 | import rpc.tomstillcoding.com.IDL.Hello.HelloResponse; 5 | import rpc.tomstillcoding.com.IDL.Hello.HelloService; 6 | import rpc.tomstillcoding.com.IDL.Ping.PingRequest; 7 | import rpc.tomstillcoding.com.IDL.Ping.PingResponse; 8 | import rpc.tomstillcoding.com.IDL.Ping.PingService; 9 | 10 | public class PingServiceImpl implements PingService { 11 | 12 | @Override 13 | public PingResponse ping(PingRequest request) { 14 | String name = request.getName(); 15 | String retMsg = "pong: " + name; 16 | PingResponse response = new PingResponse(retMsg); 17 | return response; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/server/TestServer.java: -------------------------------------------------------------------------------- 1 | package server; 2 | 3 | import rpc.tomstillcoding.com.IDL.Hello.HelloService; 4 | import rpc.tomstillcoding.com.IDL.Ping.PingService; 5 | import rpc.tomstillcoding.com.core.server.RpcServer; 6 | 7 | public class TestServer { 8 | public static void main(String[] args) { 9 | RpcServer rpcServer = new RpcServer(); // 真正的rpc server 10 | HelloService helloService = new HelloServiceImpl(); // 包含需要处理的方法的对象 11 | rpcServer.register(helloService); // 向rpc server注册对象里面的所有方法 12 | // PingService pingService = new PingServiceImpl(); 13 | // rpcServer.register(pingService); 14 | 15 | rpcServer.serve(9000); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/test/Employee.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | public class Employee implements java.io.Serializable 4 | { 5 | public String name; 6 | public String address; 7 | public transient int SSN; 8 | public int number; 9 | public void mailCheck() 10 | { 11 | System.out.println("Mailing a check to " + name 12 | + " " + address); 13 | } 14 | } -------------------------------------------------------------------------------- /src/test/java/test/Test.java: -------------------------------------------------------------------------------- 1 | package test; 2 | 3 | import java.io.*; 4 | 5 | public class Test 6 | { 7 | public static void main(String [] args) 8 | { 9 | 10 | 11 | Employee e = null; 12 | try 13 | { 14 | FileInputStream fileIn = new FileInputStream("/Users/thomas/Desktop/test.txt"); 15 | ObjectInputStream in = new ObjectInputStream(fileIn); 16 | e = (Employee) in.readObject(); 17 | in.close(); 18 | fileIn.close(); 19 | }catch(IOException i) 20 | { 21 | i.printStackTrace(); 22 | return; 23 | }catch(ClassNotFoundException c) 24 | { 25 | System.out.println("Employee class not found"); 26 | c.printStackTrace(); 27 | return; 28 | } 29 | System.out.println("Deserialized Employee..."); 30 | System.out.println("Name: " + e.name); 31 | System.out.println("Address: " + e.address); 32 | System.out.println("SSN: " + e.SSN); 33 | System.out.println("Number: " + e.number); 34 | } 35 | } -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/IDL/Hello/HelloRequest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/IDL/Hello/HelloRequest.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/IDL/Hello/HelloResponse.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/IDL/Hello/HelloResponse.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/IDL/Hello/HelloService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/IDL/Hello/HelloService.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/IDL/Ping/PingRequest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/IDL/Ping/PingRequest.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/IDL/Ping/PingResponse.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/IDL/Ping/PingResponse.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/IDL/Ping/PingService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/IDL/Ping/PingService.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/core/client/RpcClientProxy.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/core/client/RpcClientProxy.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/core/client/RpcClientTransfer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/core/client/RpcClientTransfer.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/core/codec/RpcRequestBody$RpcRequestBodyBuilder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/core/codec/RpcRequestBody$RpcRequestBodyBuilder.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/core/codec/RpcRequestBody.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/core/codec/RpcRequestBody.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/core/codec/RpcResponseBody$RpcResponseBodyBuilder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/core/codec/RpcResponseBody$RpcResponseBodyBuilder.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/core/codec/RpcResponseBody.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/core/codec/RpcResponseBody.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/core/rpc_protocol/RpcRequest$RpcRequestBuilder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/core/rpc_protocol/RpcRequest$RpcRequestBuilder.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/core/rpc_protocol/RpcRequest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/core/rpc_protocol/RpcRequest.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/core/rpc_protocol/RpcResponse$RpcResponseBuilder.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/core/rpc_protocol/RpcResponse$RpcResponseBuilder.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/core/rpc_protocol/RpcResponse.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/core/rpc_protocol/RpcResponse.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/core/server/RpcServer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/core/server/RpcServer.class -------------------------------------------------------------------------------- /target/classes/rpc/tomstillcoding/com/core/server/RpcServerWorker.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/classes/rpc/tomstillcoding/com/core/server/RpcServerWorker.class -------------------------------------------------------------------------------- /target/test-classes/client/TestClient.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/test-classes/client/TestClient.class -------------------------------------------------------------------------------- /target/test-classes/server/HelloServiceImpl.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/test-classes/server/HelloServiceImpl.class -------------------------------------------------------------------------------- /target/test-classes/server/PingServiceImpl.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/test-classes/server/PingServiceImpl.class -------------------------------------------------------------------------------- /target/test-classes/server/TestServer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/test-classes/server/TestServer.class -------------------------------------------------------------------------------- /target/test-classes/test/Employee.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/test-classes/test/Employee.class -------------------------------------------------------------------------------- /target/test-classes/test/Test.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomstillcoding/Simple-RPC/4d3dc61f73bc462722123e47da9fded7c204d693/target/test-classes/test/Test.class --------------------------------------------------------------------------------