鱼皮项目简易版 RPC 框架开发(二)

发布于:2025-07-30 ⋅ 阅读:(39) ⋅ 点赞:(0)

本文为笔者阅读鱼皮的项目 《简易版 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 {

    @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();

网站公告

今日签到

点亮在社区的每一天
去签到