鱼皮项目简易版 RPC 框架开发(二)
本文为笔者阅读鱼皮的项目 《简易版 RPC 框架开发》的笔记,如果有时间可以直接去看原文,1. 简易版 RPC 框架开发
前面的内容可以笔者的看第一篇笔记
鱼皮项目简易版 RPC 框架开发(一)
引用:
1. 简易版 RPC 框架开发
鱼皮项目简易版 RPC 框架开发(一)
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 {@Overridepublic String getUser(String name) {return "Hello, " + name + "!";}@Overridepublic 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> {@Overridepublic 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 {// 如果只提供了端口号,构造完整URLtry {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*/@Overridepublic 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*/@Overridepublic <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*/@Overridepublic <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 TBA[客户端] --> 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
sequenceDiagramparticipant SP as 服务提供者participant RC as 注册中心participant SM as 服务管理器SP->>SM: 注册服务(serviceName, implClass)SM->>RC: register(serviceName, implClass)RC->>RC: 存储到ConcurrentHashMapRC-->>SM: 注册成功SM-->>SP: 注册完成
```## 3. 客户端调用流程```mermaid
sequenceDiagramparticipant Client as 客户端participant Proxy as 代理对象participant Serializer as 序列化器participant Network as 网络层participant Server as 服务器Client->>Proxy: 调用方法(method, args)Proxy->>Proxy: 构造RpcRequestProxy->>Serializer: 序列化请求Serializer-->>Proxy: 字节数组Proxy->>Network: 发送HTTP POSTNetwork->>Server: 传输请求Server->>Server: 处理请求Server-->>Network: 返回响应Network-->>Proxy: 接收响应Proxy->>Serializer: 反序列化响应Serializer-->>Proxy: RpcResponse对象Proxy-->>Client: 返回结果
```## 4. 服务端处理流程```mermaid
flowchart TDA[接收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 --> NN --> O[发送HTTP响应]E --> OH --> O
```## 5. 序列化流程```mermaid
graph LRA[对象] --> B[序列化器]B --> C[字节数组]C --> D[网络传输]D --> E[字节数组]E --> F[反序列化器]F --> G[对象]
```## 6. 异常处理流程```mermaid
flowchart TDA[发生异常] --> B{异常类型?}B -->|网络异常| C[重试机制]B -->|序列化异常| D[返回序列化错误]B -->|服务不存在| E[返回服务未找到]B -->|方法调用异常| F[返回调用异常]C --> G{重试次数?}G -->|未超限| H[重新发送请求]G -->|已超限| I[返回网络错误]H --> J{是否成功?}J -->|是| K[正常返回]J -->|否| GD --> L[客户端处理]E --> LF --> LI --> LK --> L
```## 7. 并发处理流程```mermaid
graph TBA[多个客户端请求] --> B[Vert.x事件循环]B --> C[异步处理请求1]B --> D[异步处理请求2]B --> E[异步处理请求N]C --> F[线程池执行]D --> FE --> FF --> G[并发访问注册中心]G --> H[ConcurrentHashMap]H --> I[返回处理结果]
```## 8. 启动流程```mermaid
sequenceDiagramparticipant 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 TDA[接收关闭信号] --> B[停止接收新请求]B --> C[等待当前请求完成]C --> D[关闭HTTP服务器]D --> E[清理注册中心]E --> F[释放资源]F --> G[程序退出]
```## 10. 性能监控流程```mermaid
graph LRA[请求开始] --> 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();