调用服务器上的 getUserInfo(),为什么代码写起来像本地函数,底层却走了十万八千里?

文章封面

做客户端开发的时候,最爽的就是写一行 userApi.getUserInfo(userId),然后就像调用本地函数一样拿到结果。

结果真出问题的时候才发现:这一行函数调用,根本不是本地的。它要把参数序列化、打包、过网络、到服务器、执行、把结果序列化、再传回来。中间任何一步出问题,都是用户看到的"加载失败"。

一、先想清楚:这一行函数调用到底做了什么

先把最基础的问题想明白。

你写的是:

let user = await userApi.getUserInfo(userId);

看起来就是调了一个函数。但实际上,背后发生了这么多事:

阶段做什么
参数序列化把 userId 打包成网络能传的格式
建立连接跟服务器建立 TCP/UDP 连接
发送请求把请求包发出去
服务端执行服务器收到请求,执行业务逻辑
结果序列化把返回值打包
返回响应把结果传回来
反序列化把结果解成你要的对象

这一套走下来,可能几十毫秒,也可能几百毫秒。网络不好的时候,可能直接超时。

二、URPC 和普通 HTTP/REST 有什么区别

很多人以为:RPC 不就是换个方式发 HTTP 请求吗?

不对。HTTP/REST 是面向资源的,你请求一个 URL,拿到一个资源。

RPC 是面向方法的,你调用一个远程方法,就像调用本地函数一样。

类型思维方式适合场景
HTTP/REST请求资源公开 API、跨平台
URPC调用方法内部服务、高性能

URPC 是 HarmonyOS 提供的高性能远程过程调用。它不是简单的 HTTP 封装,是一套完整的 RPC 框架。

三、Client、Server 和远程方法

URPC 的基本结构很简单:Client 调用方,Server 提供方,中间是远程方法。

这段代码解决什么问题: 定义远程方法。
文件: rpc/UserService.ets
用途: 远程服务接口定义
接入位置: 客户端和服务端共用

import { rpc } from '@kit.RemoteCommunicationKit';

// 定义远程接口
export interface IUserService {
  getUserInfo(userId: string): Promise<UserInfo>;
  updateProfile(userId: string, profile: Profile): Promise<boolean>;
}

客户端拿到这个接口,调用的时候就像调用本地方法一样。但实际上,调用会被框架拦截,打包发到服务端。

四、一次调用的完整生命周期

完整的远程调用是这样的:

  1. 客户端调用 getUserInfo(userId)
  2. 框架拦截调用,把方法名和参数序列化;
  3. 建立连接,发送请求包;
  4. 服务端收到请求,反序列化,找到对应方法;
  5. 服务端执行业务逻辑;
  6. 服务端把返回值序列化;
  7. 把响应包发回客户端;
  8. 客户端反序列化,拿到结果;
  9. 把结果返回给调用方。

系统架构图

五、弱网环境为什么容易出问题

URPC 特别强调弱网传输。为什么?因为手机网络不是一直稳定的。

网络情况问题
Wi-Fi 信号差丢包、延迟高
蜂窝网络切换连接中断、重连
多径传输Wi-Fi 和蜂窝同时传,选快的

多径传输是什么意思?就是 Wi-Fi 和蜂窝网络同时传同一份数据,哪条路快走哪条。这样即使 Wi-Fi 断了,蜂窝网络还在传,不会整个挂掉。

六、超时和异常传播为什么很重要

很多人写 RPC 只处理"成功"这一种情况。不对。

RPC 的失败情况太多了:

失败类型说明
网络超时服务器没响应
网络断开连接断了
服务端错误业务报错
序列化失败参数打包出错
反序列化失败结果解不开

每一种失败,客户端都要能处理。不能网络一断,整个应用就崩了。

七、几个容易踩的坑

第一个坑:把远程调用当成本地函数。它不是本地的,它有网络延迟,会失败。

第二个坑:循环里大量细粒度 RPC。循环调十次,十次网络往返,太慢了。应该批量传。

第三个坑:接口幂等性没有设计。网络重试了,服务器执行了两次,业务就乱了。

第四个坑:客户端超时但服务端仍继续执行。客户端不等了,服务端还在跑,资源浪费。

第五个坑:网络重试导致业务重复提交。点一次提交,网络不好重试,结果提交了两次。

第六个坑:把网络断开当普通函数异常。网络断开是常见情况,不是异常。

第七个坑:RPC 对象长期持有失效连接。连接断了还拿着用,每次都超时。

运行效果图

这次做远程调用最大的体会是:RPC 看起来像本地函数,但它本质是网络调用。你必须把它当网络问题来处理:超时、重试、幂等、异常。

每一个环节都有它存在的理由:序列化把参数打包、连接管理管网络、超时控制防卡死、异常处理防崩溃。

真正做的时候,最容易忽略的不是 API 怎么调,而是周边的工程问题:重试策略、幂等设计、连接生命周期、弱网处理。这些才是 RPC 真正难的地方。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐