当前位置:首页 > 技术 > 正文内容

深入解析 RPC 机制:从 TCP 到 HTTP 的演进与实践

访客 技术 2026年9月30日 7

远程过程调用的核心原理

RPC(Remote Procedure Call,远程过程调用)是实现分布式服务交互的基础设施。其本质在于允许调用方像调用本地方法一样去触发远端服务的逻辑。整个流程涉及两个角色:服务消费者(发起请求)和服务提供者(接收并执行)。客户端封装指令发送至网络,服务端解析指令、执行对应业务逻辑并将结果回传,从而完成一次完整的远程交互。

对象数据的序列化处理

在网络传输中,所有数据最终都必须转换为二进制字节流。在面向对象开发中,这涉及到将内存中的对象实例转换为字节流(序列化),以及接收后还原为对象(反序列化)的过程。Java 生态中常见的方案包括原生 JDK 序列化与第三方高效协议如 Hessian。

以下是使用原生 API 进行序列化的示例代码:

// 编码阶段:对象转字节
UserInfo user = new UserInfo();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(user);
byte[] encodedData = bos.toByteArray();

// 解码阶段:字节转对象
ByteArrayInputStream bis = new ByteArrayInputStream(encodedData);
ObjectInputStream ois = new ObjectInputStream(bis);
UserInfo decodedUser = (UserInfo) ois.readObject();

若采用 Hessian 协议,逻辑则通过特定的输出流类完成,能减少数据体积并提升跨语言兼容性。

HessianOutput hOut = new HessianOutput(new ByteArrayOutputStream());
hOut.writeObject(payload);
// ...传输操作...
HessianInput hIn = new HessianInput(inputStream);
Object result = hIn.readObject();

基于 TCP 套接字的 RPC 实现

为了直观展示原理,我们可以利用 Java Socket API 构建一个简易的 RPC 框架。这里定义一个简单的通知服务接口,根据输入内容返回不同的问候语。

public interface NotifyInterface {
    String sendAlert(String message);
}

public class NotifyImpl implements NotifyInterface {
    @Override
    public String sendAlert(String msg) {
        return msg.equals("start") ? "System Running" : "System Stopped";
    }
}

客户端侧需要利用反射动态获取方法信息,并通过 Socket 发送元数据及参数:

public class RemoteCaller {
    public static void main(String[] args) throws Exception {
        Class<?> svcClass = NotifyInterface.class;
        Method targetMethod = svcClass.getMethod("sendAlert", String.class);
        
        Socket sock = new Socket("localhost", 9090);
        
        // 构造发送包
        ObjectOutputStream out = new ObjectOutputStream(sock.getOutputStream());
        out.writeUTF(svcClass.getName());
        out.writeUTF(targetMethod.getName());
        out.writeObject(targetMethod.getParameterTypes());
        out.writeObject(new Object[]{"start"});
        
        // 接收响应
        ObjectInputStream in = new ObjectInputStream(sock.getInputStream());
        System.out.println("Response: " + in.readObject());
        sock.close();
    }
}

服务端则监听指定端口,提取请求信息并路由到具体实现类:

public class ServiceNode {
    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(9090);
        Map<String, Object> registry = new HashMap<>();
        registry.put("com.example.NotifyInterface", new NotifyImpl());
        
        while(true) {
            Socket client = server.accept();
            ObjectInputStream in = new ObjectInputStream(client.getInputStream());
            
            String className = in.readUTF();
            String methodName = in.readUTF();
            Class[] paramTypes = (Class[]) in.readObject();
            Object[] params = (Object[]) in.readObject();
            
            // 定位并执行
            Class clazz = Class.forName(className);
            Object instance = registry.get(className);
            Method method = clazz.getMethod(methodName, paramTypes);
            Object ret = method.invoke(instance, params);
            
            // 返回结果
            new ObjectOutputStream(client.getOutputStream()).writeObject(ret);
        }
    }
}

虽然上述代码实现了基本功能,但在生产环境中直接处理 Socket 连接、并发控制及服务发现等底层问题复杂度极高,通常不建议在生产系统裸用。

HTTP 协议栈与自定义应用层协议

HTTP(超文本传输协议)作为应用层标准,基于 TCP 构建,具有成熟的规范。为了更好地理解应用层封包过程,我们可以模拟设计一个简单的自定义帧格式。假设客户端发送指令,服务端校验指令内容并反馈。

定义请求与响应的数据结构:

public class CmdPacket {
    private int charsetType; // 0: GBK, 1: UTF-8
    private String actionCmd;
    private int cmdLen;
    // getter/setter omitted
}
public class RspPacket {
    private int charsetType;
    private String replyMsg;
    private int msgLen;
    // getter/setter omitted
}

通信工具类负责将对象转化为符合网络字节序的二进制流:

public class FrameEncoder {
    public static void encodeCmd(OutputStream os, CmdPacket p) throws IOException {
        os.write(p.getCharsetType());
        os.write(toByteArr(p.getCmdLen())); // 处理整数转字节
        os.write(p.getActionCmd().getBytes(getCharset(p.getCharsetType())));
        os.flush();
    }

    private static byte[] toByteArr(int val) {
        byte[] b = new byte[4];
        b[0] = (byte)(val >> 24);
        b[1] = (byte)(val >> 16);
        b[2] = (byte)(val >> 8);
        b[3] = (byte)(val & 0xFF);
        return b;
    }
    
    // decode 逻辑类似,读取头信息再按长度读取 payload
}

服务端与客户端通过该类进行数据交换。这种模式揭示了 HTTP 协议下数据包"头部+体"的基本结构特征。

HttpClient 的使用与协议优势

在实际开发中,Apache HttpClient 等库已经屏蔽了底层的报文组装细节。使用它们发起请求非常简单:

HttpGet request = new HttpGet("https://api.example.com/status");
HttpClient client = new DefaultHttpClient();
HttpResponse resp = client.execute(request);
String body = EntityUtils.toString(resp.getEntity(), "utf-8");

相较于直接使用 TCP Socket 编写 RPC,基于 HTTP 方案拥有显著优势。在高并发场景下,原始 TCP 连接管理需要开发者自行处理多线程、锁竞争及 I/O 模型优化,风险较大且容易引发稳定性问题。而成熟的 Web 容器(如 Tomcat、Jetty)已经内置了连接池、线程管理及负载均衡机制。采用 HTTP 协议可以让开发者专注于业务逻辑的实现,借助现有基础设施来保障系统的可用性与扩展性。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。