本文为笔者阅读鱼皮的项目 《简易版 RPC 框架开发》的笔记,如果有时间可以直接去看原文,1. 简易版 RPC 框架开发
前面的内容可以笔者的看第一篇笔记
引用:
1.项目结构
2.项目运行原理及调试过程
RpcServerExample类(服务器)
package com.yupi.yurpc.example;
import com.yupi.yurpc.registry.LocalRegistry;
import com.yupi.yurpc.server.HttpServer;
import com.yupi.yurpc.server.VertxHttpServer;
import java.util.Scanner;
/**
* RPC 服务器示例
*
*/
public class RpcServerExample {
public static void main(String[] args) {
// 注册服务
LocalRegistry.register(UserService.class.getName(), UserServiceImpl.class);
// 获取端口号
int port = getPort(args);
System.out.println(" 启动RPC服务器...");
System.out.println(" 已注册服务: " + UserService.class.getName());
// 启动 web 服务
HttpServer httpServer = new VertxHttpServer();
httpServer.doStart(port);
// 保持服务器运行
System.out.println(" 服务器运行中,按 Ctrl+C 停止...");
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
System.out.println(" 服务器已停止");
}
}
/**
* 获取端口号
*/
private static int getPort(String[] args) {
int defaultPort = 8080;
// 从命令行参数获取端口
if (args.length > 0) {
try {
int port = Integer.parseInt(args[0]);
if (port > 0 && port < 65536) {
return port;
}
} catch (NumberFormatException e) {
System.err.println("️ 无效的端口号: " + args[0] + ",使用默认端口: " + defaultPort);
}
}
// 如果端口被占用,尝试其他端口
Scanner scanner = new Scanner(System.in);
System.out.print("🔧 请输入端口号 (默认 " + defaultPort + "): ");
String input = scanner.nextLine().trim();
if (!input.isEmpty()) {
try {
int port = Integer.parseInt(input);
if (port > 0 && port < 65536) {
return port;
}
} catch (NumberFormatException e) {
System.err.println(" 无效的端口号,使用默认端口: " + defaultPort);
}
}
return defaultPort;
}
}
启动服务端
注册服务
RpcServerExample类
// 注册服务
LocalRegistry.register(UserService.class.getName(), UserServiceImpl.class);
UserService.class.getName()
获取接口UserService
的全限定类名(如com.example.UserService
),作为服务的唯一标识 Key。
UserServiceImpl.class
接口UserService
的具体实现类(需实现该接口)。
LocalRegistry.register()
将接口与实现类绑定到内存中的注册表(通常是Map<String, Class<?>>
结构)。将服务标识和具体实现类绑定
LocalRegistry类(本地注册中心)
package com.yupi.yurpc.registry;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 本地注册中心
*/
public class LocalRegistry {
/**
* 注册信息存储
*/
private static final Map<String, Class<?>> map = new ConcurrentHashMap<>();
/**
* 注册服务
*
* @param serviceName
* @param implClass
*/
public static void register(String serviceName, Class<?> implClass) {
map.put(serviceName, implClass);
}
/**
* 获取服务
*
* @param serviceName
* @return
*/
public static Class<?> get(String serviceName) {
return map.get(serviceName);
}
/**
* 删除服务
*
* @param serviceName
*/
public static void remove(String serviceName) {
map.remove(serviceName);
}
}
注册服务的实现
public static void register(String serviceName, Class<?> implClass) { map.put(serviceName, implClass); // 基础注册逻辑 }
组件 | 说明 |
---|---|
map |
静态注册表(通常为 Map<String, Class<?>> 类型) |
serviceName |
服务标识(建议使用接口全限定名) |
implClass |
服务实现类(需实现 serviceName 对应的接口) |
将UserService接口与UserServiceImpl实现类绑定到内存中的注册表
UserService接口(用户服务接口)
package com.yupi.yurpc.example;
/**
* 用户服务接口
*/
public interface UserService {
/**
* 获取用户信息
*
* @param name 用户名
* @return 用户信息
*/
String getUser(String name);
/**
* 计算两个数的和
*
* @param a 第一个数
* @param b 第二个数
* @return 和
*/
int add(int a, int b);
}
UserServiceImpl (用户服务实现类)
(UserService接口的实现)
package com.yupi.yurpc.example;
/**
* 用户服务实现类
*/
public class UserServiceImpl implements UserService {
@Override
public String getUser(String name) {
return "Hello, " + name + "!";
}
@Override
public int add(int a, int b) {
return a + b;
}
}
LocalRegistry类绑定成功:
RpcServerExample类端口的获得
端口默认为8080
java.lang.IndexOutOfBoundsException: Invalid array range: 0 to 0
错误表示正在尝试访问或操作数组的一个无效范围。错误的核心原因是:尝试在数组或集合上执行一个范围操作(如复制、切片等),但指定的范围(0到0)对于当前数据结构是无效的。
这里无需在意
然后从命令行参数获取端口(这里没有开命令行,所以会直接跳过)
如果端口被占用,尝试其他端口
笔者的8080端口确实被占用了,所以使用的为9090
然后就是对输入和端口的处理直接跳过了
下面的调试图为实现后的展示
判断输入是否为空,如果不是就可以进行下面的判断了
这里的判断比较简单,就是判断端口是否合理。如果合理则返回,否则返回报错和提示语句。
直接跳过了
然后就是返回注册成功的提示
下一步启动注册服务
VertxHttpServer类(Vertx HTTP 服务器)
继承了HttpServer接口
package com.yupi.yurpc.server;
import io.vertx.core.Vertx;
/**
* Vertx HTTP 服务器
*/
public class VertxHttpServer implements HttpServer {
/**
* 启动服务器
*
* @param port
*/
public void doStart(int port) {
// 创建 Vert.x 实例
Vertx vertx = Vertx.vertx();
// 创建 HTTP 服务器
io.vertx.core.http.HttpServer server = vertx.createHttpServer();
// 监听端口并处理请求
server.requestHandler(new HttpServerHandler());
// 启动 HTTP 服务器并监听指定端口
server.listen(port, result -> {
if (result.succeeded()) {
System.out.println(" RPC服务器启动成功,监听端口: " + port);
System.out.println(" 服务器地址: http://localhost:" + port);
} else {
System.err.println(" 服务器启动失败: " + result.cause().getMessage());
System.err.println(" 请尝试以下解决方案:");
System.err.println(" 1. 检查端口 " + port + " 是否被其他程序占用");
System.err.println(" 2. 尝试使用其他端口号");
System.err.println(" 3. 以管理员权限运行程序");
System.exit(1);
}
});
}
}
HttpServer接口(HTTP 服务器接口)
package com.yupi.yurpc.server;
/**
* HTTP 服务器接口
*/
public interface HttpServer {
/**
* 启动服务器
*
* @param port
*/
void doStart(int port);
}
监听端口并且处理请求
HttpServerHandler类( HTTP 请求处理)
package com.yupi.yurpc.server;
import com.yupi.yurpc.model.RpcRequest;
import com.yupi.yurpc.model.RpcResponse;
import com.yupi.yurpc.registry.LocalRegistry;
import com.yupi.yurpc.serializer.JdkSerializer;
import com.yupi.yurpc.serializer.Serializer;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* HTTP 请求处理
*/
public class HttpServerHandler implements Handler<HttpServerRequest> {
@Override
public void handle(HttpServerRequest request) {
// 指定序列化器
final Serializer serializer = new JdkSerializer();
// 记录日志
System.out.println("Received request: " + request.method() + " " + request.uri());
// 异步处理 HTTP 请求
request.bodyHandler(body -> {
byte[] bytes = body.getBytes();
RpcRequest rpcRequest = null;
try {
rpcRequest = serializer.deserialize(bytes, RpcRequest.class);
} catch (Exception e) {
e.printStackTrace();
}
// 构造响应结果对象
RpcResponse rpcResponse = new RpcResponse();
// 如果请求为 null,直接返回
if (rpcRequest == null) {
rpcResponse.setMessage("rpcRequest is null");
doResponse(request, rpcResponse, serializer);
return;
}
try {
// 获取要调用的服务实现类,通过反射调用
Class<?> implClass = LocalRegistry.get(rpcRequest.getServiceName());
if (implClass == null) {
rpcResponse.setMessage("Service not found: " + rpcRequest.getServiceName());
doResponse(request, rpcResponse, serializer);
return;
}
Method method = implClass.getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes());
Object result = method.invoke(implClass.newInstance(), rpcRequest.getArgs());
// 封装返回结果
rpcResponse.setData(result);
rpcResponse.setDataType(method.getReturnType());
rpcResponse.setMessage("ok");
} catch (Exception e) {
e.printStackTrace();
rpcResponse.setMessage(e.getMessage());
rpcResponse.setException(e);
}
// 响应
doResponse(request, rpcResponse, serializer);
});
}
/**
* 响应
*
* @param request
* @param rpcResponse
* @param serializer
*/
void doResponse(HttpServerRequest request, RpcResponse rpcResponse, Serializer serializer) {
HttpServerResponse httpServerResponse = request.response()
.putHeader("content-type", "application/json");
try {
// 序列化
byte[] serialized = serializer.serialize(rpcResponse);
httpServerResponse.end(Buffer.buffer(serialized));
} catch (IOException e) {
e.printStackTrace();
httpServe
启动服务并且监听端口
RpcRequest类(RPC 请求)
package com.yupi.yurpc.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* RPC 请求
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RpcRequest implements Serializable {
/**
* 服务名称
*/
private String serviceName;
/**
* 方法名称
*/
private String methodName;
/**
* 参数类型列表
*/
private Class<?>[] parameterTypes;
/**
* 参数列表
*/
private Object[] args;
}
RpcResponse类( RPC 响应)
package com.yupi.yurpc.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* RPC 响应
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RpcResponse implements Serializable {
/**
* 响应数据
*/
private Object data;
/**
* 响应数据类型(预留)
*/
private Class<?> dataType;
/**
* 响应信息
*/
private String message;
/**
* 异常信息
*/
private Exception exception;
}
测试用例,JDK序列化和服务代理(JDK 动态代理)下次分析
下面是代码
RpcClientExample类( RPC 客户端示例)
package com.yupi.yurpc.example;
import com.yupi.yurpc.proxy.ServiceProxyFactory;
import java.util.Scanner;
/**
* RPC 客户端示例
*/
public class RpcClientExample {
public static void main(String[] args) {
// 配置服务器地址
configureServerUrl(args);
System.out.println("连接到RPC服务器: " + System.getProperty("rpc.server.url", "http://localhost:8080"));
System.out.println("开始RPC调用测试...");
// 获取代理对象
UserService userService = ServiceProxyFactory.getProxy(UserService.class);
try {
// 调用方法
System.out.println("\n 测试 getUser 方法:");
String result1 = userService.getUser("张三");
System.out.println(" getUser result: " + result1);
System.out.println("\n 测试 add 方法:");
int result2 = userService.add(7, 2);
System.out.println(" add result: " + result2);
System.out.println("\n 所有测试通过!");
} catch (Exception e) {
System.err.println(" RPC调用失败: " + e.getMessage());
System.err.println(" 请确保服务器已启动并且地址正确");
}
}
/**
* 配置服务器地址
*/
private static void configureServerUrl(String[] args) {
String defaultUrl = "http://localhost:8080";
// 从命令行参数获取服务器地址
if (args.length > 0) {
String url = args[0];
if (url.startsWith("http://") || url.startsWith("https://")) {
System.setProperty("rpc.server.url", url);
return;
} else {
// 如果只提供了端口号,构造完整URL
try {
int port = Integer.parseInt(url);
System.setProperty("rpc.server.url", "http://localhost:" + port);
return;
} catch (NumberFormatException e) {
System.err.println(" 无效的服务器地址: " + url);
}
}
}
// 交互式配置
Scanner scanner = new Scanner(System.in);
System.out.print("🔧 请输入服务器地址 (默认 " + defaultUrl + "): ");
String input = scanner.nextLine().trim();
if (!input.isEmpty()) {
if (input.startsWith("http://") || input.startsWith("https://")) {
System.setProperty("rpc.server.url", input);
} else {
try {
int port = Integer.parseInt(input);
System.setProperty("rpc.server.url", "http://localhost:" + port);
} catch (NumberFormatException e) {
System.err.println(" 无效的地址,使用默认地址: " + defaultUrl);
System.setProperty("rpc.server.url", defaultUrl);
}
}
} else {
System.setProperty("rpc.server.url", defaultUrl);
}
}
}
ServiceProxy(服务代理(JDK 动态代理))
package com.yupi.yurpc.proxy;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.yupi.yurpc.model.RpcRequest;
import com.yupi.yurpc.model.RpcResponse;
import com.yupi.yurpc.serializer.JdkSerializer;
import com.yupi.yurpc.serializer.Serializer;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 服务代理(JDK 动态代理)
*/
public class ServiceProxy implements InvocationHandler {
/**
* 调用代理
*
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 指定序列化器
Serializer serializer = new JdkSerializer();
// 构造请求
RpcRequest rpcRequest = RpcRequest.builder()
.serviceName(method.getDeclaringClass().getName())
.methodName(method.getName())
.parameterTypes(method.getParameterTypes())
.args(args)
.build();
try {
// 序列化
byte[] bodyBytes = serializer.serialize(rpcRequest);
// 发送请求
// todo 注意,这里地址被硬编码了(需要使用注册中心和服务发现机制解决)
String serverUrl = System.getProperty("rpc.server.url", "http://localhost:8080");
try (HttpResponse httpResponse = HttpRequest.post(serverUrl)
.body(bodyBytes)
.execute()) {
byte[] result = httpResponse.bodyBytes();
// 反序列化
RpcResponse rpcResponse = serializer.deserialize(result, RpcResponse.class);
if (rpcResponse.getException() != null) {
throw new RuntimeException("RPC调用异常: " + rpcResponse.getMessage(), rpcResponse.getException());
}
return rpcResponse.getData();
}
} catch (IOException e) {
throw new RuntimeException("网络请求异常", e);
}
}
}
ServiceProxyFactory类( 服务代理工厂(用于创建代理对象))
package com.yupi.yurpc.proxy;
import java.lang.reflect.Proxy;
/**
* 服务代理工厂(用于创建代理对象)
*/
public class ServiceProxyFactory {
/**
* 根据服务类获取代理对象
*
* @param serviceClass
* @param <T>
* @return
*/
public static <T> T getProxy(Class<T> serviceClass) {
return (T) Proxy.newProxyInstance(
serviceClass.getClassLoader(),
new Class[]{serviceClass},
new ServiceProxy());
}
}
JdkSerializer类(JDK 序列化器)
package com.yupi.yurpc.serializer;
import java.io.*;
/**
* JDK 序列化器
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @learn <a href="https://codefather.cn">编程宝典</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
public class JdkSerializer implements Serializer {
/**
* 序列化
*
* @param object
* @param <T>
* @return
* @throws IOException
*/
@Override
public <T> byte[] serialize(T object) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream)) {
objectOutputStream.writeObject(object);
return outputStream.toByteArray();
}
}
/**
* 反序列化
*
* @param bytes
* @param type
* @param <T>
* @return
* @throws IOException
*/
@Override
public <T> T deserialize(byte[] bytes, Class<T> type) throws IOException {
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
try {
return (T) objectInputStream.readObject();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
objectInputStream.close();
}
}
}
Serializer类( 序列化器接口)
package com.yupi.yurpc.serializer;
import java.io.IOException;
/**
* 序列化器接口
*
* @author <a href="https://github.com/liyupi">程序员鱼皮</a>
* @learn <a href="https://codefather.cn">编程宝典</a>
* @from <a href="https://yupi.icu">编程导航知识星球</a>
*/
public interface Serializer {
/**
* 序列化
*
* @param object
* @param <T>
* @return
* @throws IOException
*/
<T> byte[] serialize(T object) throws IOException;
/**
* 反序列化
*
* @param bytes
* @param type
* @param <T>
* @return
* @throws IOException
*/
<T> T deserialize(byte[] bytes, Class<T> type) throws IOException;
}
状态分析
# Yu-RPC-Easy 状态图
## 1. 服务器生命周期状态图
```mermaid
stateDiagram-v2
[*] --> 初始化
初始化 --> 服务注册 : 注册服务
服务注册 --> 启动中 : 启动HTTP服务器
启动中 --> 运行中 : 服务器启动成功
启动中 --> 启动失败 : 端口被占用/其他错误
启动失败 --> 初始化 : 重新配置
运行中 --> 处理请求 : 接收HTTP请求
处理请求 --> 请求解析 : 读取请求体
请求解析 --> 服务查找 : 反序列化成功
请求解析 --> 错误响应 : 反序列化失败
服务查找 --> 方法调用 : 服务存在
服务查找 --> 服务未找到 : 服务不存在
方法调用 --> 响应封装 : 调用成功
方法调用 --> 异常处理 : 调用失败
响应封装 --> 响应发送 : 序列化成功
异常处理 --> 响应发送 : 封装异常信息
错误响应 --> 响应发送
服务未找到 --> 响应发送
响应发送 --> 运行中 : 等待下一个请求
运行中 --> 关闭中 : 收到关闭信号
关闭中 --> 已关闭 : 清理完成
已关闭 --> [*]
```
## 2. 客户端代理状态图
```mermaid
stateDiagram-v2
[*] --> 代理创建
代理创建 --> 等待调用 : 代理对象创建成功
等待调用 --> 方法拦截 : 客户端调用方法
方法拦截 --> 请求构建 : 获取方法信息
请求构建 --> 序列化 : 构造RpcRequest
序列化 --> 网络发送 : 序列化成功
序列化 --> 序列化失败 : 序列化异常
网络发送 --> 等待响应 : 发送成功
网络发送 --> 网络异常 : 连接失败
等待响应 --> 响应接收 : 收到服务器响应
等待响应 --> 超时异常 : 响应超时
响应接收 --> 反序列化 : 读取响应数据
反序列化 --> 结果返回 : 反序列化成功
反序列化 --> 反序列化失败 : 反序列化异常
结果返回 --> 等待调用 : 返回给客户端
序列化失败 --> 异常返回 : 抛出RuntimeException
网络异常 --> 异常返回
超时异常 --> 异常返回
反序列化失败 --> 异常返回
异常返回 --> 等待调用
```
## 3. 注册中心状态图
```mermaid
stateDiagram-v2
[*] --> 注册中心初始化
注册中心初始化 --> 等待注册 : 创建ConcurrentHashMap
等待注册 --> 服务注册 : 收到注册请求
服务注册 --> 注册成功 : 存储成功
服务注册 --> 注册失败 : 存储异常
注册成功 --> 等待注册 : 继续等待
注册失败 --> 等待注册 : 继续等待
等待注册 --> 服务查找 : 收到查找请求
服务查找 --> 服务存在 : 找到服务
服务查找 --> 服务不存在 : 未找到服务
服务存在 --> 等待注册 : 返回服务类
服务不存在 --> 等待注册 : 返回null
等待注册 --> 服务注销 : 收到注销请求
服务注销 --> 注销成功 : 删除成功
服务注销 --> 注销失败 : 删除异常
注销成功 --> 等待注册 : 继续等待
注销失败 --> 等待注册 : 继续等待
```
## 4. 序列化器状态图
```mermaid
stateDiagram-v2
[*] --> 序列化器初始化
序列化器初始化 --> 等待序列化 : 创建序列化器实例
等待序列化 --> 序列化处理 : 收到序列化请求
序列化处理 --> 序列化成功 : 序列化完成
序列化处理 --> 序列化失败 : 序列化异常
序列化成功 --> 等待序列化 : 返回字节数组
序列化失败 --> 异常处理 : 抛出IOException
异常处理 --> 等待序列化 : 继续等待
等待序列化 --> 反序列化处理 : 收到反序列化请求
反序列化处理 --> 反序列化成功 : 反序列化完成
反序列化处理 --> 反序列化失败 : 反序列化异常
反序列化成功 --> 等待序列化 : 返回对象
反序列化失败 --> 异常处理 : 抛出IOException
```
## 5. HTTP请求处理状态图
```mermaid
stateDiagram-v2
[*] --> 请求接收
请求接收 --> 请求验证 : 接收HTTP请求
请求验证 --> 请求有效 : 验证通过
请求验证 --> 请求无效 : 验证失败
请求有效 --> 请求解析 : 开始解析
请求解析 --> 解析成功 : 解析完成
请求解析 --> 解析失败 : 解析异常
解析成功 --> 服务处理 : 开始处理
服务处理 --> 处理成功 : 处理完成
服务处理 --> 处理失败 : 处理异常
处理成功 --> 响应构建 : 构建响应
处理失败 --> 错误响应 : 构建错误响应
解析失败 --> 错误响应
请求无效 --> 错误响应
响应构建 --> 响应发送 : 发送响应
错误响应 --> 响应发送
响应发送 --> 请求完成 : 响应发送完成
请求完成 --> [*]
```
## 6. 异常处理状态图
```mermaid
stateDiagram-v2
[*] --> 正常运行
正常运行 --> 网络异常 : 网络连接失败
正常运行 --> 序列化异常 : 序列化失败
正常运行 --> 服务异常 : 服务调用失败
正常运行 --> 超时异常 : 请求超时
网络异常 --> 重试机制 : 尝试重试
重试机制 --> 重试成功 : 重试成功
重试机制 --> 重试失败 : 重试次数用完
重试成功 --> 正常运行 : 恢复正常
重试失败 --> 异常返回 : 返回网络错误
序列化异常 --> 异常返回 : 返回序列化错误
服务异常 --> 异常返回 : 返回服务错误
超时异常 --> 异常返回 : 返回超时错误
异常返回 --> 正常运行 : 等待下一个请求
```
## 7. 连接池状态图
```mermaid
stateDiagram-v2
[*] --> 连接池初始化
连接池初始化 --> 空闲状态 : 创建连接池
空闲状态 --> 获取连接 : 客户端请求连接
获取连接 --> 连接可用 : 有可用连接
获取连接 --> 创建连接 : 无可用连接
连接可用 --> 使用中 : 分配连接
创建连接 --> 连接创建成功 : 创建成功
创建连接 --> 连接创建失败 : 创建失败
连接创建成功 --> 使用中 : 使用新连接
连接创建失败 --> 连接失败 : 返回错误
使用中 --> 请求处理 : 处理HTTP请求
请求处理 --> 响应完成 : 请求处理完成
响应完成 --> 释放连接 : 释放连接
释放连接 --> 空闲状态 : 连接回到池中
连接失败 --> 空闲状态 : 继续等待
```
## 8. 性能监控状态图
```mermaid
stateDiagram-v2
[*] --> 监控初始化
监控初始化 --> 监控运行 : 启动监控
监控运行 --> 请求开始 : 收到请求
请求开始 --> 请求处理中 : 开始处理
请求处理中 --> 请求完成 : 处理完成
请求处理中 --> 请求超时 : 处理超时
请求完成 --> 统计更新 : 更新统计信息
请求超时 --> 统计更新 : 更新超时统计
统计更新 --> 监控运行 : 继续监控
监控运行 --> 性能告警 : 性能指标异常
性能告警 --> 监控运行 : 继续监控
监控运行 --> 监控停止 : 停止监控
监控停止 --> [*]
```
## 9. 服务发现状态图
```mermaid
stateDiagram-v2
[*] --> 服务发现初始化
服务发现初始化 --> 等待发现 : 初始化完成
等待发现 --> 服务查找 : 收到查找请求
服务查找 --> 本地查找 : 在本地注册中心查找
本地查找 --> 服务存在 : 找到服务
本地查找 --> 服务不存在 : 未找到服务
服务存在 --> 服务可用 : 服务可用
服务存在 --> 服务不可用 : 服务不可用
服务可用 --> 返回服务 : 返回服务信息
服务不可用 --> 服务不存在 : 标记为不存在
服务不存在 --> 返回空 : 返回null
返回服务 --> 等待发现 : 继续等待
返回空 --> 等待发现 : 继续等待
```
## 10. 配置管理状态图
```mermaid
stateDiagram-v2
[*] --> 配置初始化
配置初始化 --> 默认配置 : 加载默认配置
默认配置 --> 配置验证 : 验证配置
配置验证 --> 配置有效 : 验证通过
配置验证 --> 配置无效 : 验证失败
配置有效 --> 配置应用 : 应用配置
配置应用 --> 配置完成 : 配置应用成功
配置完成 --> 运行中 : 开始运行
配置无效 --> 配置修复 : 修复配置
配置修复 --> 配置验证 : 重新验证
运行中 --> 配置更新 : 收到配置更新
配置更新 --> 配置验证 : 验证新配置
运行中 --> 配置完成 : 程序结束
配置完成 --> [*]
```
流程图
# Yu-RPC-Easy 项目流程图
## 1. 整体架构流程图
```mermaid
graph TB
A[客户端] --> B[代理对象]
B --> C[序列化]
C --> D[网络传输]
D --> E[HTTP服务器]
E --> F[请求处理]
F --> G[服务发现]
G --> H[反射调用]
H --> I[序列化响应]
I --> J[网络返回]
J --> K[反序列化]
K --> L[返回结果]
M[服务注册] --> N[注册中心]
G --> N
```
## 2. 服务注册流程
```mermaid
sequenceDiagram
participant SP as 服务提供者
participant RC as 注册中心
participant SM as 服务管理器
SP->>SM: 注册服务(serviceName, implClass)
SM->>RC: register(serviceName, implClass)
RC->>RC: 存储到ConcurrentHashMap
RC-->>SM: 注册成功
SM-->>SP: 注册完成
```
## 3. 客户端调用流程
```mermaid
sequenceDiagram
participant Client as 客户端
participant Proxy as 代理对象
participant Serializer as 序列化器
participant Network as 网络层
participant Server as 服务器
Client->>Proxy: 调用方法(method, args)
Proxy->>Proxy: 构造RpcRequest
Proxy->>Serializer: 序列化请求
Serializer-->>Proxy: 字节数组
Proxy->>Network: 发送HTTP POST
Network->>Server: 传输请求
Server->>Server: 处理请求
Server-->>Network: 返回响应
Network-->>Proxy: 接收响应
Proxy->>Serializer: 反序列化响应
Serializer-->>Proxy: RpcResponse对象
Proxy-->>Client: 返回结果
```
## 4. 服务端处理流程
```mermaid
flowchart TD
A[接收HTTP请求] --> B[读取请求体]
B --> C[反序列化RpcRequest]
C --> D{请求是否有效?}
D -->|否| E[返回错误响应]
D -->|是| F[从注册中心查找服务]
F --> G{服务是否存在?}
G -->|否| H[返回服务未找到]
G -->|是| I[获取方法信息]
I --> J[反射调用目标方法]
J --> K{调用是否成功?}
K -->|否| L[封装异常信息]
K -->|是| M[封装返回结果]
L --> N[序列化响应]
M --> N
N --> O[发送HTTP响应]
E --> O
H --> O
```
## 5. 序列化流程
```mermaid
graph LR
A[对象] --> B[序列化器]
B --> C[字节数组]
C --> D[网络传输]
D --> E[字节数组]
E --> F[反序列化器]
F --> G[对象]
```
## 6. 异常处理流程
```mermaid
flowchart TD
A[发生异常] --> B{异常类型?}
B -->|网络异常| C[重试机制]
B -->|序列化异常| D[返回序列化错误]
B -->|服务不存在| E[返回服务未找到]
B -->|方法调用异常| F[返回调用异常]
C --> G{重试次数?}
G -->|未超限| H[重新发送请求]
G -->|已超限| I[返回网络错误]
H --> J{是否成功?}
J -->|是| K[正常返回]
J -->|否| G
D --> L[客户端处理]
E --> L
F --> L
I --> L
K --> L
```
## 7. 并发处理流程
```mermaid
graph TB
A[多个客户端请求] --> B[Vert.x事件循环]
B --> C[异步处理请求1]
B --> D[异步处理请求2]
B --> E[异步处理请求N]
C --> F[线程池执行]
D --> F
E --> F
F --> G[并发访问注册中心]
G --> H[ConcurrentHashMap]
H --> I[返回处理结果]
```
## 8. 启动流程
```mermaid
sequenceDiagram
participant App as 应用程序
participant Registry as 注册中心
participant Server as HTTP服务器
participant Handler as 请求处理器
App->>Registry: 注册服务
Registry-->>App: 注册成功
App->>Server: 启动服务器(port)
Server->>Handler: 设置请求处理器
Server->>Server: 监听端口
Server-->>App: 服务器启动成功
App->>App: 等待请求
```
## 9. 关闭流程
```mermaid
flowchart TD
A[接收关闭信号] --> B[停止接收新请求]
B --> C[等待当前请求完成]
C --> D[关闭HTTP服务器]
D --> E[清理注册中心]
E --> F[释放资源]
F --> G[程序退出]
```
## 10. 性能监控流程
```mermaid
graph LR
A[请求开始] --> B[记录开始时间]
B --> C[处理请求]
C --> D[记录结束时间]
D --> E[计算响应时间]
E --> F[更新统计信息]
F --> G[返回结果]
```
优势
优势 | 说明 |
---|---|
解耦 | 调用方只需依赖接口,无需知道具体实现类 |
动态替换 | 修改注册的实现类即可切换功能(如替换为 UserServiceMock.class 做测试) |
集中管理 | 所有服务绑定关系在注册中心统一维护 |
对比远程注册中心
类型 | 本地注册 (LocalRegistry ) |
远程注册中心 (如 ZooKeeper/Nacos) |
---|---|---|
存储位置 | 应用内存中 | 独立中间件服务器 |
适用场景 | 单机/进程内服务调用 | 分布式跨进程服务发现 |
性能 | 无网络开销,速度极快 | 需网络通信,有延迟 |
服务发现 | 直接通过接口名获取 | 需从注册中心拉取服务地址列表 |
补充
register()
方法的核心作用
核心目的:建立 接口(抽象) 与 具体实现类 的映射关系,实现服务解耦。
典型方法签名:
public static void register(String serviceName, Class<?> implClass) {
// 实现逻辑
}
参数解析:
参数 | 类型 | 作用 | 示例 |
---|---|---|---|
serviceName |
String |
服务唯一标识 (通常用接口全限定名) | "com.example.UserService" |
implClass |
Class<?> |
接口的具体实现类 | UserServiceImpl.class |
map.put
public static void register(String serviceName, Class<?> implClass) { map.put(serviceName, implClass); // 基础注册逻辑 }
组件 | 说明 |
---|---|
map |
静态注册表(通常为 Map<String, Class<?>> 类型) |
serviceName |
服务标识(建议使用接口全限定名) |
implClass |
服务实现类(需实现 serviceName 对应的接口) |
trim()
trim()
是一个常用的字符串处理方法,用于移除字符串两端的空白字符。
端口号解析工具
int port = Integer.parseInt(input);
用于将字符串输入转换为整数端口号
Vert.x实例
// 创建 Vert.x 实例 Vertx vertx = Vertx.vertx(); // 创建 HTTP 服务器 io.vertx.core.http.HttpServer server = vertx.createHttpServer();