This is an automated email from the ASF dual-hosted git repository. chaokunyang pushed a commit to branch release_1.3 in repository https://gitbox.apache.org/repos/asf/fory-site.git
commit a139c2a7428632b53e4629b3c2b7a25cffe85ce0 Author: 慕白 <[email protected]> AuthorDate: Thu Jun 25 21:18:05 2026 +0800 Sync zh-CN docs for Fory 1.3.0 --- .../2026-06-25-fory_1_3_0_release.md | 165 ++++++++++++ .../current/community/DEVELOPMENT.md | 1 + .../current/compiler/compiler-guide.md | 39 ++- .../current/compiler/flatbuffers-idl.md | 17 ++ .../current/compiler/generated-code.md | 67 +++++ .../current/compiler/index.md | 10 +- .../current/compiler/protobuf-idl.md | 12 +- .../current/compiler/schema-idl.md | 14 + .../current/guide/cpp/configuration.md | 80 +++++- .../current/guide/cpp/index.md | 8 +- .../current/guide/csharp/configuration.md | 66 ++++- .../current/guide/csharp/grpc-support.md | 4 +- .../current/guide/csharp/index.md | 2 +- .../current/guide/dart/configuration.md | 41 ++- .../current/guide/dart/grpc-support.md | 284 +++++++++++++++++++++ .../current/guide/dart/index.md | 2 +- .../current/guide/go/configuration.md | 53 +++- .../current/guide/go/grpc-support.md | 2 +- .../current/guide/java/compression.md | 2 +- .../current/guide/java/configuration.md | 15 ++ .../current/guide/java/grpc-support.md | 2 +- .../current/guide/javascript/configuration.md | 30 ++- .../current/guide/javascript/grpc-support.md | 2 +- .../current/guide/kotlin/configuration.md | 23 ++ .../current/guide/kotlin/grpc-support.md | 2 +- .../current/guide/kotlin/index.md | 4 +- .../current/guide/python/configuration.md | 45 +++- .../current/guide/python/grpc-support.md | 204 ++++++++++----- .../current/guide/rust/basic-serialization.md | 2 +- .../current/guide/rust/configuration.md | 39 ++- .../current/guide/rust/grpc-support.md | 4 +- .../current/guide/rust/index.md | 2 +- .../current/guide/scala/configuration.md | 3 + .../current/guide/scala/index.md | 2 +- .../current/guide/swift/configuration.md | 34 +++ .../current/guide/xlang/getting-started.md | 16 +- .../current/guide/xlang/serialization.md | 27 ++ .../specification/java_serialization_spec.md | 4 + .../specification/xlang_implementation_guide.md | 17 +- .../specification/xlang_serialization_spec.md | 7 + .../current/start/install.md | 32 +-- .../download/index.md | 10 +- 42 files changed, 1211 insertions(+), 184 deletions(-) diff --git a/i18n/zh-CN/docusaurus-plugin-content-blog/2026-06-25-fory_1_3_0_release.md b/i18n/zh-CN/docusaurus-plugin-content-blog/2026-06-25-fory_1_3_0_release.md new file mode 100644 index 0000000000..cb237103bc --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-blog/2026-06-25-fory_1_3_0_release.md @@ -0,0 +1,165 @@ +--- +slug: fory_1_3_0_release +title: Fory v1.3.0 发布 +authors: [chaokunyang] +tags: [fory, java, kotlin, scala, android, python, rust, c++, go, c#, swift, dart, compiler] +--- + +Apache Fory 团队很高兴宣布 1.3.0 版本正式发布。本版本包含来自 3 位贡献者的 [8 个 PR](https://github.com/apache/fory/compare/v1.2.0...v1.3.0),并继续改进各支持语言的跨语言运行时。请访问 [Install 页面](https://fory.apache.org/docs/start/install) 获取各平台安装方式。 + +## 发布亮点 + +* Python gRPC 代码生成现在默认使用 `grpc.aio` AsyncIO API;同步 `grpcio` 输出仍可通过 `--grpc-python-mode=sync` 显式启用。 +* Dart 加入生成式 gRPC service 支持:`foryc --dart_out=... --grpc` 现在会生成 `package:grpc` client、service base、method descriptor,以及基于 Fory 的 payload 序列化逻辑。 +* Compiler gRPC 文档在多个语言上进一步细化,包括更清晰的生成 service 依赖和传输行为说明。 +* 运行时继续增强安全性,加入远端 schema metadata 限制,并修复 Java aligned-varint 与 type-checker cache 相关问题。 + +## Python Async gRPC 模式 + +Python gRPC 生成现在默认面向 AsyncIO。生成的 companion 使用 `grpc.aio`:servicer base 暴露 `async def` 方法,stub 搭配 `grpc.aio.Channel` 使用,streaming RPC 使用 async iterable。这让生成代码更适合现代 Python async service,同时仍保留既有 gRPC 支持中的 Fory-backed request/response 编码方式。 + +默认 async companion 生成命令如下: + +```bash +foryc service.fdl --python_out=./generated/python --grpc +``` + +对于简单 unary service,生成代码对应的 async server 形态如下: + +```python +import asyncio + +import grpc.aio + +import demo_greeter +import demo_greeter_grpc + + +class Greeter(demo_greeter_grpc.GreeterServicer): + async def say_hello(self, request, context): + return demo_greeter.HelloReply(reply=f"Hello, {request.name}") + + +async def serve(): + server = grpc.aio.server() + demo_greeter_grpc.add_servicer(Greeter(), server) + server.add_insecure_port("[::]:50051") + await server.start() + await server.wait_for_termination() + + +asyncio.run(serve()) +``` + +Client 使用 `grpc.aio` channel,并 `await` 生成的 stub 方法: + +```python +import grpc +import grpc.aio + +import demo_greeter +import demo_greeter_grpc + + +credentials = grpc.ssl_channel_credentials() +async with grpc.aio.secure_channel("api.example.com:443", credentials) as channel: + stub = demo_greeter_grpc.GreeterStub(channel) + reply = await stub.say_hello(demo_greeter.HelloRequest(name="Fory")) +``` + +已有同步应用仍可显式请求 sync companion: + +```bash +foryc service.fdl --python_out=./generated/python --grpc --grpc-python-mode=sync +``` + +Sync 模式保持相同的生成 public name 和 `<module>_grpc.py` 文件名,但应用使用 `grpc.server(...)`、标准 `grpc.Channel` 实例,以及普通 `def` servicer 方法。 + +## Dart gRPC 代码生成 + +Fory 1.3.0 为包含 service 定义的 schema 增加 Dart gRPC service 生成。Service 定义可以来自 Fory IDL、protobuf IDL 或 FlatBuffers `rpc_service`。生成代码使用标准 grpc-dart API 来处理 client、service base、method descriptor、call option、deadline、取消、metadata 和 status code;每个 request/response 对象则使用 Fory 序列化,而不是 protobuf message bytes。 + +在 Dart 应用中,除 Fory package 外,还需要加入 `grpc` 和 `build_runner`: + +```yaml +dependencies: + fory: ^1.3.0 + grpc: ^4.0.0 + +dev_dependencies: + build_runner: ^2.4.0 +``` + +生成 Dart model 和 gRPC companion: + +```bash +foryc service.fdl --dart_out=./lib/generated --grpc +dart run build_runner build --delete-conflicting-outputs +``` + +对于 `demo.greeter` package,generator 会输出 model 文件、由 `build_runner` 生成的 serializer part,以及包含 `GreeterServiceBase` 和 `GreeterClient` 的 `<stem>_grpc.dart` companion。生成的 client 和 service base 会在首次使用时自动安装该 schema 的 Fory module,因此 service 实现不需要为生成的 message type 单独手动注册。 + +Unary Dart server 使用 grpc-dart 的 `Server` 和生成的 service base: + +```dart +import 'dart:io'; + +import 'package:grpc/grpc.dart'; +import 'demo/greeter/greeter.dart'; +import 'demo/greeter/greeter_grpc.dart'; + +class GreeterService extends GreeterServiceBase { + @override + Future<HelloReply> sayHello(ServiceCall call, HelloRequest request) async { + return HelloReply()..reply = 'Hello, ${request.name}'; + } +} + +Future<void> main() async { + final server = Server.create(services: [GreeterService()]); + await server.serve(address: InternetAddress.loopbackIPv4, port: 50051); +} +``` + +生成的 Dart client 使用标准 `ClientChannel`,并返回 grpc-dart 调用类型: + +```dart +import 'package:grpc/grpc.dart'; +import 'demo/greeter/greeter.dart'; +import 'demo/greeter/greeter_grpc.dart'; + +final channel = ClientChannel( + 'localhost', + port: 50051, + options: const ChannelOptions(credentials: ChannelCredentials.insecure()), +); +final client = GreeterClient(channel); + +final reply = await client.sayHello(HelloRequest()..name = 'Fory'); +await channel.shutdown(); +``` + +Dart 生成支持 unary、server-streaming、client-streaming 和 bidirectional streaming 四种 RPC 形态,并遵循 grpc-dart 约定。 + +## Features + +* feat(python): add async grpc mode for python by @chaokunyang in https://github.com/apache/fory/pull/3768 +* feat: limit remote schema metadata by @chaokunyang in https://github.com/apache/fory/pull/3770 +* feat(compiler): add dart gRPC codegen by @yash-agarwa-l in https://github.com/apache/fory/pull/3723 + +## Bug Fix + +* fix(java): guard aligned varint unsafe read by @chaokunyang in https://github.com/apache/fory/pull/3772 +* fix(java): cache accepted type checker classes by @chaokunyang in https://github.com/apache/fory/pull/3773 + +## Other Improvements + +* docs: refine gRPC support guides by @chaokunyang in https://github.com/apache/fory/pull/3767 +* docs: add threat model + SECURITY.md/AGENTS.md discoverability by @potiuk in https://github.com/apache/fory/pull/3734 +* chore(release): enforce OpenJDK 25 for JVM publishing by @chaokunyang in https://github.com/apache/fory/pull/3775 + +## New Contributors + +* @potiuk made their first contribution in https://github.com/apache/fory/pull/3734 + +**Full Changelog**: https://github.com/apache/fory/compare/v1.2.0...v1.3.0 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/community/DEVELOPMENT.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/community/DEVELOPMENT.md index 9e8a116d20..1980387e77 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/community/DEVELOPMENT.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/community/DEVELOPMENT.md @@ -144,3 +144,4 @@ prettier --write "**/*.md" ## 贡献 更多信息,请参考[如何贡献到 Apache Fory™](https://github.com/apache/fory/blob/main/CONTRIBUTING.md)。 +对于 AI 辅助贡献,请遵循 [AI Contribution Policy](https://github.com/apache/fory/blob/main/AI_POLICY.md),包括 substantial AI assistance 所需的自审、双 reviewer AI review 循环、披露和验证证据。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/compiler-guide.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/compiler-guide.md index 16a2d40b27..215430e8d4 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/compiler-guide.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/compiler-guide.md @@ -72,7 +72,9 @@ foryc --scan-generated [OPTIONS] | `--swift_namespace_style` | Swift 命名空间方式:`enum` 或 `flatten` | `enum` | | `--emit-fdl` | 输出转换后的 FDL(用于非 FDL 输入) | `false` | | `--emit-fdl-path` | 将转换后的 FDL 写入此路径(文件或目录) | (stdout) | -| `--grpc` | 为 Java 和 Python 生成 gRPC service companion 代码 | `false` | +| `--grpc` | 为支持的输出生成 gRPC service companion 代码 | `false` | +| `--grpc-python-mode=MODE` | Python gRPC 模式:`async` 或 `sync` | `async` | +| `--grpc-web` | 生成 JavaScript gRPC-Web client companion | `false` | 支持 schema 级文件选项,用于控制特定语言的生成行为。 对于 `go_nested_type_style` 和 `swift_namespace_style`,当 CLI 标志和 @@ -139,22 +141,37 @@ foryc schema.fdl --output ./src/generated foryc user.fdl order.fdl product.fdl --output ./generated ``` -**编译包含 service 定义的简单 schema(Java + Python 模型):** +**编译包含 service 定义的简单 schema(Java + Python + Go + Rust + C# + Dart + Scala + Kotlin + JavaScript 模型):** ```bash -foryc compiler/examples/service.fdl --java_out=./generated/java --python_out=./generated/python +foryc compiler/examples/service.fdl --java_out=./generated/java --python_out=./generated/python --go_out=./generated/go --rust_out=./generated/rust --csharp_out=./generated/csharp --dart_out=./generated/dart --scala_out=./generated/scala --kotlin_out=./generated/kotlin --javascript_out=./generated/javascript ``` -**生成 Java 和 Python gRPC service companion 代码:** +**生成 Java、Python、Go、Rust、C#、Dart、Scala、Kotlin 和 Node.js JavaScript gRPC service companion 代码:** ```bash -foryc compiler/examples/service.fdl --java_out=./generated/java --python_out=./generated/python --grpc +foryc compiler/examples/service.fdl --java_out=./generated/java --python_out=./generated/python --go_out=./generated/go --rust_out=./generated/rust --csharp_out=./generated/csharp --dart_out=./generated/dart --scala_out=./generated/scala --kotlin_out=./generated/kotlin --javascript_out=./generated/javascript --grpc ``` 生成的 gRPC service 代码使用 Fory 序列化请求和响应载荷。Java 输出会导入 -grpc-java API,Python 输出会导入 `grpc`;编译或运行这些生成 service 文件的 -应用需要自行提供 gRPC 依赖。Fory 的 Java 和 Python 运行时包不会为此功能加入 -强制 gRPC 依赖。 +grpc-java API,Python 输出默认使用 `grpc.aio`,Go 输出会导入 grpc-go,Rust 输出会导入 +`tonic` 和 `bytes`,Scala 输出会导入 grpc-java API,Kotlin 输出会导入 grpc-java 和 +grpc-kotlin API 并使用 coroutine stub。C# 输出会导入 `Grpc.Core.Api` 类型,并可由 +`Grpc.AspNetCore` 等常规 .NET gRPC package 承载,或通过 `Grpc.Net.Client` 调用。Dart 输出 +会导入 `package:grpc`。JavaScript 输出会导入 `@grpc/grpc-js`。编译或运行这些生成 +service 文件的应用需要自行提供 gRPC 依赖。Fory package 不会为此功能加入强制 gRPC 依赖。 + +为已有同步 `grpcio` 应用生成同步 Python gRPC companion: + +```bash +foryc compiler/examples/service.fdl --python_out=./generated/python --grpc --grpc-python-mode=sync +``` + +**生成 JavaScript gRPC-Web 浏览器 client:** + +```bash +foryc compiler/examples/service.fdl --javascript_out=./generated/javascript --grpc --grpc-web +``` **使用 import 搜索路径:** @@ -397,6 +414,8 @@ generated/ - Package 片段映射为目录(例如 `demo.foo` → `demo/foo/`) - IDL module 类包含在主文件中;生成的 serializer 元数据包含在 part 文件中 - 非可选、非 `ref` 的 primitive list 使用类型化数组(例如 `Int32List`) +- 使用 `--grpc` 时,会在 model 文件旁为每个 schema 生成一个 `<stem>_grpc.dart` companion, + 其中包含各 service 的 `Client` 和 `ServiceBase`,并导入 `package:grpc` ### Scala @@ -667,7 +686,7 @@ cc_library( ```yaml dependencies: - fory: ^1.2.0 + fory: ^1.3.0 dev_dependencies: build_runner: ^2.4.0 @@ -859,5 +878,5 @@ fory = "x.y.z" ```yaml dependencies: - fory: ^1.2.0 + fory: ^1.3.0 ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/flatbuffers-idl.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/flatbuffers-idl.md index f250081313..78bf32de21 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/flatbuffers-idl.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/flatbuffers-idl.md @@ -113,6 +113,23 @@ message Container { } ``` +### gRPC Service + +FlatBuffers `rpc_service` 定义会转换为 Fory service。使用 `--grpc` 时,compiler 会为 Java、Python、Go、Rust、C#、Dart、Scala、Kotlin 和 JavaScript 等支持的输出生成 gRPC service companion。JavaScript 浏览器 client 通过 `--grpc-web` 生成。这些 companion 使用 Fory 序列化 request 和 response payload。 + +```fbs +rpc_service SearchService { + Lookup(SearchRequest):SearchResponse; + StreamLookup(SearchRequest):SearchResponse (streaming: "server"); +} +``` + +```bash +foryc api.fbs --java_out=./generated/java --python_out=./generated/python --go_out=./generated/go --rust_out=./generated/rust --csharp_out=./generated/csharp --dart_out=./generated/dart --scala_out=./generated/scala --kotlin_out=./generated/kotlin --javascript_out=./generated/javascript --grpc +``` + +生成的 service 代码会导入 gRPC API,因此编译或运行这些文件的应用需要提供 grpc-java、grpc-kotlin、Scala grpc-java API、`grpcio`、grpc-go、Rust `tonic` 和 `bytes`、`@grpc/grpc-js`、C# `Grpc.Core.Api` 及 server/client 依赖,或 Dart `package:grpc`。Python companion 默认使用 `grpc.aio`,也可以通过 `--grpc-python-mode=sync` 生成同步模式。Fory package 不会把 gRPC 作为硬依赖。JavaScript 输出配合 `--grpc-web` 可生成导入 `grpc-web` 的浏览器 client。 + ### 默认值与元数据 - FlatBuffers 默认值会被解析,但不会作为 Fory 运行时默认值生效。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/generated-code.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/generated-code.md index 024eb203ca..9494463652 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/generated-code.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/generated-code.md @@ -323,6 +323,41 @@ data = person.to_bytes() restored = Person.from_bytes(data) ``` +### gRPC Service Companion + +当 schema 包含 service,且 compiler 使用 `--grpc` 运行时,Python 会生成名为 +`<module>_grpc.py` 的 companion module。Module 名称由 Fory package 的点号替换为下划线得到; +没有 package 时使用 `generated`。Python gRPC 输出默认使用 `grpc.aio` AsyncIO API。 + +```python +import grpc +import grpc.aio + + +class AddressBookServiceStub: + def __init__(self, channel): + self.lookup = channel.unary_unary( + "/example.AddressBookService/Lookup", + request_serializer=..., + response_deserializer=..., + ) + + +class AddressBookServiceServicer: + async def lookup(self, request, context): + await context.abort(grpc.StatusCode.UNIMPLEMENTED, "Method not implemented!") + + +def add_servicer(servicer, server): ... +``` + +编译或运行生成 companion module 的应用必须安装 `grpcio`;`pyfory` 不会加入硬 gRPC 依赖。 +Python API 使用 snake_case 方法名,同时在 gRPC wire path 中保留原始 IDL method 名称。 + +使用 `--grpc --grpc-python-mode=sync` 可以生成同步 Python `grpcio` companion。Sync 模式保持 +相同的生成文件名和 public name,但 servicer 方法使用普通 `def`,并使用同步 `grpc.Channel` +和 `grpc.Server` 实例。 + ## Rust ### 输出布局 @@ -1000,6 +1035,38 @@ void main() { } ``` +### gRPC Service Companion + +当 schema 包含 service,且 compiler 使用 `--grpc` 运行时,Dart 会在 model type 旁为每个 +schema 生成一个 `<module>_grpc.dart` 文件。它面向 `package:grpc`。Request 和 response +序列化使用 companion 自动获取的 Fory runtime,并在首次使用时注册该 schema 的类型,因此不需要手动注册;应用也可以在第一次调用前通过 schema module 的 `install(...)` 注入自定义 `Fory`。 + +生成支持四种 RPC 模式:unary、server-streaming、client-streaming 和 bidirectional。Client +class 继承 `Client`;service base class 继承 `Service` 并通过 `$addMethod` 注册各方法。 + +```dart +class GreeterClient extends Client { + // Single response: ResponseFuture. Streaming response: ResponseStream. + ResponseFuture<HelloReply> sayHello(HelloRequest request, {CallOptions? options}) { ... } + ResponseStream<HelloReply> sayHellos(HelloRequest request, {CallOptions? options}) { ... } + ResponseFuture<HelloReply> collectHellos(Stream<HelloRequest> request, {CallOptions? options}) { ... } + ResponseStream<HelloReply> chatHellos(Stream<HelloRequest> request, {CallOptions? options}) { ... } +} + +abstract class GreeterServiceBase extends Service { + Future<HelloReply> sayHello(ServiceCall call, HelloRequest request); + Stream<HelloReply> sayHellos(ServiceCall call, HelloRequest request); + Future<HelloReply> collectHellos(ServiceCall call, Stream<HelloRequest> request); + Stream<HelloReply> chatHellos(ServiceCall call, Stream<HelloRequest> request); +} +``` + +单响应 client 方法返回 `ResponseFuture<R>`(client-streaming 会用 `.single` 适配 streaming 调用); +streaming 响应方法返回 `ResponseStream<R>`。Server 端实现会 override 抽象方法:单请求以 `Q` +传入,client-streaming 请求以 `Stream<Q>` 传入;单响应返回 `Future`,streaming 响应返回 +`Stream`。编译这些文件的应用必须提供 `grpc` 依赖;Fory Dart runtime 不会加入此依赖。原始 +IDL method 名称用于 gRPC wire path。 + ## 跨语言说明 ### 类型 ID 行为 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/index.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/index.md index 73caa1018b..bb1809493c 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/index.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/index.md @@ -20,7 +20,7 @@ license: | --- Fory IDL 是 Apache Fory 的 Schema 定义语言,可实现类型安全的跨语言序列化。 -你只需定义一次数据结构,即可为 Java、Python、Go、Rust、C++、C#、Swift、JavaScript 和 Dart 生成原生数据结构代码。 +你只需定义一次数据结构,即可为 Java、Python、Go、Rust、C++、C#、Swift、Dart、Scala、Kotlin 和 JavaScript/TypeScript 生成原生数据结构代码。Fory IDL 也可以描述 RPC service;对于 Java、Python、Go、Rust、C#、Dart、Scala、Kotlin 和 JavaScript,compiler 可以生成使用 Fory 序列化 request/response payload 的 gRPC service companion。 ## 示例 Schema @@ -143,6 +143,14 @@ foryc example.fdl --output ./generated foryc example.fdl --lang java,python,csharp,javascript,swift,dart --output ./generated ``` +为包含 service 定义的 schema 生成 Java、Python、Go、Rust、C#、Dart、Scala、Kotlin 和 JavaScript model 以及 gRPC service companion: + +```bash +foryc animals.fdl --java_out=./generated/java --python_out=./generated/python --go_out=./generated/go --rust_out=./generated/rust --csharp_out=./generated/csharp --dart_out=./generated/dart --scala_out=./generated/scala --kotlin_out=./generated/kotlin --javascript_out=./generated/javascript --grpc +``` + +生成的 service 代码使用标准 gRPC API,但 request 和 response 对象使用 Fory 序列化。应用需要自行提供 grpc-java、grpc-kotlin、Scala grpc-java API、`grpcio`、grpc-go、Rust `tonic` 和 `bytes`、C# `Grpc.Core.Api` 及 server/client 依赖,或 Dart `package:grpc`;Fory package 不会把 gRPC 作为硬依赖。Python companion 默认使用 `grpc.aio`,也可以通过 `--grpc-python-mode=sync` 生成同步模式。JavaScript Node.js companion 使用 `@grpc/grpc-js`;浏览器 client 通过 `--grpc-web` 单独生成并使用 `grpc-web`。 + ### 4. 使用生成代码 **Java:** diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/protobuf-idl.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/protobuf-idl.md index 13c0de6995..6b0c3ef9bf 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/protobuf-idl.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/protobuf-idl.md @@ -48,9 +48,9 @@ license: | | 循环引用 | 不支持 | 支持 | | 未知字段 | 保留 | 不保留 | | 生成类型 | protobuf 专用模型类型 | 语言原生构造 | -| gRPC 生态 | 原生成熟 | 持续建设中(活跃开发) | +| gRPC 生态 | 原生成熟 | Java/Python/Go/Rust/C#/Dart/Scala/Kotlin/JavaScript service codegen | -Fory 的 gRPC 支持仍在持续开发中。当前生产级 gRPC 工作流里,protobuf 仍是更成熟的默认选择。 +Fory 可以通过 `--grpc` 生成 Java、Python、Go、Rust、C#、Dart、Scala、Kotlin 和 JavaScript gRPC service companion。JavaScript 浏览器 client 通过 `--grpc-web` 生成。这些 service 使用标准 gRPC 传输,但 request 和 response payload 使用 Fory 序列化,而不是 protobuf。对于广泛的 gRPC 生态工具、schema reflection 和 protobuf-native interceptor,protobuf 仍是更成熟的默认选择。 ## 为什么使用 Apache Fory @@ -295,6 +295,14 @@ message TreeNode { 将 protobuf 代码生成步骤替换为 Fory 编译器针对目标语言的生成命令。 +对于支持的 service 输出,添加 `--grpc` 以生成 gRPC companion 代码: + +```bash +foryc api.proto --java_out=./generated/java --python_out=./generated/python --go_out=./generated/go --rust_out=./generated/rust --csharp_out=./generated/csharp --dart_out=./generated/dart --scala_out=./generated/scala --kotlin_out=./generated/kotlin --javascript_out=./generated/javascript --grpc +``` + +生成的 Java service 文件依赖 grpc-java;Python service module 默认使用 `grpc.aio`;Rust service 文件导入 `tonic` 和 `bytes`;Go service 文件导入 grpc-go;JavaScript Node.js service 文件导入 `@grpc/grpc-js`;C# service 文件导入 `Grpc.Core.Api` 类型;Dart service 文件导入 `package:grpc`;Scala service 文件依赖 grpc-java;Kotlin service 文件依赖 grpc-java 和 grpc-kotlin。请在应用构建中加入这些依赖;Fory package 不会把 gRPC 作为硬依赖。同步 Python `grpcio` companion 可使用 `--grpc-python-mode=sync`。JavaScript 输出配合 `--grpc-web` 可生成导入 `grpc-web` 的浏览器 client。 + ### 第 5 步:执行兼容性验证 分阶段迁移时,可并行保留两种格式,并通过集成测试验证 payload 级一致性。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/schema-idl.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/schema-idl.md index baabf880e7..3e8fd2db19 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/schema-idl.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/compiler/schema-idl.md @@ -860,6 +860,20 @@ message Order [id=206] { } ``` +## Service 定义 + +Service 用于在 Fory IDL 中定义 RPC method 契约。它是可选的:包含 service 的 schema 仍会生成常规数据 model;只有在 compiler 使用 `--grpc` 且目标语言受支持时,才会生成 gRPC service 代码。支持的输出包括 Java、Python、Go、Rust、C#、Dart、Scala、Kotlin 和 JavaScript。JavaScript 浏览器 gRPC-Web client 通过 `--grpc-web` 生成。 + +```protobuf +service PetDirectory { + rpc Lookup (PetRequest) returns (PetResponse); + rpc Watch (PetRequest) returns (stream PetResponse); +} +``` + +- 生成的 gRPC companion 会对每个 RPC payload 使用 Fory 序列化。 +- 编译或运行这些 companion 的应用需要自行提供 gRPC 依赖,例如 grpc-java、grpc-kotlin、`grpcio`、grpc-go、Rust `tonic` 和 `bytes`、Scala grpc-java API、`@grpc/grpc-js`、`grpc-web`、C# `Grpc.Core.Api` 及 server/client package,或 Dart `package:grpc`。Python companion 默认使用 `grpc.aio`,也可以通过 `--grpc-python-mode=sync` 生成同步模式。 + ## 语法摘要 以下为简化文法(便于快速查阅,具体以编译器实现为准): diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/cpp/configuration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/cpp/configuration.md index 62a93572f0..f6518d611b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/cpp/configuration.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/cpp/configuration.md @@ -69,6 +69,10 @@ auto fory = Fory::builder() .xlang(true) .track_ref(true) .max_dyn_depth(10) + .max_type_fields(512) + .max_type_meta_bytes(4096) + .max_schema_versions_per_type(10) + .max_average_schema_versions_per_type(3) .check_struct_version(true) .build(); ``` @@ -136,6 +140,54 @@ auto fory = Fory::builder() - **增加**:对于合法的深度嵌套数据结构 - **减少**:对于更严格的安全要求或浅层数据结构 +### max_schema_versions_per_type(uint32_t) + +设置一个逻辑类型可接受的最大远端 metadata 版本数。 + +```cpp +auto fory = Fory::builder() + .max_schema_versions_per_type(10) + .build(); +``` + +**默认值:** `10` + +### max_type_fields(uint32_t) + +设置一个收到的远端 struct metadata body 中可接受的最大字段数。 + +```cpp +auto fory = Fory::builder() + .max_type_fields(512) + .build(); +``` + +**默认值:** `512` + +### max_type_meta_bytes(uint32_t) + +设置一个收到的 TypeDef body 可接受的最大编码 body 字节数,不包含 8 字节 header 和扩展 size varint。 + +```cpp +auto fory = Fory::builder() + .max_type_meta_bytes(4096) + .build(); +``` + +**默认值:** `4096` + +### max_average_schema_versions_per_type(uint32_t) + +设置所有已接受远端类型的平均 metadata 版本数限制。有效全局下限为 `8192` 个 schema。 + +```cpp +auto fory = Fory::builder() + .max_average_schema_versions_per_type(3) + .build(); +``` + +**默认值:** `3` + ### check_struct_version(bool) 启用/禁用结构体版本检查。 @@ -174,13 +226,27 @@ auto fory = Fory::builder() ## 配置摘要 -| 选项 | 说明 | 默认值 | -| ---------------------------- | ---------------------- | ------- | -| `xlang(bool)` | 启用跨语言模式 | `true` | -| `compatible(bool)` | 启用 schema 演化 | `false` | -| `track_ref(bool)` | 启用引用跟踪 | `true` | -| `max_dyn_depth(uint32_t)` | 动态类型的最大嵌套深度 | `5` | -| `check_struct_version(bool)` | 启用结构体版本检查 | `false` | +| 选项 | 说明 | 默认值 | +| ------------------------------------------------ | --------------------------------- | ------- | +| `xlang(bool)` | 启用跨语言模式 | `true` | +| `compatible(bool)` | 启用 schema 演化 | `false` | +| `track_ref(bool)` | 启用引用跟踪 | `true` | +| `max_dyn_depth(uint32_t)` | 动态类型的最大嵌套深度 | `5` | +| `max_type_fields(uint32_t)` | 一个收到的 struct metadata body 最大字段数 | `512` | +| `max_type_meta_bytes(uint32_t)` | 一个收到的 metadata body 最大编码字节数 | `4096` | +| `max_schema_versions_per_type(uint32_t)` | 一个逻辑类型最大远端 metadata 版本数 | `10` | +| `max_average_schema_versions_per_type(uint32_t)` | 所有远端类型的平均 metadata 版本数 | `3` | +| `check_struct_version(bool)` | 启用结构体版本检查 | `false` | + +## 安全 + +安全相关配置: + +- 在反序列化不可信 payload 前,只注册预期的类型。 +- 对 intentional same-schema payload,将 `check_struct_version(true)` 与 `compatible(false)` 配合使用。 +- 尽可能降低 `max_dyn_depth(...)`,以拒绝异常深的多态对象图。 +- 除非数据不是恶意输入,且可信 peer 会发送更大的 metadata 或大量 schema 版本,否则保持远端 schema metadata 限制的默认值。 +- 对不可信输入,优先使用具体字段,避免宽泛的多态字段。 ## 相关主题 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/cpp/index.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/cpp/index.md index 92599c21e3..2b0805b159 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/cpp/index.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/cpp/index.md @@ -61,7 +61,7 @@ include(FetchContent) FetchContent_Declare( fory GIT_REPOSITORY https://github.com/apache/fory.git - GIT_TAG v1.2.0 + GIT_TAG v1.3.0 SOURCE_SUBDIR cpp ) FetchContent_MakeAvailable(fory) @@ -91,11 +91,11 @@ module( bazel_dep(name = "rules_cc", version = "0.1.1") -bazel_dep(name = "fory", version = "1.2.0") +bazel_dep(name = "fory", version = "1.3.0") git_override( module_name = "fory", remote = "https://github.com/apache/fory.git", - commit = "v1.2.0", # 或使用特定 commit hash 以确保可复现性 + commit = "v1.3.0", # 或使用特定 commit hash 以确保可复现性 ) ``` @@ -126,7 +126,7 @@ bazel run //:my_app 对于本地开发,也可以改用 `local_path_override`: ```bazel -bazel_dep(name = "fory", version = "1.2.0") +bazel_dep(name = "fory", version = "1.3.0") local_path_override( module_name = "fory", path = "/path/to/fory", diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/csharp/configuration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/csharp/configuration.md index 9af3ce18e2..b4a21f2269 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/csharp/configuration.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/csharp/configuration.md @@ -35,12 +35,16 @@ ThreadSafeFory threadSafe = Fory.Builder().BuildThreadSafe(); `Fory.Builder().Build()` 使用以下默认值: -| 选项 | 默认值 | 说明 | -| -------------------- | -------- | ---------------------------------- | -| `TrackRef` | `false` | 默认关闭引用跟踪 | -| `Compatible` | `false` | Schema 一致模式,不写入演进元数据 | -| `CheckStructVersion` | `false` | 默认关闭结构体 schema hash 校验 | -| `MaxDepth` | `20` | 动态对象图的最大嵌套深度 | +| 选项 | 默认值 | 说明 | +| --------------------------------- | ------- | --------------------------------- | +| `TrackRef` | `false` | 默认关闭引用跟踪 | +| `Compatible` | `false` | Schema 一致模式,不写入演进元数据 | +| `CheckStructVersion` | `false` | 默认关闭结构体 schema hash 校验 | +| `MaxDepth` | `20` | 动态对象图的最大嵌套深度 | +| `MaxTypeFields` | `512` | 一个收到的 struct metadata body 最大字段数 | +| `MaxTypeMetaBytes` | `4096` | 一个收到的 metadata body 最大编码字节数 | +| `MaxSchemaVersionsPerType` | `10` | 一个逻辑类型最大远端 metadata 版本数 | +| `MaxAverageSchemaVersionsPerType` | `3` | 所有远端类型的平均 metadata 版本数 | ## 构建器选项 @@ -88,6 +92,46 @@ Fory fory = Fory.Builder() `value` 必须大于 `0`。 +### `MaxTypeFields(int value)` + +设置一个收到的远端 struct metadata body 中可接受的最大字段数。 + +```csharp +Fory fory = Fory.Builder() + .MaxTypeFields(512) + .Build(); +``` + +### `MaxTypeMetaBytes(int value)` + +设置一个收到的 TypeMeta body 可接受的最大编码 body 字节数,不包含 8 字节 header 和扩展 size varint。 + +```csharp +Fory fory = Fory.Builder() + .MaxTypeMetaBytes(4096) + .Build(); +``` + +### `MaxSchemaVersionsPerType(int value)` + +设置一个逻辑类型可接受的最大远端 metadata 版本数。 + +```csharp +Fory fory = Fory.Builder() + .MaxSchemaVersionsPerType(10) + .Build(); +``` + +### `MaxAverageSchemaVersionsPerType(int value)` + +设置所有已接受远端类型的平均 metadata 版本数限制。有效全局下限为 `8192` 个 schema。 + +```csharp +Fory fory = Fory.Builder() + .MaxAverageSchemaVersionsPerType(3) + .Build(); +``` + ## 常见配置 ### 追求速度的 Schema 一致服务 @@ -117,6 +161,16 @@ ThreadSafeFory fory = Fory.Builder() .BuildThreadSafe(); ``` +## 安全 + +安全相关配置: + +- 在反序列化不可信 payload 前,只注册预期的类型。 +- 对 intentional same-schema payload,将 `CheckStructVersion(true)` 与 `Compatible(false)` 配合使用。 +- 设置 `MaxDepth(...)` 以拒绝异常深的动态对象图。 +- 除非数据不是恶意输入,且可信 peer 会发送更大的 metadata 或大量 schema 版本,否则保持远端 schema metadata 限制的默认值。 +- 对不可信输入,优先使用生成或已注册的具体 model,避免宽泛的动态字段。 + ## 相关主题 - [基础序列化](basic_serialization) diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/csharp/grpc-support.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/csharp/grpc-support.md index d9d7636e07..d68d388e6e 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/csharp/grpc-support.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/csharp/grpc-support.md @@ -32,7 +32,7 @@ Server project: ```xml <ItemGroup> - <PackageReference Include="Apache.Fory" Version="1.2.0" /> + <PackageReference Include="Apache.Fory" Version="1.3.0" /> <PackageReference Include="Grpc.AspNetCore" Version="2.71.0" /> </ItemGroup> ``` @@ -41,7 +41,7 @@ Client project: ```xml <ItemGroup> - <PackageReference Include="Apache.Fory" Version="1.2.0" /> + <PackageReference Include="Apache.Fory" Version="1.3.0" /> <PackageReference Include="Grpc.Core.Api" Version="2.71.0" /> <PackageReference Include="Grpc.Net.Client" Version="2.71.0" /> </ItemGroup> diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/csharp/index.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/csharp/index.md index 94d9c269e5..5e7137c845 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/csharp/index.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/csharp/index.md @@ -43,7 +43,7 @@ Apache Fory™ C# 是面向 .NET 的高性能跨语言序列化运行时。它 ```xml <ItemGroup> - <PackageReference Include="Apache.Fory" Version="1.2.0" /> + <PackageReference Include="Apache.Fory" Version="1.3.0" /> </ItemGroup> ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/dart/configuration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/dart/configuration.md index b93aa2839e..93d5a07eeb 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/dart/configuration.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/dart/configuration.md @@ -35,6 +35,10 @@ final fory = Fory(); final fory = Fory( compatible: true, maxDepth: 512, + maxTypeFields: 512, + maxTypeMetaBytes: 4096, + maxSchemaVersionsPerType: 10, + maxAverageSchemaVersionsPerType: 3, ); ``` @@ -80,6 +84,24 @@ final fory = Fory( final fory = Fory(maxDepth: 128); ``` +### 远端 Schema Metadata 限制 + +兼容模式可能接收用于 Schema 演进的远端 metadata。以下限制用于约束 metadata 大小和可接受的 schema 版本数: + +```dart +final fory = Fory( + maxTypeFields: 512, + maxTypeMetaBytes: 4096, + maxSchemaVersionsPerType: 10, + maxAverageSchemaVersionsPerType: 3, +); +``` + +- `maxTypeFields` 限制一个收到的 struct metadata body 中的字段数。 +- `maxTypeMetaBytes` 限制一个收到的 TypeMeta body 的编码 body 字节数,不包含 8 字节 header 和扩展 size varint。 +- `maxSchemaVersionsPerType` 限制一个逻辑类型可接受的远端 metadata 版本数。 +- `maxAverageSchemaVersionsPerType` 限制所有已接受远端类型的平均版本数;有效全局下限为 `8192` 个 schema。 + ### `maxCollectionSize` 任意单个 list、set 或 map 字段可接受的最大元素数。用于防止畸形消息触发失控的内存分配。 @@ -98,13 +120,17 @@ final fory = Fory(maxBinarySize: 8 * 1024 * 1024); ## 默认值 -| 选项 | 默认值 | -| -------------------- | --------- | -| `compatible` | `false` | -| `checkStructVersion` | `true` | -| `maxDepth` | 256 | -| `maxCollectionSize` | 1 048 576 | -| `maxBinarySize` | 64 MiB | +| 选项 | 默认值 | +| --------------------------------- | --------- | +| `compatible` | `false` | +| `checkStructVersion` | `true` | +| `maxDepth` | 256 | +| `maxTypeFields` | 512 | +| `maxTypeMetaBytes` | 4096 | +| `maxSchemaVersionsPerType` | 10 | +| `maxAverageSchemaVersionsPerType` | 3 | +| `maxCollectionSize` | 1 048 576 | +| `maxBinarySize` | 64 MiB | ## 跨语言说明 @@ -113,6 +139,7 @@ final fory = Fory(maxBinarySize: 8 * 1024 * 1024); - 如果任意一端需要 Schema 演进,则**所有**端都应设置 `compatible: true`。 - 每一端都要使用相同的数字 ID,或者相同的 `namespace + typeName` 组合。 - 写端和读端的 `compatible` 设置必须一致,模式不匹配会直接失败。 +- 除非数据不是恶意输入,且可信 peer 会发送更大的 metadata 或大量 schema 版本,否则保持远端 schema metadata 限制的默认值。 ## 相关主题 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/dart/grpc-support.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/dart/grpc-support.md new file mode 100644 index 0000000000..333a9b69b2 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/dart/grpc-support.md @@ -0,0 +1,284 @@ +--- +title: gRPC 支持 +sidebar_position: 12 +id: grpc_support +license: | + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--- + +Fory 可以为包含 service 定义的 schema 生成 Dart gRPC service companion。 +生成代码使用标准 `package:grpc` client、service base、method descriptor、 +call option、deadline、取消和 status code;request 和 response 对象则使用 +Fory 序列化,而不是 protobuf。 + +当 RPC 两端都由同一份 Fory IDL、protobuf IDL 或 FlatBuffers IDL 生成,并且 +两端都期望 Fory 编码的 message body 时,可以使用这种模式。如果 API 必须被通用 +protobuf client、reflection 工具或期望 protobuf message bytes 的组件消费,请使用 +标准 protobuf gRPC 代码生成。 + +## 添加依赖 + +`fory` package 不会加入 gRPC 依赖。编译或运行生成 service companion 的应用需要 +添加 `grpc`,同时添加用于生成 Fory serializer 代码的 `build_runner` dev dependency: + +```yaml +dependencies: + fory: ^1.3.0 + grpc: ^4.0.0 + +dev_dependencies: + build_runner: ^2.4.0 +``` + +client 和 server 应用使用同一组依赖。 + +## 定义 Service + +Service 定义可以来自 Fory IDL、protobuf IDL 或 FlatBuffers `rpc_service` 定义。 +Fory IDL service 示例: + +```protobuf +package demo.greeter; + +message HelloRequest { + string name = 1; +} + +message HelloReply { + string reply = 1; +} + +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply); +} +``` + +使用 `--grpc` 生成 Dart model 和 gRPC companion 代码: + +```bash +foryc service.fdl --dart_out=./lib/generated --grpc +``` + +然后运行一次 `build_runner`,为生成的 model 输出 Fory serializer part 文件。代码运行前 +必须执行这一步: + +```bash +dart run build_runner build --delete-conflicting-outputs +``` + +对这个 schema,Dart generator 会输出如下文件(model 文件和 module 名称来自 package +最后一段 `greeter`): + +| 文件 | 用途 | +| ------------------------------------------- | ----------------------------------------- | +| `demo/greeter/greeter.dart` | Fory model type 和 schema module | +| `demo/greeter/greeter.fory.dart` | Serializer 和注册逻辑,由 build_runner 生成 | +| `demo/greeter/greeter_grpc.dart` | gRPC client、service base 和 method descriptor | +| `GreeterForyModule` in `greeter.dart` | 生成类型的 Fory 注册 module | +| `GreeterServiceBase` in `greeter_grpc.dart` | server 实现使用的 base class | +| `GreeterClient` in `greeter_grpc.dart` | gRPC 调用使用的 client stub | + +生成的 client 和 service base 会自动获取可用的 `Fory`,并在首次使用时注册该 +schema 的类型,因此不需要手动注册。若需要共享自定义 `Fory`(例如已经配置了额外 +module 的实例),可在第一次 RPC 前调用一次 `GreeterForyModule.install(yourFory)`; +这是可选操作。 + +## 实现 Server + +继承生成的 `GreeterServiceBase`,并用 grpc-dart 的 `Server` 承载: + +```dart +import 'dart:io'; + +import 'package:grpc/grpc.dart'; +import 'demo/greeter/greeter.dart'; +import 'demo/greeter/greeter_grpc.dart'; + +class GreeterService extends GreeterServiceBase { + @override + Future<HelloReply> sayHello(ServiceCall call, HelloRequest request) async { + final reply = HelloReply()..reply = 'Hello, ${request.name}'; + return reply; + } +} + +Future<void> main() async { + final server = Server.create(services: [GreeterService()]); + await server.serve(address: InternetAddress.loopbackIPv4, port: 50051); +} +``` + +## 创建 Client + +通过 `ClientChannel` 使用生成的 client: + +```dart +import 'package:grpc/grpc.dart'; +import 'demo/greeter/greeter.dart'; +import 'demo/greeter/greeter_grpc.dart'; + +Future<void> main() async { + final channel = ClientChannel( + 'localhost', + port: 50051, + options: const ChannelOptions( + credentials: ChannelCredentials.insecure(), + ), + ); + final client = GreeterClient(channel); + + final reply = await client.sayHello(HelloRequest()..name = 'Fory'); + print(reply.reply); + + await channel.shutdown(); +} +``` + +## Streaming RPC + +Fory service 定义可以使用相同的 gRPC streaming 形态: + +```protobuf +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply); + rpc LotsOfReplies (HelloRequest) returns (stream HelloReply); + rpc LotsOfGreetings (stream HelloRequest) returns (HelloReply); + rpc Chat (stream HelloRequest) returns (stream HelloReply); +} +``` + +生成的 Dart 方法遵循 grpc-dart 约定。单响应方法返回 `ResponseFuture<R>` +(client-streaming 会用 `.single` 适配调用);streaming 响应方法返回 +`ResponseStream<R>`。Server 端,单请求以 message type 传入,streaming 请求以 +`Stream` 传入;方法对单响应返回 `Future`,对 streaming 响应返回 `Stream`: + +| IDL shape | Client 方法 | Server 方法(override) | +| ----------------------------------------- | --------------------------------------------------- | ----------------------------------------------------- | +| `rpc A (Req) returns (Res)` | `ResponseFuture<Res> a(Req request, {CallOptions?})` | `Future<Res> a(ServiceCall call, Req request)` | +| `rpc A (Req) returns (stream Res)` | `ResponseStream<Res> a(Req request, {CallOptions?})` | `Stream<Res> a(ServiceCall call, Req request)` | +| `rpc A (stream Req) returns (Res)` | `ResponseFuture<Res> a(Stream<Req> request, {...})` | `Future<Res> a(ServiceCall call, Stream<Req> request)` | +| `rpc A (stream Req) returns (stream Res)` | `ResponseStream<Res> a(Stream<Req> request, {...})` | `Stream<Res> a(ServiceCall call, Stream<Req> request)` | + +Server 实现直接使用生成的 streaming 方法形态: + +```dart +class GreeterService extends GreeterServiceBase { + @override + Stream<HelloReply> lotsOfReplies( + ServiceCall call, + HelloRequest request, + ) async* { + for (final greeting in ['Hello, ${request.name}', 'Welcome, ${request.name}']) { + yield HelloReply()..reply = greeting; + } + } + + @override + Future<HelloReply> lotsOfGreetings( + ServiceCall call, + Stream<HelloRequest> request, + ) async { + final names = <String>[]; + await for (final message in request) { + names.add(message.name); + } + return HelloReply()..reply = names.join(', '); + } + + @override + Stream<HelloReply> chat( + ServiceCall call, + Stream<HelloRequest> request, + ) async* { + await for (final message in request) { + yield HelloReply()..reply = 'Hello, ${message.name}'; + } + } +} +``` + +生成的 client 返回标准 grpc-dart 调用对象: + +```dart +// Server streaming. +await for (final reply in client.lotsOfReplies(HelloRequest()..name = 'Fory')) { + print(reply.reply); +} + +// Client streaming. +final summary = await client.lotsOfGreetings( + Stream.fromIterable([ + HelloRequest()..name = 'Ada', + HelloRequest()..name = 'Grace', + ]), +); +print(summary.reply); + +// Bidirectional streaming. +await for (final reply in client.chat( + Stream.fromIterable([HelloRequest()..name = 'Fory']), +)) { + print(reply.reply); +} +``` + +生成的 descriptor 会为 gRPC path 保留 IDL 中精确的 service 和 method 名称; +Dart 方法使用 camelCase 名称。 + +## 生成的 Module 名称 + +Dart model 文件和 schema module 按 package 的最后一段命名,而不是按 gRPC service +命名。(当 schema 没有 package 时,使用源文件 stem。) + +| Schema 输入(package) | Model 文件 | Schema module | +| ----------------------------- | ------------------- | ----------------------- | +| `service.fdl` (`demo.greeter`) | `greeter.dart` | `GreeterForyModule` | +| `api.fdl` (`demo.order_events`) | `order_events.dart` | `OrderEventsForyModule` | +| `greeter.fdl` (`demo.greeter`) | `greeter.dart` | `GreeterForyModule` | + +名为 `Greeter` 的 gRPC service 仍会生成 `<stem>_grpc.dart` companion,其中包含 +`GreeterClient` 和 `GreeterServiceBase`;它不会改变 schema module 名称。如果多个 +schema 文件使用同一个 package leaf,请将它们放到不同输出目录,或选择能生成不同 Dart +model 文件的 package/file 名称。 + +## gRPC 运行时行为 + +生成的 service 代码只替换 request 和 response 序列化。所有常规 gRPC 运行行为仍由你的 +gRPC stack 负责: + +- Deadline 和取消 +- TLS 和认证 +- 名称解析与负载均衡 +- Client 和 server interceptor +- Status code 和 metadata +- Channel 生命周期管理 + +## 故障排查 + +### 缺少 `package:grpc` 类型 + +将 `grpc` 加到应用依赖。生成的 Fory service 文件会 import grpc-dart API,但 `fory` +有意不依赖 gRPC。 + +### 生成代码引用缺失的 `.fory.dart` Part + +生成或重新生成 Dart source 后,运行 `dart run build_runner build --delete-conflicting-outputs`。 +Serializer part 文件由 `build_runner` 生成,不由 `foryc` 直接生成。 + +### Protobuf Client 无法解码 Service + +Fory gRPC companion 不使用 protobuf 编码格式。请为 Fory-generated service 使用 +Fory-generated client,或为通用 protobuf client 暴露单独的 protobuf service endpoint。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/dart/index.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/dart/index.md index 569fb7f5c1..1d36db9233 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/dart/index.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/dart/index.md @@ -42,7 +42,7 @@ Apache Fory™ Dart 可以把 Dart 对象序列化为字节,再从字节反序 ```yaml dependencies: - fory: ^1.2.0 + fory: ^1.3.0 dev_dependencies: build_runner: ^2.4.0 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/go/configuration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/go/configuration.md index 770e5a1022..87301e7ad8 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/go/configuration.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/go/configuration.md @@ -33,12 +33,16 @@ f := fory.New() 默认设置: -| Option | Default | Description | -| ---------- | ------- | ------------------------ | -| TrackRef | false | 关闭引用跟踪 | -| MaxDepth | 20 | 最大嵌套深度 | -| IsXlang | false | 关闭跨语言模式 | -| Compatible | false | 关闭 Schema 演进兼容模式 | +| Option | Default | Description | +| ------------------------------- | ------- | -------------------------------------- | +| TrackRef | false | 关闭引用跟踪 | +| MaxDepth | 20 | 最大嵌套深度 | +| IsXlang | false | 关闭跨语言模式 | +| Compatible | false | 关闭 Schema 演进兼容模式 | +| MaxTypeFields | 512 | 一个收到的 struct metadata body 最大字段数 | +| MaxTypeMetaBytes | 4096 | 一个收到的 metadata body 最大编码字节数 | +| MaxSchemaVersionsPerType | 10 | 一个逻辑类型最大远端 metadata 版本数 | +| MaxAverageSchemaVersionsPerType | 3 | 所有远端类型的平均 metadata 版本数 | ### 通过选项配置 @@ -47,6 +51,10 @@ f := fory.New( fory.WithTrackRef(true), fory.WithCompatible(true), fory.WithMaxDepth(10), + fory.WithMaxTypeFields(512), + fory.WithMaxTypeMetaBytes(4096), + fory.WithMaxSchemaVersionsPerType(10), + fory.WithMaxAverageSchemaVersionsPerType(3), ) ``` @@ -118,6 +126,38 @@ f := fory.New(fory.WithMaxDepth(30)) - 防护深层递归结构或恶意数据 - 超过限制会返回错误 +### WithMaxTypeFields + +设置一个收到的远端 struct metadata body 中可接受的最大字段数: + +```go +f := fory.New(fory.WithMaxTypeFields(512)) +``` + +### WithMaxTypeMetaBytes + +设置一个收到的 TypeDef body 可接受的最大编码 body 字节数,不包含 8 字节 header 和扩展 size varint: + +```go +f := fory.New(fory.WithMaxTypeMetaBytes(4096)) +``` + +### WithMaxSchemaVersionsPerType + +设置一个逻辑类型可接受的最大远端 metadata 版本数: + +```go +f := fory.New(fory.WithMaxSchemaVersionsPerType(10)) +``` + +### WithMaxAverageSchemaVersionsPerType + +设置所有已接受远端类型的平均 metadata 版本数限制。有效全局下限为 `8192` 个 schema: + +```go +f := fory.New(fory.WithMaxAverageSchemaVersionsPerType(3)) +``` + ### WithXlang 启用跨语言序列化模式: @@ -330,6 +370,7 @@ for req := range requests { 4. **需要长期保存数据时请复制**:默认实例返回的字节切片会在后续调用后失效。 5. **合理设置最大深度**:深层结构可适当调大,但需关注内存占用。 6. **Schema 可能演进时启用兼容模式**:服务版本存在结构差异时建议开启。 +7. **保留远端 metadata 限制默认值**:除非输入可信且 peer 确实会发送更大的 metadata 或大量 schema 版本。 ## 相关主题 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/go/grpc-support.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/go/grpc-support.md index d8cdd580bb..21f5552dc9 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/go/grpc-support.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/go/grpc-support.md @@ -175,7 +175,7 @@ Fory service 支持 unary、server-streaming、client-streaming 和 bidirectiona - Bidirectional streaming 使用生成的 stream client/server interface。 - 每个 message frame 都使用生成 codec。 -## Service 行为 +## gRPC 运行时行为 生成的 service companion 只提供 Fory 序列化。deadline、取消、TLS、credential、unary/stream interceptor、status code、metadata、名称解析、负载均衡、连接生命周期和 backoff 等 Service 行为都遵循标准 grpc-go。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/java/compression.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/java/compression.md index e43ad76931..35d44152f8 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/java/compression.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/java/compression.md @@ -84,7 +84,7 @@ CompressedArraySerializers.registerSerializers(fory); <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-simd</artifactId> - <version>1.2.0</version> + <version>1.3.0</version> </dependency> ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/java/configuration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/java/configuration.md index 49aa47f4a3..afdf2f5ef9 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/java/configuration.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/java/configuration.md @@ -38,6 +38,10 @@ license: | | `registerGuavaTypes` | 是否预注册 Guava 类型,例如 `RegularImmutableMap` / `RegularImmutableList`。这些类型不是公共 API,但看起来相当稳定。 | `true` | | `requireClassRegistration` | 关闭后可能允许未知类被反序列化,从而带来安全风险。 | `true` | | `maxDepth` | 设置反序列化的最大深度,超过时会抛出异常。可用于阻止反序列化 DDOS 攻击。 | `50` | +| `maxTypeFields` | 一个收到的远端 struct metadata body 中可接受的最大字段数。 | `512` | +| `maxTypeMetaBytes` | 一个收到的 TypeDef 或 TypeMeta body 可接受的最大编码 body 字节数,不包含 8 字节 header 和扩展 size varint。 | `4096` | +| `maxSchemaVersionsPerType` | 一个逻辑类型可接受的最大远端 metadata 版本数。 | `10` | +| `maxAverageSchemaVersionsPerType` | 所有已接受远端类型的平均 metadata 版本数限制;有效全局下限为 `8192` 个 metadata entry。 | `3` | | `suppressClassRegistrationWarnings` | 是否抑制类注册警告。这些警告可用于安全审计,但可能较为烦人,因此默认启用抑制。 | `true` | | `metaShareEnabled` | 启用或禁用元数据共享模式。 | 如果设置了 `CompatibleMode.COMPATIBLE` 则为 `true`,否则为 false。 | | `scopedMetaShareEnabled` | 作用域元数据共享只关注单次序列化过程。在该过程中创建或识别的元数据只归属于这次序列化,不会与其他序列化共享。 | 如果设置了 `CompatibleMode.COMPATIBLE` 则为 `true`,否则为 false。 | @@ -70,6 +74,17 @@ Fory fory = Fory.builder() .build(); ``` +## 安全 + +安全相关选项: + +- `requireClassRegistration(true)` 将反序列化限制为已注册类。 +- `withMaxDepth(...)` 拒绝异常深的对象图。 +- `withMaxTypeFields(...)` 和 `withMaxTypeMetaBytes(...)` 约束一个收到的远端 metadata body 的字段数和编码 body 大小。 +- `withMaxSchemaVersionsPerType(...)` 和 `withMaxAverageSchemaVersionsPerType(...)` 约束可接受的远端 metadata 版本数,但不改变注册、动态加载或 Schema 演进语义。 +- `withDeserializeUnknownClass(false)` 避免从 metadata 物化 unknown class。 +- `checkJdkClassSerializable(true)` 保持对 `java.*` class 的 JDK serializability 检查。 + ## 相关主题 - [字段配置](schema-metadata.md) - `@ForyField`、`@Ignore` 与整数编码注解 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/java/grpc-support.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/java/grpc-support.md index 23ede2278f..15c26ea22f 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/java/grpc-support.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/java/grpc-support.md @@ -354,7 +354,7 @@ final class StreamingClient { 生成 descriptor 会保留 IDL 中的 service 和 method 名称作为 gRPC path。 -## Service 行为 +## gRPC 运行时行为 生成的 service code 只替换 request/response 序列化。常规 gRPC service 行为仍由 grpc-java 提供: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/javascript/configuration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/javascript/configuration.md index e107aac1f5..0f52349daa 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/javascript/configuration.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/javascript/configuration.md @@ -41,22 +41,30 @@ const fory = new Fory({ ref: true, compatible: true, maxDepth: 100, + maxTypeFields: 512, + maxTypeMetaBytes: 4096, + maxSchemaVersionsPerType: 10, + maxAverageSchemaVersionsPerType: 3, maxBinarySize: 64 * 1024 * 1024, maxCollectionSize: 1_000_000, hps, }); ``` -| 选项 | 默认值 | 说明 | -| -------------------------- | ----------- | ---------------------------------------------------------------- | -| `ref` | `false` | 为共享或循环对象图启用引用跟踪 | -| `compatible` | `true` | 允许新增或删除字段而不破坏现有消息 | -| `maxDepth` | `50` | 最大嵌套深度。必须 `>= 2`。对于深度嵌套结构可以调大 | -| `maxBinarySize` | 64 MiB | 任意单个二进制字段可接受的最大字节数 | -| `maxCollectionSize` | `1_000_000` | 任意 list、set 或 map 可接受的最大元素数 | -| `useSliceString` | `false` | Node.js 的可选字符串读取优化。除非已做基准测试,否则保持默认值 | -| `hps` | 未设置 | 来自 `@apache-fory/hps` 的可选快速字符串辅助库(Node.js 20+) | -| `hooks.afterCodeGenerated` | 未设置 | 用于检查生成的序列化器代码的回调,便于调试 | +| 选项 | 默认值 | 说明 | +| --------------------------------- | ----------- | ---------------------------------------------------------------- | +| `ref` | `false` | 为共享或循环对象图启用引用跟踪 | +| `compatible` | `true` | 允许新增或删除字段而不破坏现有消息 | +| `maxDepth` | `50` | 最大嵌套深度。必须 `>= 2`。对于深度嵌套结构可以调大 | +| `maxTypeFields` | `512` | 一个收到的远端 struct metadata body 最大字段数 | +| `maxTypeMetaBytes` | `4096` | 一个收到的 TypeMeta body 最大编码字节数 | +| `maxSchemaVersionsPerType` | `10` | 一个逻辑类型最大远端 metadata 版本数 | +| `maxAverageSchemaVersionsPerType` | `3` | 所有远端类型的平均 metadata 版本数 | +| `maxBinarySize` | 64 MiB | 任意单个二进制字段可接受的最大字节数 | +| `maxCollectionSize` | `1_000_000` | 任意 list、set 或 map 可接受的最大元素数 | +| `useSliceString` | `false` | Node.js 的可选字符串读取优化。除非已做基准测试,否则保持默认值 | +| `hps` | 未设置 | 来自 `@apache-fory/hps` 的可选快速字符串辅助库(Node.js 20+) | +| `hooks.afterCodeGenerated` | 未设置 | 用于检查生成的序列化器代码的回调,便于调试 | ## 引用跟踪 @@ -96,6 +104,8 @@ const fory = new Fory({ hps }); - 在反序列化不可信载荷前,只注册预期的 schema。 - 根据服务可接受的最大载荷形状设置 `maxDepth`、`maxBinarySize` 和 `maxCollectionSize`。 +- 除非数据不是恶意输入,且可信 peer 会发送更大的远端 metadata,否则保持 `maxTypeFields` 和 `maxTypeMetaBytes` 的默认值。 +- 除非数据不是恶意输入,且可信 peer 会发送大量远端 schema 版本,否则保持 `maxSchemaVersionsPerType` 和 `maxAverageSchemaVersionsPerType` 的默认值。 - 对不可信输入,优先使用显式的 `Type.struct(...)` schema,而不是 `Type.any()`。 - 只传入与你部署的运行时版本配套的官方包中的 `hps`。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/javascript/grpc-support.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/javascript/grpc-support.md index f0cde1f61c..95e83fe1e4 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/javascript/grpc-support.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/javascript/grpc-support.md @@ -278,7 +278,7 @@ stream.on("end", () => { }); ``` -## Service 行为 +## gRPC 运行时行为 生成的 service code 只替换 request/response 序列化。常规 gRPC service 行为仍由 transport package 提供: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/kotlin/configuration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/kotlin/configuration.md index 67d1619599..1586775c3b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/kotlin/configuration.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/kotlin/configuration.md @@ -107,6 +107,9 @@ val fory = ForyKotlin.builder().withXlang(false) .withRefTracking(true) // Enable schema evolution support for native-mode payloads .withCompatible(true) + // Bound remote schema metadata resource usage + .withMaxTypeFields(512) + .withMaxTypeMetaBytes(4096) // Enable async compilation for better startup performance .withAsyncCompilation(true) // Compression options @@ -114,3 +117,23 @@ val fory = ForyKotlin.builder().withXlang(false) .withLongCompressed(true) .build() ``` + +## 安全 + +生产环境以及任何不受信任的 payload 来源都应保持启用类注册: + +```kotlin +val fory = ForyKotlin.builder() + .requireClassRegistration(true) + .withMaxDepth(50) + .withMaxTypeFields(512) + .withMaxTypeMetaBytes(4096) + .build() +``` + +安全相关配置: + +- 保持 `requireClassRegistration(true)`,并注册应用类或生成的 module。 +- 使用 `withMaxDepth(...)` 拒绝异常深的对象图。 +- 除非数据不是恶意输入,且可信 peer 会发送更大的 metadata 或大量 schema 版本,否则保持 `withMaxTypeFields(...)`、`withMaxTypeMetaBytes(...)` 以及远端 schema-version 限制的默认值。 +- Allow-listing 和 unknown-class 控制请遵循 [Java 配置](../java/configuration.md#forybuilder-选项)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/kotlin/grpc-support.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/kotlin/grpc-support.md index 89cc9893f2..ce9b7b81fa 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/kotlin/grpc-support.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/kotlin/grpc-support.md @@ -219,7 +219,7 @@ stub.chat( } ``` -## Service 行为 +## gRPC 运行时行为 生成的 service code 只替换 request/response 序列化。常规 gRPC service 行为仍由 grpc-java 和 grpc-kotlin 提供: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/kotlin/index.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/kotlin/index.md index 859dfa00f9..d5a9007d94 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/kotlin/index.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/kotlin/index.md @@ -50,14 +50,14 @@ Fory Kotlin 继承了 Fory Java 的全部特性,并增加了 Kotlin 特定优 <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-kotlin</artifactId> - <version>1.2.0</version> + <version>1.3.0</version> </dependency> ``` ### Gradle ```kotlin -implementation("org.apache.fory:fory-kotlin:1.2.0") +implementation("org.apache.fory:fory-kotlin:1.3.0") ``` ## 快速开始 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/python/configuration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/python/configuration.md index 203d7e5a57..4e5158b03b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/python/configuration.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/python/configuration.md @@ -33,7 +33,11 @@ class Fory: ref: bool = False, strict: bool = True, compatible: bool = False, - max_depth: int = 50 + max_depth: int = 50, + max_type_fields: int = 512, + max_type_meta_bytes: int = 4096, + max_schema_versions_per_type: int = 10, + max_average_schema_versions_per_type: int = 3 ) ``` @@ -49,19 +53,27 @@ class ThreadSafeFory: ref: bool = False, strict: bool = True, compatible: bool = False, - max_depth: int = 50 + max_depth: int = 50, + max_type_fields: int = 512, + max_type_meta_bytes: int = 4096, + max_schema_versions_per_type: int = 10, + max_average_schema_versions_per_type: int = 3 ) ``` ## 参数 -| 参数 | 类型 | 默认值 | 描述 | -| ------------ | ------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------- | -| `xlang` | `bool` | `False` | 启用跨语言序列化。当为 `False` 时,启用 Python 原生模式,支持所有 Python 对象。当为 `True` 时,启用跨语言模式,兼容 Java、Go、Rust 等。 | -| `ref` | `bool` | `False` | 启用引用跟踪以支持共享/循环引用。如果数据没有共享引用,禁用此选项可获得更好性能。 | -| `strict` | `bool` | `True` | 出于安全考虑需要类型注册。**生产环境强烈推荐**。仅在受信任的环境中禁用。 | -| `compatible` | `bool` | `False` | 在跨语言模式中启用 schema 演化,允许在保持兼容性的同时添加/删除字段。 | -| `max_depth` | `int` | `50` | 反序列化的最大深度,用于安全防护,防止栈溢出攻击。 | +| 参数 | 类型 | 默认值 | 描述 | +| -------------------------------------- | ------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| `xlang` | `bool` | `False` | 启用跨语言序列化。当为 `False` 时,启用 Python 原生模式,支持所有 Python 对象。当为 `True` 时,启用跨语言模式,兼容 Java、Go、Rust 等。 | +| `ref` | `bool` | `False` | 启用引用跟踪以支持共享/循环引用。如果数据没有共享引用,禁用此选项可获得更好性能。 | +| `strict` | `bool` | `True` | 出于安全考虑需要类型注册。**生产环境强烈推荐**。仅在受信任的环境中禁用。 | +| `compatible` | `bool` | `False` | 在跨语言模式中启用 schema 演化,允许在保持兼容性的同时添加/删除字段。 | +| `max_depth` | `int` | `50` | 反序列化的最大深度,用于安全防护,防止栈溢出攻击。 | +| `max_type_fields` | `int` | `512` | 一个收到的远端 struct metadata body 中可接受的最大字段数。 | +| `max_type_meta_bytes` | `int` | `4096` | 一个收到的 TypeDef body 可接受的最大编码 body 字节数,不包含 8 字节 header 和扩展 size varint。 | +| `max_schema_versions_per_type` | `int` | `10` | 一个逻辑类型可接受的最大远端 metadata 版本数。 | +| `max_average_schema_versions_per_type` | `int` | `3` | 所有已接受远端类型的平均 metadata 版本数限制;有效全局下限为 `8192` 个 schema。 | ## 核心方法 @@ -151,7 +163,11 @@ fory = pyfory.Fory( ref=False, # 如果有共享/循环引用则启用 strict=True, # 关键:生产环境始终为 True compatible=False, # 仅在需要 schema 演化时启用 - max_depth=20 # 根据数据结构深度调整 + max_depth=20, # 根据数据结构深度调整 + max_type_fields=512, + max_type_meta_bytes=4096, + max_schema_versions_per_type=10, + max_average_schema_versions_per_type=3, ) # 预先注册所有类型 @@ -160,6 +176,15 @@ fory.register(OrderModel, type_id=101) fory.register(ProductModel, type_id=102) ``` +收到的远端 metadata 也会受到限制: + +- `max_type_fields` 限制一个收到的 struct metadata body 中的字段数。 +- `max_type_meta_bytes` 限制一个收到的 TypeDef body 可接受的编码 body 字节数。 +- `max_schema_versions_per_type` 限制一个逻辑类型可接受的远端 metadata 版本数。 +- `max_average_schema_versions_per_type` 限制所有已接受远端类型的平均版本数。 + +这些限制不会改变 `strict`、policy、动态加载、unknown-class handling 或 Schema 演进语义。 + ### 开发环境配置 ```python diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/python/grpc-support.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/python/grpc-support.md index 0ee9beb11e..c2579e7cfa 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/python/grpc-support.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/python/grpc-support.md @@ -1,6 +1,6 @@ --- title: gRPC 支持 -sidebar_position: 9 +sidebar_position: 13 id: grpc_support license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -19,30 +19,31 @@ license: | limitations under the License. --- -Fory 可以为包含 service 定义的 schema 生成 Python gRPC companion module。生成代码使用 -`grpcio` 负责传输,request 和 response 对象使用 `pyfory` 序列化。 +Fory 可以为包含 service 定义的 schema 生成 Python gRPC service companion。生成 module +使用 `grpcio` 负责传输,并使用 Fory 序列化 request 和 response 对象。 -当两端都由同一份 Fory IDL、protobuf IDL 或 FlatBuffers IDL 生成,并且你希望使用 gRPC -传输语义与 Fory payload 编码时,可以使用这种模式。如果客户端或工具必须直接消费 protobuf -message bytes,请使用标准 protobuf gRPC 代码生成。 +当每个 RPC peer 都由同一份 Fory IDL、protobuf IDL 或 FlatBuffers IDL 生成,并且你希望 +使用 gRPC 传输语义与 Fory payload 编码时,可以使用这种模式。如果 client 或工具必须直接 +消费 protobuf message bytes,请使用标准 protobuf gRPC 代码生成。 -当前生成的 Python companion 面向同步 `grpcio` API。请使用普通 `def` servicer 方法、 -`grpc.server(...)`、标准 `grpc.Channel` 实例,并用 Python iterator/generator 处理 streaming RPC。 -生成的 stub 可以接收应用自行配置的任意 channel。Compiler 不会生成 `grpc.aio` stub 或 service -base,因此不要把生成 servicer 方法实现成 `async def`,除非你在生成 companion 外自行封装 adapter。 -基于 `grpc.aio` 的 Python gRPC async 支持将在下一个 Fory 版本提供。 +Python gRPC 生成默认使用 `grpc.aio` AsyncIO API。生成的 servicer base 使用 +`async def` 方法,生成的 stub 搭配 `grpc.aio.Channel` 实例使用,streaming RPC 使用 +async iterable。同步 `grpcio` companion 仍可通过 `--grpc-python-mode=sync` 生成。 -## 添加依赖 +## 安装依赖 + +将 `grpcio` 与 `pyfory` 一起安装。生成的 companion 会 import `grpc`,并且在默认模式下 +import `grpc.aio`;但 `pyfory` 不会把 gRPC 作为硬依赖。 ```bash pip install pyfory grpcio ``` -Fory Python package 不会把 gRPC 作为硬依赖;只有编译或运行生成 gRPC companion 的应用需要安装 -`grpcio`。 - ## 定义 Service +Service 定义可以来自 Fory IDL、protobuf IDL 或 FlatBuffers `rpc_service` 定义。 +Fory IDL service 示例: + ```protobuf package demo.greeter; @@ -59,88 +60,96 @@ service Greeter { } ``` -生成 Python model 和 gRPC companion: +使用 `--grpc` 生成 Python model 和 gRPC companion 代码: ```bash foryc service.fdl --python_out=./generated/python --grpc ``` -该 schema 会生成: +对这个 schema,Python generator 会输出: -| 文件 | 用途 | -| ---------------------- | --------------------------------------- | -| `demo_greeter.py` | Fory dataclass 和注册辅助逻辑 | -| `demo_greeter_grpc.py` | `grpcio` stub、servicer base 和注册函数 | +| 文件 | 用途 | +| ---------------------- | ----------------------------------------- | +| `demo_greeter.py` | Fory dataclass 和注册辅助逻辑 | +| `demo_greeter_grpc.py` | `grpc.aio` stub、servicer base 和注册函数 | -Module 名称来自 Fory package,点号会替换成下划线;没有 package 的 schema 使用 `generated.py` 和 -`generated_grpc.py`。 +Module 名称来自 Fory package,点号会替换为下划线。没有 package 的 schema 使用 +`generated.py` 和 `generated_grpc.py`。 -## 实现 Server +## 实现 Async Server + +继承生成的 servicer,并将它注册到 `grpc.aio` server。生成的 Python 方法名使用 +snake_case,而 gRPC wire path 保留原始 IDL method 名称。 ```python -from concurrent import futures +import asyncio -import grpc +import grpc.aio import demo_greeter import demo_greeter_grpc class Greeter(demo_greeter_grpc.GreeterServicer): - def say_hello(self, request, context): + async def say_hello(self, request, context): return demo_greeter.HelloReply(reply=f"Hello, {request.name}") -def serve(): - server = grpc.server(futures.ThreadPoolExecutor(max_workers=8)) +async def serve(): + server = grpc.aio.server() demo_greeter_grpc.add_servicer(Greeter(), server) server.add_insecure_port("[::]:50051") - server.start() - server.wait_for_termination() + await server.start() + await server.wait_for_termination() if __name__ == "__main__": - serve() + asyncio.run(serve()) ``` -## 创建 Client +生成的 request 和 response 类型由生成 companion 序列化,因此 service 实现不需要手动 +执行 Fory 注册。 + +## 创建 Async Client -使用生成的 stub 和普通 `grpcio` channel。生产 client 通常传入配置了 TLS/认证的 channel: +通过 `grpc.aio` channel 使用生成的 stub。生产 client 通常传入配置了 TLS/认证的 channel: ```python +import asyncio + import grpc +import grpc.aio import demo_greeter import demo_greeter_grpc -def main(): +async def main(): credentials = grpc.ssl_channel_credentials() - with grpc.secure_channel("api.example.com:443", credentials) as channel: + async with grpc.aio.secure_channel("api.example.com:443", credentials) as channel: stub = demo_greeter_grpc.GreeterStub(channel) - reply = stub.say_hello(demo_greeter.HelloRequest(name="Fory")) + reply = await stub.say_hello(demo_greeter.HelloRequest(name="Fory")) print(reply.reply) if __name__ == "__main__": - main() + asyncio.run(main()) ``` 本地测试和开发可以显式使用 insecure channel: ```python -# 仅用于本地测试和开发。 -# 生产环境请使用配置了 TLS/认证的 grpc.Channel。 -with grpc.insecure_channel("localhost:50051") as channel: +# Test-only channel. Use a TLS/auth-configured grpc.aio.Channel in production. +async with grpc.aio.insecure_channel("localhost:50051") as channel: stub = demo_greeter_grpc.GreeterStub(channel) ``` -Channel、credential、deadline、metadata、interceptor、retry 和 server lifecycle 都保持 `grpcio` -行为。 +Channel option、credential、deadline、metadata、retry 和 interceptor 仍由 `grpcio` 负责。 ## Streaming RPC -Fory service 可以使用 unary、server-streaming、client-streaming 和 bidirectional streaming: +Fory service 定义可以使用 unary、server-streaming、client-streaming 和 bidirectional +streaming RPC 形态: ```protobuf service Greeter { @@ -151,72 +160,127 @@ service Greeter { } ``` -生成 Python companion 遵循 `grpcio` 的 iterator/generator 约定: +默认 Python gRPC 输出遵循 `grpc.aio` 约定: -| IDL shape | Servicer 方法形态 | Stub 方法形态 | -| ----------------------------------------- | ----------------------------------------- | ---------------------- | -| `rpc A (Req) returns (Res)` | 返回一个 response 对象 | 返回一个 response 对象 | -| `rpc A (Req) returns (stream Res)` | yield 多个 response 对象 | 返回 response iterator | -| `rpc A (stream Req) returns (Res)` | 消费 request iterator 并返回一个 response | 接收 request iterator | -| `rpc A (stream Req) returns (stream Res)` | 消费 request iterator 并 yield response | 接收并返回 iterator | +| IDL shape | Servicer 方法形态 | Stub 方法形态 | +| ----------------------------------------- | ---------------------------------------------- | ------------------------------ | +| `rpc A (Req) returns (Res)` | `async def` 返回一个 response 对象 | awaitable 返回一个 response 对象 | +| `rpc A (Req) returns (stream Res)` | `async def` yield response 对象 | 返回 response async iterator | +| `rpc A (stream Req) returns (Res)` | 消费 async iterator 并返回 response | 接收 request async iterator | +| `rpc A (stream Req) returns (stream Res)` | 消费并 yield async iterator | 接收并返回 async iterator | -Servicer 方法使用 snake_case 名称;生成 descriptor 会保留 IDL 中的 service 和 method 名称作为 -gRPC path。每个 message frame 都通过 Fory serializer/deserializer 编码。 +Servicer 方法使用 snake_case 名称;生成 descriptor 会保留精确的 IDL service 和 +method 名称作为 gRPC path。 -Server 可以直接使用 Python iterator: +Server 实现使用 async 方法和 async iteration: ```python class Greeter(demo_greeter_grpc.GreeterServicer): - def lots_of_replies(self, request, context): + async def lots_of_replies(self, request, context): yield demo_greeter.HelloReply(reply=f"Hello, {request.name}") yield demo_greeter.HelloReply(reply=f"Welcome, {request.name}") - def lots_of_greetings(self, request_iterator, context): - names = [request.name for request in request_iterator] + async def lots_of_greetings(self, request_iterator, context): + names = [] + async for request in request_iterator: + names.append(request.name) return demo_greeter.HelloReply(reply=", ".join(names)) - def chat(self, request_iterator, context): - for request in request_iterator: + async def chat(self, request_iterator, context): + async for request in request_iterator: yield demo_greeter.HelloReply(reply=f"Hello, {request.name}") ``` -生成的 client 使用标准 `grpcio` streaming 调用形态: +生成的 client 使用 `grpc.aio` streaming 调用形态: ```python credentials = grpc.ssl_channel_credentials() -with grpc.secure_channel("api.example.com:443", credentials) as channel: +async with grpc.aio.secure_channel("api.example.com:443", credentials) as channel: stub = demo_greeter_grpc.GreeterStub(channel) - for reply in stub.lots_of_replies( + async for reply in stub.lots_of_replies( demo_greeter.HelloRequest(name="Fory") ): print(reply.reply) - def greeting_requests(): + async def greeting_requests(): yield demo_greeter.HelloRequest(name="Ada") yield demo_greeter.HelloRequest(name="Grace") - summary = stub.lots_of_greetings(greeting_requests()) + summary = await stub.lots_of_greetings(greeting_requests()) print(summary.reply) - def chat_requests(): + async def chat_requests(): yield demo_greeter.HelloRequest(name="Fory") yield demo_greeter.HelloRequest(name="RPC") - for reply in stub.chat(chat_requests()): + async for reply in stub.chat(chat_requests()): print(reply.reply) ``` -## Service 行为 +## Sync 模式 + +已有同步 `grpcio` 应用,或不运行 asyncio event loop 的环境,可以使用 sync 模式。显式生成 +sync companion: + +```bash +foryc service.fdl --python_out=./generated/python --grpc --grpc-python-mode=sync +``` + +Sync 模式输出相同的 `<module>_grpc.py` 文件名和 public name,但 servicer 方法使用普通 +`def`,应用使用 `grpc.server(...)` 和标准 `grpc.Channel` 实例。 + +Unary sync server 示例: + +```python +from concurrent import futures + +import grpc + +import demo_greeter +import demo_greeter_grpc + + +class Greeter(demo_greeter_grpc.GreeterServicer): + def say_hello(self, request, context): + return demo_greeter.HelloReply(reply=f"Hello, {request.name}") + + +server = grpc.server(futures.ThreadPoolExecutor(max_workers=8)) +demo_greeter_grpc.add_servicer(Greeter(), server) +server.add_insecure_port("[::]:50051") +server.start() +server.wait_for_termination() +``` + +Unary sync client 示例: + +```python +import grpc + +import demo_greeter +import demo_greeter_grpc + + +with grpc.insecure_channel("localhost:50051") as channel: + stub = demo_greeter_grpc.GreeterStub(channel) + reply = stub.say_hello(demo_greeter.HelloRequest(name="Fory")) + print(reply.reply) +``` + +Sync streaming 遵循普通 `grpcio` iterator 和 generator 约定。 + +## gRPC 运行时行为 -生成的 service companion 只提供 Fory serialization callback。Service 行为仍遵循标准 `grpcio`: +生成的 service companion 只提供 Fory serialization callback。运行行为仍遵循标准 +`grpcio`: - Deadline 和取消 - TLS 和认证 credential - Client/server interceptor - Status code、details 和 metadata -- Channel/server 生命周期 -- 同步 server 的线程池大小 +- 默认模式下的 async event loop、channel 和 server 生命周期 +- Sync 模式下同步 server 的线程池大小 ## 故障排查 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/basic-serialization.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/basic-serialization.md index 76bfe93fa6..c9f65dda28 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/basic-serialization.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/basic-serialization.md @@ -143,7 +143,7 @@ let later = timestamp.checked_add_duration(duration)?; ```toml [dependencies] -fory = { version = "1.2.0", features = ["chrono"] } +fory = { version = "1.3.0", features = ["chrono"] } ``` ### 自定义类型 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/configuration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/configuration.md index 254846b0d9..e0a8d06a7e 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/configuration.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/configuration.md @@ -74,6 +74,24 @@ let fory = Fory::default().max_dyn_depth(10); // 允许最多 10 层 注意:静态数据类型(非动态类型)本质上是安全的,不受深度限制约束,因为它们的结构在编译时就已知。 +### 远端 Schema Metadata 限制 + +兼容模式可能接收用于 Schema 演进的远端 metadata。以下限制用于约束 metadata 大小和可接受的 schema 版本数: + +```rust +let fory = Fory::builder() + .max_type_fields(512) + .max_type_meta_bytes(4096) + .max_schema_versions_per_type(10) + .max_average_schema_versions_per_type(3) + .build(); +``` + +- `max_type_fields` 默认值为 `512`,限制一个收到的 struct metadata body 中的字段数。 +- `max_type_meta_bytes` 默认值为 `4096`,限制一个收到的 TypeDef 或 TypeMeta body 的编码 body 字节数,不包含 8 字节 header 和扩展 size varint。 +- `max_schema_versions_per_type` 默认值为 `10`,限制一个逻辑类型可接受的远端 metadata 版本数。 +- `max_average_schema_versions_per_type` 默认值为 `3`,限制所有已接受远端类型的平均版本数;有效全局下限为 `8192` 个 schema。 + ### 跨语言模式 启用跨语言序列化: @@ -112,11 +130,22 @@ let fory = Fory::default() ## 配置摘要 -| 选项 | 描述 | 默认值 | -| -------------------- | ---------------------- | ------- | -| `compatible(bool)` | 启用 schema 演化 | `false` | -| `xlang(bool)` | 启用跨语言模式 | `false` | -| `max_dyn_depth(u32)` | 动态类型的最大嵌套深度 | `5` | +| 选项 | 描述 | 默认值 | +| --------------------------------------------- | --------------------------------- | ------- | +| `compatible(bool)` | 启用 schema 演化 | `false` | +| `xlang(bool)` | 启用跨语言模式 | `false` | +| `max_dyn_depth(u32)` | 动态类型的最大嵌套深度 | `5` | +| `max_type_fields(usize)` | 一个收到的 struct metadata body 最大字段数 | `512` | +| `max_type_meta_bytes(usize)` | 一个收到的 metadata body 最大编码字节数 | `4096` | +| `max_schema_versions_per_type(usize)` | 一个逻辑类型最大远端 metadata 版本数 | `10` | +| `max_average_schema_versions_per_type(usize)` | 所有远端类型的平均 metadata 版本数 | `3` | + +## 安全建议 + +- 反序列化不可信 payload 前,先注册应用 struct 和 trait-object 实现。 +- 使用 `max_dyn_depth(...)` 拒绝异常深的动态对象图。 +- 除非数据不是恶意输入,且可信 peer 会发送更大的 metadata 或大量 schema 版本,否则保持远端 schema metadata 限制的默认值。 +- 对不可信输入,优先使用具体类型字段,避免宽泛的 `dyn Any` 或 trait-object 字段。 ## 相关主题 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/grpc-support.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/grpc-support.md index de234fbd4a..f48fd2fa11 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/grpc-support.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/grpc-support.md @@ -34,7 +34,7 @@ streaming response 或 request stream,可添加 `tokio-stream`。 ```toml [dependencies] -fory = "1.2.0" +fory = "1.3.0" bytes = "1" tonic = { version = "0.14", features = ["transport"] } tokio = { version = "1", features = ["macros", "rt-multi-thread"] } @@ -283,7 +283,7 @@ while let Some(reply) = chat.message().await? { Rust gRPC payload 必须满足 `Send + 'static`,这样 tonic 才能在线程间移动 request/response。 如果 request 或 response schema 使用非线程安全的引用元信息,Rust gRPC 生成会拒绝该 service。 -## Service 行为 +## gRPC 运行时行为 生成的 service companion 只提供 Fory 序列化和 tonic binding。deadline、取消、TLS、认证、 Tower middleware、interceptor、status code、metadata、channel/server 生命周期和 backpressure diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/index.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/index.md index d6349d2e66..4b05b6f92d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/index.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/rust/index.md @@ -47,7 +47,7 @@ Rust 实现提供灵活而高性能的序列化能力,具备自动内存管理 ```toml [dependencies] -fory = "1.2.0" +fory = "1.3.0" ``` ### 基础示例 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/scala/configuration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/scala/configuration.md index 54141e8b04..59edf6cf95 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/scala/configuration.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/scala/configuration.md @@ -160,6 +160,8 @@ Scala 使用 Java 运行时配置表面。生产环境以及任何不受信任 val fory = ForyScala.builder() .requireClassRegistration(true) .withMaxDepth(50) + .withMaxTypeFields(512) + .withMaxTypeMetaBytes(4096) .build() ``` @@ -167,4 +169,5 @@ val fory = ForyScala.builder() - 保持 `requireClassRegistration(true)`,并注册应用类或生成的 modules。 - 使用 `withMaxDepth(...)` 拒绝异常深的对象图。 +- 除非数据不是恶意输入,且可信 peer 会发送更大的 metadata 或大量 schema 版本,否则保持 `withMaxTypeFields(...)`、`withMaxTypeMetaBytes(...)` 以及远端 schema-version 限制的默认值。 - Allow-listing 和 unknown-class 控制请遵循 [Java 配置](../java/configuration.md#forybuilder-选项)。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/scala/index.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/scala/index.md index f04891a0d4..e6bc455afb 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/scala/index.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/scala/index.md @@ -48,7 +48,7 @@ Fory Scala 继承了 Fory Java 的全部特性,并增加了 Scala 特定优化 使用 sbt 添加依赖: ```sbt -libraryDependencies += "org.apache.fory" %% "fory-scala" % "1.2.0" +libraryDependencies += "org.apache.fory" %% "fory-scala" % "1.3.0" ``` ## 快速开始 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/swift/configuration.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/swift/configuration.md index 108e2785f0..31b76aae32 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/swift/configuration.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/swift/configuration.md @@ -30,6 +30,12 @@ public struct ForyConfig { public var xlang: Bool public var trackRef: Bool public var compatible: Bool + public let checkClassVersion: Bool + public let maxDepth: Int + public let maxTypeFields: Int + public let maxTypeMetaBytes: Int + public let maxSchemaVersionsPerType: Int + public let maxAverageSchemaVersionsPerType: Int } ``` @@ -78,6 +84,25 @@ let fory = Fory(xlang: true, trackRef: true) let fory = Fory(xlang: true, trackRef: false, compatible: true) ``` +### Size 和 Depth 限制 + +`maxDepth` 限制解码 payload 的嵌套深度。兼容模式下的远端 metadata 也会被限制: + +- `maxTypeFields` 默认值为 `512`,限制一个收到的 struct metadata body 中的字段数。 +- `maxTypeMetaBytes` 默认值为 `4096`,限制一个收到的 TypeMeta body 的编码 body 字节数,不包含 8 字节 header 和扩展 size varint。 +- `maxSchemaVersionsPerType` 默认值为 `10`,限制一个逻辑类型可接受的远端 metadata 版本数。 +- `maxAverageSchemaVersionsPerType` 默认值为 `3`,限制所有已接受远端类型的平均版本数;有效全局下限为 `8192` 个 schema。 + +```swift +let fory = Fory( + maxDepth: 5, + maxTypeFields: 512, + maxTypeMetaBytes: 4096, + maxSchemaVersionsPerType: 10, + maxAverageSchemaVersionsPerType: 3 +) +``` + ## 推荐配置 ### 本地严格 Schema @@ -97,3 +122,12 @@ let fory = Fory(xlang: true, trackRef: false, compatible: true) ```swift let fory = Fory(xlang: true, trackRef: true, compatible: true) ``` + +## 安全 + +安全相关配置: + +- 在反序列化不可信 payload 前,只注册预期的生成 model。 +- 对 intentional same-schema payload,将 `checkClassVersion` 与 `compatible: false` 配合使用。 +- 根据服务接受的最大嵌套深度设置 `maxDepth`。 +- 除非数据不是恶意输入,且可信 peer 会发送更大的 metadata 或大量 schema 版本,否则保持远端 schema metadata 限制的默认值。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/xlang/getting-started.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/xlang/getting-started.md index f2b9a89e77..55a6900400 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/xlang/getting-started.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/xlang/getting-started.md @@ -31,14 +31,14 @@ license: | <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-core</artifactId> - <version>1.2.0</version> + <version>1.3.0</version> </dependency> ``` **Gradle:** ```gradle -implementation 'org.apache.fory:fory-core:1.2.0' +implementation 'org.apache.fory:fory-core:1.3.0' ``` ### Python @@ -57,7 +57,7 @@ go get github.com/apache/fory/go/fory ```toml [dependencies] -fory = "1.2.0" +fory = "1.3.0" ``` ### JavaScript/TypeScript @@ -75,13 +75,13 @@ npm install @apache-fory/core @apache-fory/hps ### C\# ```bash -dotnet add package Apache.Fory --version 1.2.0 +dotnet add package Apache.Fory --version 1.3.0 ``` ### Dart ```bash -dart pub add fory:^1.2.0 +dart pub add fory:^1.3.0 dart pub add dev:build_runner ``` @@ -91,20 +91,20 @@ dart pub add dev:build_runner ```swift dependencies: [ - .package(url: "https://github.com/apache/fory.git", exact: "1.2.0") + .package(url: "https://github.com/apache/fory.git", exact: "1.3.0") ] ``` ### Scala ```scala -libraryDependencies += "org.apache.fory" %% "fory-scala" % "1.2.0" +libraryDependencies += "org.apache.fory" %% "fory-scala" % "1.3.0" ``` ### Kotlin ```kotlin -implementation("org.apache.fory:fory-kotlin:1.2.0") +implementation("org.apache.fory:fory-kotlin:1.3.0") ``` ### C++ diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/xlang/serialization.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/xlang/serialization.md index f5bb2ecfae..14cc126d8a 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/xlang/serialization.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/guide/xlang/serialization.md @@ -21,6 +21,33 @@ license: | 本页演示常见的跨语言序列化模式。当各端使用匹配的类型标识、字段 schema 和兼容性设置时,在一种受支持语言中序列化的数据可以在任何其他受支持语言中反序列化。 +## 远端 Schema Metadata 限制 + +兼容模式可能为 reader 尚未知的类型接收远端 metadata(`TypeDef` 或 `TypeMeta`)。Fory 会限制可接受的不同远端 metadata 版本数量,并限制每个收到的 metadata body 大小: + +- `maxSchemaVersionsPerType`:一个逻辑类型可接受的最大远端 metadata 版本数,默认值为 `10`。 +- `maxAverageSchemaVersionsPerType`:所有已接受远端类型的平均 metadata 版本数,默认值为 `3`;有效全局下限为 `8192` 个 metadata entry。 +- `maxTypeFields`:一个收到的 struct metadata body 可声明的最大字段数,默认值为 `512`。 +- `maxTypeMetaBytes`:一个收到的 TypeDef 或 TypeMeta body 的最大编码 metadata body 字节数,不包含 8 字节 header 和扩展 size varint,默认值为 `4096`。 + +这些限制是资源保护。它们不会改变编码格式、注册要求、动态类型加载、unknown-type handling 或 Schema 演进兼容性。 + +仅当数据不是恶意输入,且可信 peer 会发送更大的 metadata 或大量 schema 版本时,才调高这些值。 + +| 语言 | 字段数选项 | Metadata 字节选项 | 单类型版本选项 | 平均版本选项 | +| --------------------- | ------------------- | --------------------- | ------------------------------ | ------------------------------------- | +| Java | `withMaxTypeFields` | `withMaxTypeMetaBytes` | `withMaxSchemaVersionsPerType` | `withMaxAverageSchemaVersionsPerType` | +| Scala | `withMaxTypeFields` | `withMaxTypeMetaBytes` | `withMaxSchemaVersionsPerType` | `withMaxAverageSchemaVersionsPerType` | +| Kotlin | `withMaxTypeFields` | `withMaxTypeMetaBytes` | `withMaxSchemaVersionsPerType` | `withMaxAverageSchemaVersionsPerType` | +| Python | `max_type_fields` | `max_type_meta_bytes` | `max_schema_versions_per_type` | `max_average_schema_versions_per_type` | +| JavaScript/TypeScript | `maxTypeFields` | `maxTypeMetaBytes` | `maxSchemaVersionsPerType` | `maxAverageSchemaVersionsPerType` | +| C++ | `max_type_fields` | `max_type_meta_bytes` | `max_schema_versions_per_type` | `max_average_schema_versions_per_type` | +| Go | `WithMaxTypeFields` | `WithMaxTypeMetaBytes` | `WithMaxSchemaVersionsPerType` | `WithMaxAverageSchemaVersionsPerType` | +| Rust | `max_type_fields` | `max_type_meta_bytes` | `max_schema_versions_per_type` | `max_average_schema_versions_per_type` | +| C# | `MaxTypeFields` | `MaxTypeMetaBytes` | `MaxSchemaVersionsPerType` | `MaxAverageSchemaVersionsPerType` | +| Swift | `maxTypeFields` | `maxTypeMetaBytes` | `maxSchemaVersionsPerType` | `maxAverageSchemaVersionsPerType` | +| Dart | `maxTypeFields` | `maxTypeMetaBytes` | `maxSchemaVersionsPerType` | `maxAverageSchemaVersionsPerType` | + ## 序列化内置类型 常见类型可以自动序列化,无需注册:原始数值类型、字符串、二进制、数组、列表、映射等。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/java_serialization_spec.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/java_serialization_spec.md index 8bf9b6ec15..e1e3a9796b 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/java_serialization_spec.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/java_serialization_spec.md @@ -210,6 +210,10 @@ class_layer: | namespace | type_name | field_infos | ``` +Reader 可以拒绝超过运行时资源限制的 TypeDef,例如 metadata body 字节数上限或一个 +TypeDef 中的最大字段数。这些限制是接收侧资源控制,不改变 TypeDef 编码格式、类型标识、 +动态类加载、unknown-class handling、注册策略或 Schema 演进语义。 + ### Field info 每个字段: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_implementation_guide.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_implementation_guide.md index 641a6ebed0..67051522c1 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_implementation_guide.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_implementation_guide.md @@ -312,13 +312,28 @@ struct 专属的 schema/version 包装与 compatible-field staging - 运行时会在 struct 载荷前写入 schema hash - 读取侧会在读字段之前检查该 hash +## 远端 Metadata 资源限制 + +实现应在 cold metadata parse path 上限制收到的 metadata body 和 struct 字段列表。 +`maxTypeMetaBytes` 限制一个 TypeDef 或 TypeMeta 的编码 body,不包含 8 字节 header 和扩展 +size varint,并且应在复制或解压 body 前检查。`maxTypeFields` 限制一个收到的 struct metadata +body 声明的字段数,并应在预留或分配字段列表前检查。 + +这些限制是运行时资源控制;它们不会改变编码格式、类型标识、动态加载、unknown-type 行为、 +反序列化策略或 Schema 演进语义。Metadata cache hit 和生成的字段 reader 仍是 hot path,不应为这些限制增加额外工作。 + +远端 schema-version 限制属于同一个 cold metadata owner path。Header cache hit 必须跳过剩余 +metadata body,并返回已缓存 metadata;不要重新执行 schema-limit 检查、hash 复验、分配或 policy 工作。Header miss 时,应在同一个具体 owner path 中完成处理:证明并读取 metadata body bytes,按 header 校验 body,校验字段数,通过现有注册与反序列化策略检查解析类型,必要时按原始编码字节比较精确本地 metadata,对非本地远端 metadata 检查 schema-version 限制,构建所需 read state,发布到持久 metadata cache,最后记录 schema count。失败或不兼容的 metadata 不得发布到持久 cache,也不得消耗 schema-version count。 + +编码字节与本地已注册 metadata 精确匹配的远端 metadata,在完成选择本地类型所需的现有类型与反序列化策略检查后,可以使用本地 metadata,且不消耗远端 schema-version 限制。这个 exact-local bypass 不只适用于 struct;当 named enum、ext 和 union metadata 带有 metadata body 且与本地编码字节匹配时,同样适用。纯 id-based enum、ext 和 typed-union value 不携带 TypeDef 或 TypeMeta body,必须继续走普通 type-id 加 user-type-id 路径。兼容 named enum、ext 和 union metadata 通常只有一个版本,但当它作为 shared metadata 发送且不精确匹配本地 metadata 时,仍会计入已接受远端 metadata 总数。`maxTypeFields` 只适用于 struct 字段列表。 + ## Meta string 与共享类型元信息 xlang 类型元信息由两类显式状态支撑: - `MetaStringWriter` 和 `MetaStringReader` 负责去重与解码命名空间和类型名字串 -- 共享 TypeDef 的读写状态负责跟踪已声明的 compatible struct 元数据 +- 共享 TypeDef 的读写状态负责跟踪已声明的 TypeDef 元数据 所有权规则: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_serialization_spec.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_serialization_spec.md index 4e2bfdcc74..99d177cd12 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_serialization_spec.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_serialization_spec.md @@ -203,6 +203,13 @@ TypeDef 头部包含: - 每层字段数量与类型标识 - 字段级元信息(名称编码/tag、nullable/ref、field type) +Reader 可以拒绝超过运行时资源限制的 TypeDef,例如 metadata body 字节数上限或一个 struct +TypeDef 中的最大字段数。这些限制是接收侧资源控制,不改变 TypeDef 编码格式、类型标识、 +动态加载、unknown-type handling、注册策略或 Schema 演进语义。 + +纯 id-based enum、ext 和 typed-union value 不携带 TypeDef body。接收侧 TypeDef 资源限制只在 +stream 实际携带 shared TypeDef metadata 时适用。 + ## Meta String meta string 用于 namespace、typename、fieldname 的压缩表示。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/install.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/install.md index 5c43f4d64b..509dcf52db 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/install.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/install.md @@ -16,14 +16,14 @@ Apache Fory™ 同时提供源码发布物和各语言对应的软件包。 <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-core</artifactId> - <version>1.2.0</version> + <version>1.3.0</version> </dependency> <!-- 可选的 row format 支持 --> <!-- <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-format</artifactId> - <version>1.2.0</version> + <version>1.3.0</version> </dependency> --> <!-- 用于数组压缩的 SIMD 加速(Java 16+) --> @@ -31,7 +31,7 @@ Apache Fory™ 同时提供源码发布物和各语言对应的软件包。 <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-simd</artifactId> - <version>1.2.0</version> + <version>1.3.0</version> </dependency> --> ``` @@ -44,7 +44,7 @@ Scala 2.13 的 Maven 依赖: <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-scala_2.13</artifactId> - <version>1.2.0</version> + <version>1.3.0</version> </dependency> ``` @@ -54,20 +54,20 @@ Scala 3 的 Maven 依赖: <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-scala_3</artifactId> - <version>1.2.0</version> + <version>1.3.0</version> </dependency> ``` Scala 2.13 的 sbt 依赖: ```sbt -libraryDependencies += "org.apache.fory" % "fory-scala_2.13" % "1.2.0" +libraryDependencies += "org.apache.fory" % "fory-scala_2.13" % "1.3.0" ``` Scala 3 的 sbt 依赖: ```sbt -libraryDependencies += "org.apache.fory" % "fory-scala_3" % "1.2.0" +libraryDependencies += "org.apache.fory" % "fory-scala_3" % "1.3.0" ``` ## Kotlin @@ -78,7 +78,7 @@ libraryDependencies += "org.apache.fory" % "fory-scala_3" % "1.2.0" <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-kotlin</artifactId> - <version>1.2.0</version> + <version>1.3.0</version> </dependency> ``` @@ -86,7 +86,7 @@ libraryDependencies += "org.apache.fory" % "fory-scala_3" % "1.2.0" ```bash python -m pip install --upgrade pip -pip install pyfory==1.2.0 +pip install pyfory==1.3.0 ``` ## Go @@ -94,7 +94,7 @@ pip install pyfory==1.2.0 请使用完整的 Go 模块路径 `github.com/apache/fory/go/fory`: ```bash -go get github.com/apache/fory/go/[email protected] +go get github.com/apache/fory/go/[email protected] ``` 如果你的 Go proxy 还没有同步新的子模块 tag,请稍后重试,或者临时使用 `GOPROXY=direct`。 @@ -103,13 +103,13 @@ go get github.com/apache/fory/go/[email protected] ```toml [dependencies] -fory = "1.2.0" +fory = "1.3.0" ``` 或者使用 `cargo add`: ```bash -cargo add [email protected] +cargo add [email protected] ``` ## JavaScript / TypeScript @@ -132,7 +132,7 @@ npm install @apache-fory/hps ```yaml dependencies: - fory: ^1.2.0 + fory: ^1.3.0 dev_dependencies: build_runner: ^2.4.13 @@ -149,12 +149,12 @@ dart run build_runner build --delete-conflicting-outputs 安装 `Apache.Fory` NuGet 包。它同时包含运行时以及 `[ForyObject]` 类型所需的源代码生成器。 ```bash -dotnet add package Apache.Fory --version 1.2.0 +dotnet add package Apache.Fory --version 1.3.0 ``` ```xml <ItemGroup> - <PackageReference Include="Apache.Fory" Version="1.2.0" /> + <PackageReference Include="Apache.Fory" Version="1.3.0" /> </ItemGroup> ``` @@ -164,7 +164,7 @@ dotnet add package Apache.Fory --version 1.2.0 ```swift dependencies: [ - .package(url: "https://github.com/apache/fory.git", exact: "1.2.0") + .package(url: "https://github.com/apache/fory.git", exact: "1.3.0") ], targets: [ .target( diff --git a/i18n/zh-CN/docusaurus-plugin-content-pages/download/index.md b/i18n/zh-CN/docusaurus-plugin-content-pages/download/index.md index 9cebfa3847..0257e242ae 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-pages/download/index.md +++ b/i18n/zh-CN/docusaurus-plugin-content-pages/download/index.md @@ -9,11 +9,11 @@ Apache Fory™ 的官方发布以源码制品形式提供。 ## 最新版本 -当前最新源码版本为 1.2.0: +当前最新源码版本为 1.3.0: | 版本 | 日期 | 源码 | 发布说明 | | ------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- | -| 1.2.0 | 2026-06-16 | [source](https://www.apache.org/dyn/closer.lua/fory/1.2.0/apache-fory-1.2.0-src.tar.gz?action=download) [asc](https://downloads.apache.org/fory/1.2.0/apache-fory-1.2.0-src.tar.gz.asc) [sha512](https://downloads.apache.org/fory/1.2.0/apache-fory-1.2.0-src.tar.gz.sha512) | [release notes](https://github.com/apache/fory/releases/tag/v1.2.0) | +| 1.3.0 | 2026-06-25 | [source](https://www.apache.org/dyn/closer.lua/fory/1.3.0/apache-fory-1.3.0-src.tar.gz?action=download) [asc](https://downloads.apache.org/fory/1.3.0/apache-fory-1.3.0-src.tar.gz.asc) [sha512](https://downloads.apache.org/fory/1.3.0/apache-fory-1.3.0-src.tar.gz.sha512) | [release notes](https://github.com/apache/fory/releases/tag/v1.3.0) | ## 所有归档版本 @@ -31,13 +31,13 @@ Fory 为下载站点上的所有文件提供 SHA 摘要和 PGP 签名文件。 要验证 SHA 摘要,你需要 `.tar.gz` 文件及其对应的 `.tar.gz.sha512` 文件。示例命令如下: ```bash -sha512sum --check apache-fory-1.2.0-src.tar.gz.sha512 +sha512sum --check apache-fory-1.3.0-src.tar.gz.sha512 ``` 输出类似下面这样即表示校验通过: ```bash -apache-fory-1.2.0-src.tar.gz: OK +apache-fory-1.3.0-src.tar.gz: OK ``` ### 校验签名 @@ -53,7 +53,7 @@ gpg --import KEYS 之后可以校验签名: ```bash -gpg --verify apache-fory-1.2.0-src.tar.gz.asc apache-fory-1.2.0-src.tar.gz +gpg --verify apache-fory-1.3.0-src.tar.gz.asc apache-fory-1.3.0-src.tar.gz ``` 如果出现如下输出,即表示签名正确: --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
