将RPC连接到 Tauri IPC 上
A 类 连接RPC 搭载连接的运输 协议通过Tauri的IPC而不是HTTP。 网景是客户端Rust 主机服务,没有涉及HTTP服务器或端口.
所有四种方法都有效:无线、服务器流、客户端流和 师曰.
为何没有重新执行连接而工作
双方都已经暴露出字节平面的缝隙 所以这个箱子只是个穿梭机 双方:
- 红宝石
ConnectRpcService是一个tower::Service<http::Request>返回时http::Response,所以它接受一个合成请求 从来没有触及过一个 接头. - 类型脚本
@connectrpc/connect/protocol-connect出口createTransport, (中文(简体) ), 需要一个httpClient: UniversalClientFn- 一个平地(request) => Promise<response>穿过可触动的身体.
信件序列化 信封框架 压缩谈判 拖车, 错误映射,连接的同位素- GET 支持都来自现有的 运行时间.
这包起来 协议级别 createTransport没有 connect-web是因为
createConnectTransport使客户流动和
tidi 工作: 网络传输拒绝, 因为获取的 API 无法流出 a
请求正文,这里没有抓取被限制.
使用量
锈:
let router = Arc::new(Greeter).register(connectrpc::Router::new());
tauri::Builder::default()
.plugin(connectrpc_tauri::serve(ConnectRpcService::new(router)))
.run(tauri::generate_context!())
.expect("failed to run app");
如果路由器依赖于 Tauri 设置时创建的状态, 则注册延迟 调度器先并初始化到设置钩子中 :
let deferred = connectrpc_tauri::DeferredDispatcher::new();
tauri::Builder::default()
.plugin(connectrpc_tauri::serve(ConnectRpcService::new(deferred.clone())))
.setup(move |app| {
let router = router_from_app(app.handle());
assert!(deferred.set(router).is_ok(), "ConnectRPC router initialized twice");
Ok(())
})
.run(tauri::generate_context!())
.expect("failed to run app");
类型脚本 :
import { createClient } from "@connectrpc/connect";
import { createTauriTransport } from "@connectrpc-tauri/transport";
const client = createClient(GreetService, createTauriTransport());
const { greeting } = await client.greet({ name: "World" });
能力文件必须给予插件权限, 否则每次呼叫都失败
与 Plugin not found数字 :
{
"identifier": "default",
"windows": ["main"],
"permissions": ["core:default", "connectrpc-tauri:default"]
}
许可前缀是 Rust 箱子名称 。 Tauri 的 ACL 密钥插件
权限在 CARGO_PKG_NAME这样 PLUGIN_NAME 箱名必须等同;
在任何命令运行前,在ACL层中拒绝不匹配.
它如何映射到IPC上
独家电话绕过Tauri的IPC 其他的都用三个命令 和每个呼叫一个频道:
| 连接概念 | Tauri机制 |
|---|---|
| 联合请求和答复 | fetch("ipc-connect://…"),自定义 URI 方案 |
| 请求头 + 第一个正文块 | invoke("connect_rpc", …) 带原生出 ArrayBuffer |
| 反应头(状况+信头) | 确定的价值 invoke |
| 流化反应机构框架 | Channel<InvokeResponseBody> 携带生原生生物 |
| 客户- 流 / bodi 请求框架 | invoke("connect_rpc_send", …) |
| 取消 | invoke("connect_rpc_cancel", …)从 AbortSignal |
| 拖车 | 带内,通过Connect自己的 EndStreamResponse 信封 |
信封是原生的 因为Tauri的IPC有效载荷是 无论是 原始字节或
JSON,从来没有两者,一个JSON有效载荷编码为每个字节一个数字.
Protobuf 保存每个帧的单个原始缓冲,所以连接的字节保持二进制
结束到结束。 出于同样的原因,反应通道id在内部旅行
StartRequest 而不是作为JSON命令的论据.
使用频道而不是活动: Tauri 的活动系统是广播和 相对于一个呼叫来说没有顺序,所以它需要关联的id和去除a 频道提供免费服务.
流出和后压
分流是双向递增的,而不是缓冲. 请求机构是
一个 mpsc 支持 http_body::Body 那个 connect_rpc_send feed, 所以一个网页视图
写得快于处理器读取的时间 CHANNEL_DEPTH 块块——
待处理 invoke 是后压信号.
在网景方面,信号是飞行中的信号。 信件生成时
一个是未完成的,被组合到下一个发送中,而不是每个使用一个
来回旅行 以及一个超过跳跃的制作人
MAX_PENDING_BYTES 等待它而不是缓冲溪流.
IPC路径上的一切是同音:所有三个命令都是 async fn,则
服务是按自己的任务驱动的,这样反应头就可以在返回之前返回
身体完好,反应泵通过合作排出
预算,所以内流不能垄断工人,也不能超过取消.
一个同步的锁 守护着一个散列地图, 从来没有在等待,
哪个 #![deny(clippy::await_holding_lock)] 强制执行.
版式
crates/connectrpc-tauri Rust plugin
packages/transport @connectrpc-tauri/transport
examples/greet-app demo app exercising all four kinds
proto/ transport envelopes + demo service
tools/protobuf-bindings regenerates the crate's checked-in bindings
Protobuf 装订
两个软件包的生成绑定都经过检查,所以两个建筑都没有
不安装 npm 包 protoc 或者说 buf之后
改变计划,再生他们:
npm run generate # TypeScript, with buf
mise exec -- cargo run --locked -p protobuf-bindings # Rust, with the pinned protoc
hk check 如果需要再生成, 则失败 .
正在验证
cargo test -p connectrpc-tauri # 12 tests over the real Connect runtime
npx vitest run # transport unit tests
# End-to-end, in a real webview. Exits nonzero on failure.
npm run build --workspace @connectrpc-tauri/transport
npm run build --workspace greet-app
GREET_APP_SELFTEST=1 cargo run --release -p greet-app --features custom-protocol
前两段将IPC桥自侧而上. 第三个是那个 证明 Tauri 的真实命令和频道携带协议: 它运行全部四个 在真实的网页视图中, 方法类型加上中流取消, 表示问候 每个RPC返回,打印一份记录,并有状态退出.
基准制定
npm run build --workspace @connectrpc-tauri/transport
npm run build --workspace greet-app
GREET_APP_BENCH=1 cargo run --release -p greet-app --features custom-protocol
这次是Tauri IPC能够为
相同的字节: 一个生 invoke 携带原生生物 invoke 和陶丽的
默认 JSON 参数, 空 Channel 用于流式响应,以及
invoke 每条消息用于流请求。 它运行在网络视图中,因为
这就是成本所在——Tauri IPC的昂贵部分正在穿越
webview 边界, 一个只使用 Rust 的基准永远不会支付 .
时间进行分批而不是按每个电话进行测量: WebKit 夹子
performance.now() 至 1ms,比整个RPC更粗.
在M系列的mac上,发布构建:
| 大小写 | 这个运输 | 最佳原始基线 |
|---|---|---|
| unary, 16字节请求 | 0.203毫米口径 | 0.195m (1.04x) (中文(简体) ) |
| unary, 4KiB 请求 | 0.227毫米口径 | 0.223ms (1.02x) (中文(简体) ) |
| unary, 64KiB 请求 | 0.363亿 | 0.340 ms (1.07x) (1 |
| 服务器流, 100 x 16 字节 | 0.797亿 | 1.453ms(0.55x) (英语) |
| 服务器流, 100 x 4KiB | 2.938亿 | 7.875ms(0.37x) (英语) |
| 客户端流, 100 x 16 字节 | 1.156亿 | 19.250毫米(0.06x) |
| 客户端流, 100 x 4KiB | 2.250毫米口径 | 21.500ms (0.10x) (中文(简体) ) |
上面的原始基线是原生的 invoke最快的移动方式
相同的字节用手写——客户端流行每条消息一个,即
一个手写客户端发送一个序列会做什么.
一个无名电话在手写的几分之一之内 invoke 已经不是了
完全通过Tauri的IPC。 一元请求是普通的 fetch 反对:
ipc-connect:// 设置插件登记器,它跳过命令路由器,
aCL的检查, 和原生包。 见
Unary 完全跳过 IPC。 。 。 .
两个流出方向都领先于手写式IPC,因为
每条消息支付一个网页视图过关:答复以 http_body 块
携带许多信封, 和请求合在一起 在出道。 见
流出费用低于每条电文的过路费。 。 。 .
为什么IPC的有效载荷会保持二进制
Tauri的IPC载荷是 无论是 原始字节或JSON,从来没有两者, 和一个JSON 有效载荷将字节编码为每字节一个JSON编号. 发送连接的字节为 JSON 命令参数费用, at 64KiB:
| 有效载荷 | 64KiB请求 |
|---|---|
| 生字节( 我们做什么) | 0.375毫米口径 |
| jSON 参数内的字节 | 5.063亿米 |
也就是13x,它随大小而增长: 16字节时的同比 1.04x
和 2.4x at 4KiB, 因为爆炸是在编码中,而不是固定的成本.
一个JSON invoke 移动平原 字符串 长相快 (0. 375ms at 64KiB) – 但 a
连接消息是字节, 那就是应用的行 .
二进制 vs JSON 连接编码c
与上述问题不同的一个问题: useBinaryFormat 选择如何是 消息
被编码,而 IPC 有效载荷 无论是保持原始字节。 已经接近了
在这些大小下自由,二进制保留默认值 :
| 代码c | 16个B | 4个KiB | 64个KB |
|---|---|---|---|
| 二进制 | 0.258亿 | 0.313毫米口径 | 0.430毫米口径 |
| 贾森 | 0.258亿 | 0.309亿 | 0.461亿 |
JSON只值得用于可读流量的编舞或代理视图 在进程跳动中,没有这种视图可获取,而且需要重新编码 更大的讯息.
流出费用低于每条电文的过路费
一个网页浏览器比它携带的字节要贵得多 所以两个都流着 所建立的方向发送数量比发送信息少.
回复不受协议约束: 连接的信封到达时是
http_body 块,这么多信息 在一个框架。 测量,100个流体
信件作为2个框架,而不是100个框架跨越 IPC 边界——其中天真 Channel
循环每次支付1个交叉.
请求需要帮助, 因为连接一次给客户端一个消息 .
等待一个 invoke 每条消息都会让一个客户端的调用 花费一整发
每条信件的行程——100条信件取出~19ms,准确跟踪手写
连环引用。 水泵现在集中了所有生产者
已经 连接到一个发送中, 将同样的100个信件连接到
~1.2ms (英语).
"已经制作"是整个微妙的. 泵从不等待一块 因此,客户端正在等待的讯息 仍然立即独自离开。 这就是保持badi互动:a 及时分批的窗口会让对话陷入僵局,而下一个 请求取决于最后的答复.
Unary 完全跳过 IPC
Tauri 的 IPC 是一个命令路由器: 一个请求以一个
InvokeBody,与命令名称相匹配,然后对照ACL检查
回答过 无关紧要的连接电话不需要这些 它已经是一个HTTP
反对 tower::Service,而网页视图已经说 HTTP .
因此,插件注册了自己的URI方案, ipc-connect://,和闲电话
像个平地出海 fetchTauri的网页浏览管理器检查计划是否
在安装自己的处理器之前已经注册过, 因此它与
ipc:// 而不是替换它——每个股票插件继续工作.
跳过命令路由器也跳过什么使其他页面退出它. 每个
在 webview 中的框架可以达到自定义方案,包括另一个 iframe
来源:远程页面,或内容该应用程序服务于它自己的方案.
Tauri的IPC拒绝那些有它只给主框的调取键的人. 这个
计划没有密钥,所以它检查请求的 Origin,该页面无法设置:
它只回答应用程序自己的页面( )tauri://localhost, (中文(简体) ), http://tauri.localhost
视窗和Android,或 devUrl )和名称
来源, 不 *,作为唯一可以读取答复的.
从无名电话中移除的是什么: 原生信封及其
编码/解码、命令名称发送、ACL权限查询、呼叫
注册和频道。 剩下的是接近身份图
http::Request 在,服务, http::Response 出局了,这就是为什么现在不必要
占手写土地的几分之一 invoke 而不是以上15%-20%
这个.
流能保持命令与渠道的路径. 计划响应器是一发
每个平台:它需要一个完整的响应值, 并在它下面
WKWebView 这样做 didReceiveResponse 然后是单曲 didReceiveData时
WebKitGTK 从一个完成的缓冲器建立它的流. 没有什么东西能推出第二秒
框架,所以通过这个方案来引导服务器流响应
一旦缓冲了整个系统就交付——这恰恰是源源不绝的
避开.
这个计划叫做 ipc-connect 而不是类似的东西 ipc+connect
因为Windows和Android wry重写 scheme://localhost 改为
http://scheme.localhost,使方案名称成为主机名标签. + 实值
在一个URI计划中是合法的,但是在主机名中没有,所以它可以工作在macOS和
Linux 并打破 Windows .
custom-protocol 塔里就是为捆绑的资产服务的原因 没有它,
每一建筑物——包括释放——载荷 devUrl 需求 npm run dev
运行中,因为Tauri从该特性中而不是从
货物简介.
限制
- 网景 只收烂摊子. 网络浏览托管服务中的粗糙电话 超出范围.
- 压缩是默认关闭的。 它将 CPU 用于在进程内缩小字节 跳出永不触碰网络; 旋钮在两侧被曝光 .
- Android的IPC缺乏
InvokeBody::Raw回到JSON数组。 A 类 基础64倒计时未执行。 这只影响流; 无效去 超过ipc-connect://计划,不受影响. - 在 macOS 上已经验证出无源路径 。 计划登记和 处理Windows/Android主机名重写,但尚未在其中运行 这些平台上的CI.
捐款
从一个开始 讨论情况, (中文(简体) ), 不是拉动请求 一名数学维护者将审查该提案。 如果我们 决定执行它,一个维护者 或我们的AI代理将打开拉出 请求。 GitHub 将拉动请求创建限制在数学维护者 拥有写入、维护或管理权限的仓库协作者,加授权 维修人员.
数学执行提案时,执行拉动请求将 与讨论有关,并归功于提案的原作者.
见 捐款额 整个政策.