# rpc
**Repository Path**: herenpeng/rpc
## Basic Information
- **Project Name**: rpc
- **Description**: 基于 Netty 网络通信的一个简易的 RPC 框架
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 2
- **Forks**: 3
- **Created**: 2021-09-01
- **Last Updated**: 2024-06-27
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# RPC 框架
项目地址:https://github.com/herenpeng/rpc.git
- 基于 JDK17 开发
- 该项目是基于 Netty4 的一个简易的 RPC 框架
- 配置即实例,无需编写代码,根据配置内容自动实例化 RPC 实例
- 完备的服务启动、心跳监控、服务重连等机制
- 客户端支持动态代理,路径调用两种远程调用方式
- 支持同步和异步调用,异步支持函数式回调
- 支持 Json、Hessian、Kryo 三种序列化方式,可以通过配置自由切换,无需改动代码
- 支持自定义通信协议
## RPC 入门
RPC 框架的入门,首先要使用这个 RPC 框架,搭建一个简单的 RPC 环境,使得 RPC 客户端可以通过 RPC 协议远程调用 RPC 服务端。
1、从 GitHub 上下载项目,项目地址:`https://github.com/herenpeng/rpc.git`
2、使用 Maven 将项目安装到本地仓库中。
3、搭建一个简单的 Maven 工程。
在项目中,引入 rpc 框架的依赖:
```xml
com.herenpeng
rpc
1.0.0
ch.qos.logback
logback-classic
1.2.9
```
## 配置即实例
RPC 框架的配置文件为 resources 目录下的 rpc.yaml 文件,可以手动创建该文件。
一份完整的配置,即是一个完整的 RPC 对象,如果需要启动一个 RPC 客户端,可以使用以下的配置:
```yaml
rpc:
client:
name: MockRpcClient
host: 127.0.0.1
port: 10000
sync-timeout: 1000
reconnection-time: 1000
heartbeat-time: 10000
heartbeat-invalid-times: 3
serialize: 1
heartbeat-log-enable: false
monitor-log-enable: true
```
如果需要启动一个 RPC 服务端,可以使用以下的配置:
```yaml
rpc:
server:
name: MockRpcServer
port: 10000
heartbeat-log-enable: false
```
- 如果需要同时启动一个 RPC 服务端和一个 RPC 客户端,那么只需要将两份配置合并即可。
- 如果需要启动多个 RPC 服务端或者多个 RPC 客户端,则可以使用 `clients` 和 `servers` 属性,这两个属性是集合对象,可以配置多个 RPC 实例。
```yaml
rpc:
clients:
- name: MockRpcClient
host: 127.0.0.1
port: 10000
sync-timeout: 1000
reconnection-time: 1000
heartbeat-time: 10000
heartbeat-invalid-times: 3
serialize: 1
heartbeat-log-enable: false
monitor-log-enable: true
- name: MockRpcClient2
host: 127.0.0.1
port: 10000
sync-timeout: 1000
reconnection-time: 1000
heartbeat-time: 10000
heartbeat-invalid-times: 3
serialize: 1
heartbeat-log-enable: false
monitor-log-enable: true
servers:
- name: MockRpcServer
port: 10000
heartbeat-log-enable: false
worker-thread-num: 3
```
## 初始化RPC实例
> 在项目下创建`com.herenpeng.rpc`文件包,并在该包下创建`MockRpc.java`文件。
RPC 框架,可以直接使用`RpcRunner`启动类根据配置文件信息来初始化 RPC 实例:
```java
@RpcApplication
public class MockRpc {
public static void main(String[] args) throws Exception {
RpcRunner.run(MockRpc.class, args);
}
}
```
## RPC 服务代理调用
### 创建服务端接口
> 在项目`com.herenpeng.rpc`文件包下创建`UserService`接口和`UserServiceImpl`实现类。
```java
@RpcApi
public interface UserService {
User getUserInfo(String name);
User getUserInfo(String name, RpcCallback callback);
List getUserList();
List getUserList(RpcCallback> callback);
User updateUser(User user);
List updateUsers(List users);
}
```
```java
@RpcService
public class UserServiceImpl implements UserService {
@Override
public User getUserInfo(String name) {
return new User(15, name, true, 18, new Date(), new Date(), new Date());
}
@Override
public User getUserInfo(String name, RpcCallback callback) {
return getUserInfo(name);
}
@Override
public List getUserList() {
List list = new ArrayList<>();
list.add(new User(15, "小明", true, 18, new Date(), new Date(), new Date()));
list.add(new User(16, "小红", false, 21, new Date(), new Date(), new Date()));
list.add(new User(17, "小雷", true, 25, new Date(), new Date(), new Date()));
list.add(new User(18, "小刚", true, 29, new Date(), new Date(), new Date()));
list.add(new User(19, "小李", true, 42, new Date(), new Date(), new Date()));
list.add(new User(20, "小王", false, 28, new Date(), new Date(), new Date()));
list.add(new User(21, "小周", false, 35, new Date(), new Date(), new Date()));
return list;
}
@Override
public List getUserList(RpcCallback> callback) {
return getUserList();
}
@Override
public User updateUser(User user) {
user.setUsername("修改后的用户名");
return user;
}
@Override
public List updateUsers(List users) {
List list = new ArrayList<>();
list.add(new User(15, "小明2", true, 18, new Date(), new Date(), new Date()));
list.add(new User(16, "小红2", false, 21, new Date(), new Date(), new Date()));
list.add(new User(17, "小雷2", true, 25, new Date(), new Date(), new Date()));
list.add(new User(18, "小刚3", true, 29, new Date(), new Date(), new Date()));
list.add(new User(19, "小李4", true, 42, new Date(), new Date(), new Date()));
list.add(new User(20, "小王5", false, 28, new Date(), new Date(), new Date()));
list.add(new User(21, "小周6", false, 35, new Date(), new Date(), new Date()));
return list;
}
}
```
### 创建客户端接口
> 在项目`com.herenpeng.rpc`文件包下创建 `UserService` 接口,`UserService`可以和服务端使用同一个接口文件。
### RPC客户端调用
> 启动`MockRpc`类。
```java
@RpcApplication
public class MockRpc {
private static final String MockRpcClient = "MockRpcClient";
public static void main(String[] args) throws Exception {
RpcRunner.run(MockRpc.class, args);
Thread.sleep(1500);
UserService userService = RpcClient.createRpc(MockRpcClient, UserService.class);
// 服务代理调用,返回 User 对象
rpcServerProxyReturnUser(userService);
// 服务代理调用,返回 List 对象
rpcServerProxyReturnUserList(userService);
// 服务代理调用,传递参数是 User 对象
rpcServerProxyParamUser(userService);
// 服务代理调用,传递参数是 List 对象
rpcServerProxyParamUserList(userService);
}
private static void rpcServerProxyReturnUser(UserService userService) {
User user = userService.getUserInfo("肖总");
System.err.println("同步调用1 =====> " + user);
userService.getUserInfo("肖总", (data) -> {
System.err.println("异步调用2 =====> " + data);
});
}
private static void rpcServerProxyReturnUserList(UserService userService) {
List userList = userService.getUserList();
for (User user : userList) {
System.err.println("同步调用3 =====> " + user.getUsername());
}
userService.getUserList((data) -> {
for (User user : data) {
System.err.println("异步调用4 =====> " + user.getUsername());
}
});
}
public static void rpcServerProxyParamUser(UserService userService) {
User user = userService.updateUser(new User(21, "小周", false, 35, new Date(), new Date(), new Date()));
System.err.println("同步调用10 =====> " + user);
}
public static void rpcServerProxyParamUserList(UserService userService) {
List list = new ArrayList<>();
list.add(new User(15, "小明", true, 18, new Date(), new Date(), new Date()));
list.add(new User(16, "小红", false, 21, new Date(), new Date(), new Date()));
list.add(new User(17, "小雷", true, 25, new Date(), new Date(), new Date()));
list.add(new User(18, "小刚", true, 29, new Date(), new Date(), new Date()));
list.add(new User(19, "小李", true, 42, new Date(), new Date(), new Date()));
list.add(new User(20, "小王", false, 28, new Date(), new Date(), new Date()));
list.add(new User(21, "小周", false, 35, new Date(), new Date(), new Date()));
List userList = userService.updateUsers(list);
for (User user : userList) {
System.err.println("同步调用11 =====> " + user.getId() + " --- " + user.getUsername());
}
}
}
```
## RPC 路径式调用
### 创建服务端接口
```java
@RpcService("department")
public class DepartmentService {
@RpcMethod("get")
public Department get(String name) {
return new Department(1, name, new Date(), new Date());
}
@RpcMethod("list")
public List list() {
List list = new ArrayList<>();
list.add(new Department(1, "行政部", new Date(), new Date()));
list.add(new Department(2, "财务部", new Date(), new Date()));
list.add(new Department(3, "技术部", new Date(), new Date()));
list.add(new Department(4, "人事部", new Date(), new Date()));
list.add(new Department(5, "运营部", new Date(), new Date()));
list.add(new Department(6, "公关部", new Date(), new Date()));
return list;
}
}
```
### RPC客户端调用
```java
@RpcApplication
public class MockRpc {
private static final String MockRpcClient = "MockRpcClient";
public static void main(String[] args) throws Exception {
RpcRunner.run(MockRpc.class, args);
Thread.sleep(1500);
// 路径调用,返回 Department 对象
rpcPathReturnDepartment();
// 路径调用,返回 Department[] 数组对象
// rpcPathReturnDepartmentArray();
// 路径调用,返回 List 集合对象
rpcPathReturnDepartmentList();
}
private static void rpcPathReturnDepartment() {
Department department = RpcClient.get(MockRpcClient, "/department/get", Department.class, "技术部");
System.err.println("路径式同步调用5 =====> " + department);
RpcClient.get(MockRpcClient, "/department/get", Department.class, (data) -> {
System.err.println("路径式异步调用6 =====> " + data);
}, "技术部");
}
private static void rpcPathReturnDepartmentArray() {
Department[] departmentList = RpcClient.get(MockRpcClient, "/department/list", Department[].class);
for (Department dept : departmentList) {
System.err.println("路径式同步调用7 =====> " + dept.getId() + "---" + dept.getName());
}
RpcClient.get(MockRpcClient, "/department/list", Department[].class, (list) -> {
for (Department department : list) {
System.err.println("路径式异步调用8 =====> " + department.getId() + "---" + department.getName());
}
});
}
private static void rpcPathReturnDepartmentList() {
RpcClient.get(MockRpcClient, "/department/list", new ValueType>() {
}, (list) -> {
for (Department department : list) {
System.err.println("路径式异步调用9 =====> " + department.getId() + "---" + department.getName());
}
});
}
}
```
## 切换协议序列化方式
RPC 框架支持多种序列化方式,开发者可以通过修改配置的方式,自由切换序列化方式,无需改动代码文件。
```yaml
rpc:
client:
serialize: 1
```
配置文件中,`rpc.client.serialize` 属性为 RPC 实例支持的序列化方式,配置值和序列化对应方式如下:
| 配置值 | 序列化方式 |
| --- | --- |
| 1 | Json |
| 2 | Hessian |
| 3 | Kryo |
## Rpc服务器性能监控
提供了性能监控工具,使用监控工具,可以直接查看服务器性能数据。
```java
MonitorKit.monitor("127.0.0.1", 10000);
```
## 注意
1、rpc的调用规则为相同的包名调用,服务端和客户端的方法接口必须要在相同的包名下。
2、rpc的服务端接口必须要注解`@RpcService`,客户端接口必须要注解`@RpcApi`。
3、rpc的异步调用,接口方法参数中多一个`RpcCallback`的函数式接口参数,用于处理rpc的异步回调事件,服务端该参数为`null`。
4、如果使用 Hessian 作为序列化工具,所有需要被序列化的实体类,都要实现 `java.io.Serializable` 接口,否则无法序列化。
## 未来预期功能
- 运行时 RPC 操作
- 配置查询及更新