diff --git a/content/en/blog/releases/Kitex/release-v0_16_2.md b/content/en/blog/releases/Kitex/release-v0_16_2.md new file mode 100644 index 00000000000..1b8f2b5ecf2 --- /dev/null +++ b/content/en/blog/releases/Kitex/release-v0_16_2.md @@ -0,0 +1,158 @@ +--- +title: "Kitex Release v0.16.2" +linkTitle: "Release v0.16.2" +projects: ["Kitex"] +date: 2026-05-08 +description: > +--- + +> These release notes consolidate the changes from v0.16.0, v0.16.1, and v0.16.2. The externally announced version is v0.16.2. + +## **Introduction to Key Changes** + +### **Announcements** +1. **netpollmux is no longer maintained**: The `pkg/remote/trans/netpollmux` package and the corresponding `client.WithMuxConnection` / `server.WithMuxTransport` options are all marked as deprecated. netpollmux is no longer maintained. For details, see [Connection Multiplexing](/docs/kitex/tutorials/basic-feature/connection_type#connection-multiplexing). +2. **Adjustments to some interfaces**: No impact on regular users, but may affect those with extensions or special dependencies. For details, see the [**Special Changes**] section. + +### **New Features** +1. **Binary Generic Call: Per-Request IDL Service Name, Greatly Reducing the Number of Generic Clients to Maintain** + + In previous versions, binary generic clients were bound to an IDL Service, requiring maintenance of a large number of clients. This version supports dynamically specifying the IDL Service Name per call, via the newly added `callopt.WithBinaryGenericIDLService(svcName)` / `streamcall.WithBinaryGenericIDLService(svcName)` call options, and the per-call configuration takes precedence over the configuration set at client initialization. Both Ping-Pong and streaming calls are supported. For details, see [Per-Call IDL Service Name](/docs/kitex/tutorials/advanced-feature/generic-call/basic_usage#per-call-idl-service-name). + +2. **New In-Process LocalCaller: Supports Unary Local Invocation** + + Added `server.LocalCaller` and `server.NewLocalCaller(caller string, svr Server) (LocalCaller, error)`, which can directly invoke unary methods registered on the Server within the same process. It reuses the Server's Middleware and Tracer, keeping behavior consistent with remote invocations. Streaming methods and generic services that rely on `ServiceInfo.GenericMethod` are rejected. The legacy `server.InvokeCaller` is deprecated. + +3. **Recv Timeout Control for Streaming** + + Added `streaming.TimeoutConfig` for fine-grained Recv timeout control on streaming APIs. A dedicated timeout can be configured, and the new `DisableCancelRemote` flag controls whether the remote stream is cascaded-cancelled after timeout. Two configuration entry points are provided: + - Per-client: `client.WithStreamRecvTimeoutConfig(streaming.TimeoutConfig)` + - Per-call: `streamcall.WithRecvTimeoutConfig(streaming.TimeoutConfig)` + + When a Recv times out, a new error code `codes.RecvDeadlineExceeded` (value `17`) is returned together with the sentinel error `kerrors.ErrStreamingTimeout`, making timeout classification easier. + +4. **Fine-Grained Streaming Event Tracing - StreamEventHandler** + + A new event-callback mechanism, independent of the Tracer, can observe the core events of the streaming protocol layer (Stream start, Recv, Send, Recv Header, Stream finish), making it easy to build custom fine-grained streaming monitoring: + - Client: `client.WithStreamEventHandler(rpcinfo.ClientStreamEventHandler)` + - Server: `server.WithStreamEventHandler(rpcinfo.ServerStreamEventHandler)` + + New event types `stats.StreamStart`, `stats.StreamRecvHeader`, and `stats.StreamFinish` are also added. For details, see [StreamX Detailed Stream Event Tracing](/docs/kitex/tutorials/basic-feature/streamx/streamx_event_handler). + +### **Feature/Experience Optimization** +1. **Kitex gRPC: Memory Optimization and Connection Leak Fixes** + - **HTTP/2 Write Buffer Reuse and Framer-Level Pooling**: Supports per-connection pooling and reuse of write buffers, reducing memory usage for idle connections, suitable for scenarios where the service needs to directly handle a large number of gRPC connections. Use `client.WithGRPCReuseWriteBuffer` / `server.WithGRPCReuseWriteBuffer` to enable it, and further enable framer-level pooling via `ReuseWriteBufferConfig.EnableReuseHTTP2FramerBuffer`. + - **Client-Side Cancel Object Allocation Optimization**: Reduces object allocations for unified cancel on the gRPC client side, avoiding excessive allocations in gateway scenarios where cancel operations are frequent. + - **Connection Pool Leak Fix**: Fixed an issue where gRPC connections were not properly recycled after being closed with no subsequent calls. + +2. **TTHeader Streaming: Memory Optimization** + + The sender now directly reuses the underlying TCP flow control, avoiding OOM. + +3. **Service Discovery: Instance Change Event Allocation Optimization** + + Reduces object allocations caused by frequent downstream instance changes, lowering GC pressure. Also added `event.GetDefaultEventNum()` / `event.SetDefaultEventNum(num int)` for tuning the default event queue capacity (default 200). + +4. **RPCInfo Field Inlining** + + Added `rpcinfo.NewRPCInfoWithInlineFields() RPCInfo`, which returns an RPCInfo with inlined sub-objects, reducing per-request pool gets and allocations. Server hot paths and LocalCaller both adopt this inlining approach. + +5. **Load Balance: Consistent-Hash Switched to maphash** + + The consistent-hash key/node hashing in `consistBalancer` replaces `github.com/bytedance/gopkg/util/xxhash3` with `hash/maphash`. Note that `hash/maphash` uses a per-process random seed, so hash values differ across client replicas and restarts. + +### **Bug Fixes** +1. **Streaming-Related Fixes** + - **Recv/Send Panic on the Streaming Server Side**: Streaming RPCs no longer reuse `rpcinfo`, completely avoiding concurrent read/write panics on `rpcinfo` caused by the handler exiting early while the Stream is still being used asynchronously. + - **Legacy Server Middleware Extension Not Taking Effect**: Fixed an issue where the extended Stream object in legacy Server Middleware was unexpectedly discarded. + +2. **Other Fixes** + - **rpcTimeout Ticker Leak Fix** (extremely rare): Closed the ticker in the `rpcTimeout` pool to prevent resource leaks. Most online scenarios are unaffected; this issue is only noticeable in scenarios with extremely low QPS and short processing time for the API. + - **Panic Caused by Writing Elements of Different Types into Container Fields in Generic Calls** (very rare): Writing elements of different types into the same container field could cause a panic. For example, if the field itself is `[]uint8`, the first input may be `uint8` while the second input may be `string`. After the fix, the writer is resolved per element instead of caching the first element's writer, and a type mismatch now returns an error. + +### **Special Changes - May Affect a Small Number of Services** +> Mainly Breaking Changes and API deprecations. No impact on the vast majority of users; users with special dependencies should pay attention. + +#### Breaking Changes + +1. **`pkg/remote/trans/ttstream/container` Path Migration** (#1952) + + Moved to `pkg/remote/trans/ttstream/internal/container`. The package was not intended as a public API; copy the needed implementations into your own module if you depended on them. + +2. **`grpc.NewClientTransport` Callback Timing Change** (#1945) + + The `onClose` / `onGoAway` callbacks now fire after the transport transitions to closing/draining and after `http2Client.mu` is released. Callers relying on the old ordering should migrate to `grpc.NewClientTransportWithConfig`. + +#### Deprecations +> APIs are only marked as deprecated in this version; they still work, but please migrate to the new APIs at your earliest convenience. + +1. **netpollmux Fully Deprecated** (#1933) + - `client.WithMuxConnection(connNum int)` is deprecated + - `server.WithMuxTransport()` is deprecated + - The `pkg/remote/trans/netpollmux` package itself is marked as deprecated and no longer maintained + + See [Connection Multiplexing](/docs/kitex/tutorials/basic-feature/connection_type#connection-multiplexing) for the rationale. + +2. **`server.InvokeCaller` Deprecated** (#1930) + + Use the newly added `server.LocalCaller` instead. + +3. **`grpc.NewClientTransport` Deprecated** (#1945) + + Use `grpc.NewClientTransportWithConfig(ctx, conn, opts, grpc.ClientConfig)` instead. The new entry's `OnClose` / `OnGoAway` callbacks receive context and transport parameters. + +4. **`kerrors.ErrRPCFinish` Restored as Deprecated Symbol** (#1953) + + v0.16.2 restores this API as a deprecated symbol for backward compatibility with pre-v0.15 code. + +5. **Streaming-Related API Deprecations** + - `client.WithStreamRecvTimeout` is deprecated; use `client.WithStreamRecvTimeoutConfig` instead (#1911) + - `streamcall.WithRecvTimeout` is deprecated; use `streamcall.WithRecvTimeoutConfig` instead (#1911) + - `rpcinfo.TraceController.GetStreamEventHandler()` is deprecated; use `TraceController.Handle*` methods instead (#1905) + - `internal/stream.StreamEventHandler` is deprecated; use `rpcinfo.ClientStreamEventHandler` / `rpcinfo.ServerStreamEventHandler` instead (#1905) + +## **Full Change** + +### Feature +* feat(generic): support specifying IDL Service Name per call for Binary Generic by @DMwangnima in [#1928](https://github.com/cloudwego/kitex/pull/1928) +* feat(server): add in-process LocalCaller for unary calls by @xiaost in [#1930](https://github.com/cloudwego/kitex/pull/1930) +* feat: gRPC supports reusing write buffer for each connection by @DMwangnima in [#1918](https://github.com/cloudwego/kitex/pull/1918) +* feat(streaming): add Recv timeout config and adjust gRPC error/log by @DMwangnima in [#1911](https://github.com/cloudwego/kitex/pull/1911) +* feat(streaming): support detailed tracing events by @DMwangnima in [#1905](https://github.com/cloudwego/kitex/pull/1905) +* feat(streaming): remove rpcinfo reuse for streaming by @DMwangnima in [#1909](https://github.com/cloudwego/kitex/pull/1909) + +### Fix +* fix(ttstream): ttstream should not recycle connection when Recv timeout with DisableCancelRemote=true by @DMwangnima in [#1952](https://github.com/cloudwego/kitex/pull/1952) +* fix(gRPC): connection pool leak when connection is closed and there are no more subsequent calls by @DMwangnima in [#1945](https://github.com/cloudwego/kitex/pull/1945) +* fix(codec): frugalAvailable for void func result structs by @xiaost in [#1938](https://github.com/cloudwego/kitex/pull/1938) +* fix(timeout): close ticker in rpctimeout pool to prevent resource leak by @DMwangnima in [#1931](https://github.com/cloudwego/kitex/pull/1931) +* fix(streaming): server-side old Stream wrapped in server MW should not be discarded by @DMwangnima in [#1929](https://github.com/cloudwego/kitex/pull/1929) +* fix(generic): panic when generic writing different elem types of container by @DMwangnima in [#1926](https://github.com/cloudwego/kitex/pull/1926) +* fix: remove streaming rpcstats Reset by @DMwangnima in [#1922](https://github.com/cloudwego/kitex/pull/1922) +* fix(ttstream): add server-side information in ttstream errBizHandlerReturnCancel exception by @DMwangnima in [#1921](https://github.com/cloudwego/kitex/pull/1921) + +### Optimize +* perf(gRPC): reduce object allocations on the gRPC client side for unified cancel scenarios by @DMwangnima in [#1950](https://github.com/cloudwego/kitex/pull/1950) +* optimize(gRPC): support pooling HTTP2 framer write buffer to reduce idle connection memory by @DMwangnima in [#1944](https://github.com/cloudwego/kitex/pull/1944) +* perf(server): use inline RPCInfo fields in LocalCaller by @xiaost in [#1940](https://github.com/cloudwego/kitex/pull/1940) +* optimize(discovery): reduce object allocation in discovery event queue and support changing default capacity of queue by @DMwangnima in [#1939](https://github.com/cloudwego/kitex/pull/1939) +* perf: rpcinfo inline fields by @xiaost in [#1935](https://github.com/cloudwego/kitex/pull/1935) +* optimize: remove ttstream connection write goroutine to avoid Sender OOM by @DMwangnima in [#1917](https://github.com/cloudwego/kitex/pull/1917) + +### Refactor +* refactor: use maphash instead of xxhash3 by @xiaost in [#1924](https://github.com/cloudwego/kitex/pull/1924) + +### Chore +* chore: update dependencies and add ErrRPCFinish back for compatibility by @DMwangnima in [#1953](https://github.com/cloudwego/kitex/pull/1953) +* chore(queue): remove unused field tailVersion of Queue by @DMwangnima in [#1947](https://github.com/cloudwego/kitex/pull/1947) +* chore: improve bug report issue template by @xiaost in [#1941](https://github.com/cloudwego/kitex/pull/1941) +* chore(codec): log data type before errDecodeMismatchMsgType by @xiaost in [#1937](https://github.com/cloudwego/kitex/pull/1937) +* chore(mux): deprecate thrift mux transport by @DMwangnima in [#1933](https://github.com/cloudwego/kitex/pull/1933) +* chore: change tests workflow on go 1.21-1.26 by @GuangmingLuo in [#1923](https://github.com/cloudwego/kitex/pull/1923) +* chore: update dependencies by @DMwangnima in [#1912](https://github.com/cloudwego/kitex/pull/1912) +* chore: release version v0.16.2 by @DMwangnima in [#1954](https://github.com/cloudwego/kitex/pull/1954) +* chore: release version v0.16.1 by @DMwangnima in [#1919](https://github.com/cloudwego/kitex/pull/1919) +* chore: release version v0.16.0 by @DMwangnima in [#1913](https://github.com/cloudwego/kitex/pull/1913) + +### Docs +* docs: add changelog by @xiaost in [#1946](https://github.com/cloudwego/kitex/pull/1946) diff --git a/content/en/docs/kitex/Tutorials/advanced-feature/generic-call/basic_usage.md b/content/en/docs/kitex/Tutorials/advanced-feature/generic-call/basic_usage.md index d254a42086a..9237307a855 100644 --- a/content/en/docs/kitex/Tutorials/advanced-feature/generic-call/basic_usage.md +++ b/content/en/docs/kitex/Tutorials/advanced-feature/generic-call/basic_usage.md @@ -121,6 +121,20 @@ resp := &HelloEchoResult{} _, err = frugal.DecodeObject(res.([]byte), resp) ``` +###### Per-Call IDL Service Name + +To dynamically specify the IDL service name to access per call, use the following configuration: + +```go +import ( + "github.com/cloudwego/kitex/client/callopt" +) + +result, err:= genericCli.GenericCall(ctx, methodName, buf, + callopt.WithBinaryGenericIDLService(svcName), +) +``` + ##### Streaming Generic Call Kitex streaming interface's binary payload is the value after raw request/response serialization, without Args/Results struct encapsulation, which differs from the unary interface. The streaming interface provides three streaming call modes. For detailed usage, see: [StreamX Basic Stream Programming](../../basic-feature/streamx/). @@ -145,6 +159,20 @@ resp := &Response{} _, err = frugal.DecodeObject(rbuf.([]byte), resp) ``` +###### Per-Call IDL Service Name + +To dynamically specify the IDL service name to access per call, use the following configuration: + +```go +import ( + "github.com/cloudwego/kitex/client/callopt/streamcall" +) + +stream, err := genericCli.BidirectionalStreaming(ctx, methodName, + streamcall.WithBinaryGenericIDLService(svcName), +) +``` + #### Server Usage Since the thrift binary generic V2 interface must强制 specify the idl service name, and the idl service name must be carried by header protocols such as ttheader/grpc header/ttstream header, traffic from protocols like framed/buffered cannot carry idl service name and may not hit these specified idl service names, which could cause server processing errors. diff --git a/content/en/docs/kitex/Tutorials/basic-feature/streamx/StreamX_Event_Handler.md b/content/en/docs/kitex/Tutorials/basic-feature/streamx/StreamX_Event_Handler.md new file mode 100644 index 00000000000..d6e932c06a8 --- /dev/null +++ b/content/en/docs/kitex/Tutorials/basic-feature/streamx/StreamX_Event_Handler.md @@ -0,0 +1,144 @@ +--- +title: "StreamX Detailed Stream Event Tracing" +linkTitle: "Detailed Stream Event Tracing" +weight: 6 +date: 2026-05-08 +keywords: ["Stream Monitoring", "StreamEventHandler", "Detailed Stream Tracing"] +description: "Kitex StreamX detailed stream event tracing via StreamEventHandler, allowing observation of core stream protocol events for fine-grained streaming monitoring." +--- + +## Background + +To make fine-grained monitoring of streaming APIs easier and gain control over the **full lifecycle** of a stream at the protocol layer, Kitex provides a new fine-grained streaming monitoring extension mechanism **StreamEventHandler** starting from **v0.16.0**. It supports observing core events at the streaming protocol layer (Stream start, Recv, Send, Recv Header, Stream finish). + +Compared to the previous `StreamEventReporter` which could only observe Send/Recv events, StreamEventHandler offers more complete lifecycle events, making it easier to implement monitoring, telemetry, and meta-info parsing extensions. + +## What is StreamEventHandler? + +`StreamEventHandler` is a set of event callbacks provided from both Client and Server perspectives. Business code can register callbacks on events of interest to complete monitoring, telemetry, and meta-info parsing at the streaming RPC protocol layer. + +### Supported Events + +| Event | Description | Client | Server | +| --- | --- | --- | --- | +| `StreamStartEvent` | Stream start (Client: writing Header Frame; Server: receiving and parsing Header Frame) | ✅ | ✅ | +| `StreamRecvHeaderEvent` | Header Frame received from peer | ✅ | ❌ | +| `StreamRecvEvent` | Each `Stream.Recv` | ✅ | ✅ | +| `StreamSendEvent` | Each `Stream.Send` | ✅ | ✅ | +| `StreamFinishEvent` | Stream finish | ✅ | ✅ | + +### Handler Type Definitions + +Defined in `github.com/cloudwego/kitex/pkg/rpcinfo`: + +```go +// Client side +type ClientStreamEventHandler struct { + HandleStreamStartEvent func(ctx context.Context, ri RPCInfo, evt StreamStartEvent) + HandleStreamRecvHeaderEvent func(ctx context.Context, ri RPCInfo, evt StreamRecvHeaderEvent) + HandleStreamRecvEvent func(ctx context.Context, ri RPCInfo, evt StreamRecvEvent) + HandleStreamSendEvent func(ctx context.Context, ri RPCInfo, evt StreamSendEvent) + HandleStreamFinishEvent func(ctx context.Context, ri RPCInfo, evt StreamFinishEvent) +} + +// Server side (no RecvHeader) +type ServerStreamEventHandler struct { + HandleStreamStartEvent func(ctx context.Context, ri RPCInfo, evt StreamStartEvent) + HandleStreamRecvEvent func(ctx context.Context, ri RPCInfo, evt StreamRecvEvent) + HandleStreamSendEvent func(ctx context.Context, ri RPCInfo, evt StreamSendEvent) + HandleStreamFinishEvent func(ctx context.Context, ri RPCInfo, evt StreamFinishEvent) +} +``` + +You **only need to set the event fields you care about**; leave the rest as `nil` and the framework will skip unregistered events automatically. + +## Usage + +### Server Side + +1. Implement `ServerStreamEventHandler` with only the events you care about: + +```go +import ( + "context" + + "github.com/cloudwego/kitex/pkg/rpcinfo" +) + +var serverHandler = rpcinfo.ServerStreamEventHandler{ + HandleStreamStartEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamStartEvent) { + // Called when a stream is established + }, + HandleStreamRecvEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamRecvEvent) { + // Called on every Recv + }, + HandleStreamSendEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamSendEvent) { + // Called on every Send + }, + HandleStreamFinishEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamFinishEvent) { + // Called when the stream ends + }, +} +``` + +2. Inject it via `server.WithStreamEventHandler`: + +```go +import ( + "github.com/cloudwego/kitex/server" +) + +svr := testservice.NewServer(hdl, + server.WithStreamOptions(server.WithStreamEventHandler(serverHandler)), +) +err := svr.Run() +``` + +### Client Side + +1. Implement `ClientStreamEventHandler`: + +```go +import ( + "context" + + "github.com/cloudwego/kitex/pkg/rpcinfo" +) + +var clientHandler = rpcinfo.ClientStreamEventHandler{ + HandleStreamStartEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamStartEvent) { + // Called when a stream is established + }, + HandleStreamRecvHeaderEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamRecvHeaderEvent) { + // Header Frame received from peer + }, + HandleStreamRecvEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamRecvEvent) { + // Called on every Recv + }, + HandleStreamSendEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamSendEvent) { + // Called on every Send + }, + HandleStreamFinishEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamFinishEvent) { + // Called when the stream ends + }, +} +``` + +2. Inject it via `client.WithStreamEventHandler`: + +```go +import ( + "github.com/cloudwego/kitex/client" +) + +// streamx Client +cli, err := testservice.NewClient("service-name", + client.WithStreamOptions(client.WithStreamEventHandler(clientHandler)), +) +``` + +> Note: when using the gRPC protocol, remember to specify it via `client.WithTransportProtocol(transport.GRPC)`. + +## Relationship with Legacy `StreamEventReporter` + +The legacy `StreamEventReporter` is still compatible (no changes required to existing code), but it can only observe Send / Recv events. To observe more complete lifecycle events such as Start / Finish / RecvHeader, please use StreamEventHandler. diff --git a/content/zh/blog/releases/Kitex/release-v0_16_2.md b/content/zh/blog/releases/Kitex/release-v0_16_2.md new file mode 100644 index 00000000000..55791e9ba4d --- /dev/null +++ b/content/zh/blog/releases/Kitex/release-v0_16_2.md @@ -0,0 +1,190 @@ +--- +title: "Kitex Release v0.16.2" +linkTitle: "Release v0.16.2" +projects: ["Kitex"] +date: 2026-05-08 +description: > +--- + +> 本次 release notes 汇总了 v0.16.0、v0.16.1、v0.16.2 三个版本的变更,对外发布版本为 v0.16.2。 + +## **重要变更介绍** + +### **公告** +1. **netpollmux 不再维护**:`pkg/remote/trans/netpollmux` 包以及对应的 `client.WithMuxConnection` / `server.WithMuxTransport` 选项整体标记为废弃,netpollmux 模块不再维护。详见 [连接多路复用](/zh/docs/kitex/tutorials/basic-feature/connection_type#连接多路复用)。 +2. **部分接口调整**:对普通使用的用户无影响,但如果有扩展或特殊依赖会有影响,详见 [**特殊变更**] 部分。 + +### **新特性** +1. **二进制泛化调用:请求粒度指定 IDL Service Name,大幅减少维护的泛化 Client 数量** + + 旧版二进制泛化 Client 与 IDL Service 绑定,需要维护大量 Client。本版本支持请求时动态指定 IDL Service Name,通过新增的 `callopt.WithBinaryGenericIDLService(svcName)` / `streamcall.WithBinaryGenericIDLService(svcName)` 调用选项指定,且调用维度的配置优先级更高。Ping-Pong 和流式调用均支持,详见 [动态指定 IDL Service Name](/zh/docs/kitex/tutorials/advanced-feature/generic-call/basic_usage#动态指定-idl-service-name)。 + +2. **新增进程内 LocalCaller:支持 unary 本地调用** + + 新增 `server.LocalCaller` 与 `server.NewLocalCaller(caller string, svr Server) (LocalCaller, error)`,可在进程内直接调用 Server 注册的 unary 方法。复用 Server 的 Middleware、Tracer,行为与远程调用一致;流式方法和依赖 `ServiceInfo.GenericMethod` 的 generic service 会被拒绝。旧的 `server.InvokeCaller` 标记为废弃。 + +3. **流式接口 Recv 超时控制** + + 新增 `streaming.TimeoutConfig`,支持对流式接口 Recv 进行细粒度超时控制,可单独配置超时时间,并通过 `DisableCancelRemote` 控制超时后是否级联取消远端流。提供两种配置入口: + - Client 维度:`client.WithStreamRecvTimeoutConfig(streaming.TimeoutConfig)` + - 单次调用维度:`streamcall.WithRecvTimeoutConfig(streaming.TimeoutConfig)` + + Recv 超时返回新错误码 `codes.RecvDeadlineExceeded`(值为 `17`)以及哨兵错误 `kerrors.ErrStreamingTimeout`,便于区分超时类型。 + +4. **流式细粒度事件追踪 - StreamEventHandler** + + 新增独立于 Tracer 的流式事件回调机制,可以感知流式协议层面的核心 Event(Stream 开始、Recv、Send、Recv Header、Stream 结束),便于定制细粒度的流式监控: + - Client:`client.WithStreamEventHandler(rpcinfo.ClientStreamEventHandler)` + - Server:`server.WithStreamEventHandler(rpcinfo.ServerStreamEventHandler)` + + 同时新增 `stats.StreamStart`、`stats.StreamRecvHeader`、`stats.StreamFinish` 事件类型。详见 [StreamX 流式细粒度事件追踪](/zh/docs/kitex/tutorials/basic-feature/streamx/streamx_event_handler)。 + +### **功能/体验优化** +1. **Kitex gRPC:内存优化与连接泄漏修复** + - **HTTP/2 write buffer 复用与 framer 池化**:支持连接维度的 write buffer 池化复用,降低空闲连接内存占用,适用于直接承接大量 gRPC 连接的场景。通过 `client.WithGRPCReuseWriteBuffer` / `server.WithGRPCReuseWriteBuffer` 启用,并在 `ReuseWriteBufferConfig.EnableReuseHTTP2FramerBuffer` 中进一步启用 framer 层的池化。 + - **Client 侧 cancel 对象分配优化**:减少 gRPC client 在统一 cancel 场景下的对象分配,避免网关等需要频繁 cancel 的场景分配大量对象。 + - **连接池泄漏修复**:修复连接关闭且无后续调用时 gRPC 连接未正确回收的问题。 + +2. **TTHeader Streaming:内存优化** + + 发送端直接复用底层 TCP 的流控能力,避免 OOM。 + +3. **服务发现:实例变更事件对象分配优化** + + 减少因下游实例变化频繁而导致的实例变更事件对象分配,降低 GC 压力。同时新增 `event.GetDefaultEventNum()` / `event.SetDefaultEventNum(num int)`,支持调整默认事件队列容量(默认 200)。 + +4. **RPCInfo 字段内联** + + 新增 `rpcinfo.NewRPCInfoWithInlineFields() RPCInfo`,返回带内联子对象的 RPCInfo,减少每次请求的 pool get 与对象分配;server 热点路径与 LocalCaller 均采用该内联方式。 + +5. **loadbalance:consistent-hash 使用 maphash** + + `consistBalancer` 的 consistent-hash key/node 哈希将 `github.com/bytedance/gopkg/util/xxhash3` 替换为 `hash/maphash`。注意 `hash/maphash` 在每个进程使用随机种子,hash 值会在不同 client 副本和重启之间不同。 + +### **问题修复** +1. **流式相关问题修复** + - **流式 Server 侧 Recv/Send panic**:流式 RPC 不再复用 rpcinfo,彻底避免因 handler 提前退出、但仍在异步使用 Stream 导致的 rpcinfo 并发读写 panic。 + - **流式旧 Server Middleware 扩展不生效**:修复旧 Server Middleware 中扩展的 Stream 对象被意外丢弃的问题。 + +2. **其他问题修复** + - **rpcTimeout ticker 泄漏修复**(极小概率):关闭 rpcTimeout pool 中的 ticker,防止资源泄漏。线上绝大部分场景无问题,只在 QPS 极低且接口处理时间较短的场景会感知到。 + - **泛化调用容器字段写入不同类型元素 panic**(小概率):当某个容器字段被写入了不同类型的元素时会 panic(例如字段本身是 `[]uint8`,第一个传入 `uint8`,第二个却传入 `string`)。修复后按元素逐个解析 writer 而非缓存第一个 writer,并对类型不匹配返回错误。 + +### **特殊变更 - 少数服务可能会有影响** +> 主要为 Breaking Changes 与接口废弃,对绝大部分用户无影响,请有特殊依赖的用户关注。 + +#### Breaking Changes + +1. **`pkg/remote/trans/ttstream/container` 路径迁移**(#1952) + + 迁移至 `pkg/remote/trans/ttstream/internal/container`。该包本就不是对外 API,外部如有依赖请将所需实现复制到自己的 module 中。 + +2. **`grpc.NewClientTransport` 回调时序变化**(#1945) + + `onClose` / `onGoAway` 回调改为在 transport 状态转换为 closing/draining 并释放 `http2Client.mu` 之后触发。依赖旧时序的调用方请迁移至 `grpc.NewClientTransportWithConfig`。 + +#### 接口废弃 +> 当前版本仅标记废弃,仍保留可用,请及时迁移到新接口。 + +1. **netpollmux 整体废弃**(#1933) + - `client.WithMuxConnection(connNum int)` 废弃 + - `server.WithMuxTransport()` 废弃 + - `pkg/remote/trans/netpollmux` 包整体标记为废弃,不再维护 + + 废弃原因详见 [连接多路复用](/zh/docs/kitex/tutorials/basic-feature/connection_type#连接多路复用)。 + +2. **`server.InvokeCaller` 废弃**(#1930) + + 使用本版本新增的 `server.LocalCaller` 替代。 + +3. **`grpc.NewClientTransport` 废弃**(#1945) + + 使用 `grpc.NewClientTransportWithConfig(ctx, conn, opts, grpc.ClientConfig)` 替代。新入口的 `OnClose` / `OnGoAway` 回调接收 context 与 transport 参数。 + +4. **`kerrors.ErrRPCFinish` 恢复为废弃符号**(#1953) + + v0.16.2 重新加回该 API 作为废弃符号,便于 pre-v0.15 代码继续编译。 + +5. **流式相关接口废弃** + - `client.WithStreamRecvTimeout` 废弃,使用 `client.WithStreamRecvTimeoutConfig` 替代(#1911) + - `streamcall.WithRecvTimeout` 废弃,使用 `streamcall.WithRecvTimeoutConfig` 替代(#1911) + - `rpcinfo.TraceController.GetStreamEventHandler()` 废弃,使用 `TraceController.Handle*` 方法替代(#1905) + - `internal/stream.StreamEventHandler` 类型废弃,使用 `rpcinfo.ClientStreamEventHandler` / `rpcinfo.ServerStreamEventHandler` 替代(#1905) + +## **详细变更** + +### Feature +* feat(generic): support specifying IDL Service Name per call for Binary Generic by @DMwangnima in [#1928](https://github.com/cloudwego/kitex/pull/1928) +> 特性:二进制泛化调用支持请求粒度指定 IDL Service Name +* feat(server): add in-process LocalCaller for unary calls by @xiaost in [#1930](https://github.com/cloudwego/kitex/pull/1930) +> 特性:新增进程内 LocalCaller,支持 unary 本地调用 +* feat: gRPC supports reusing write buffer for each connection by @DMwangnima in [#1918](https://github.com/cloudwego/kitex/pull/1918) +> 特性:gRPC 支持连接维度的 write buffer 复用,降低空闲连接内存占用 +* feat(streaming): add Recv timeout config and adjust gRPC error/log by @DMwangnima in [#1911](https://github.com/cloudwego/kitex/pull/1911) +> 特性:流式 Recv 超时配置,并调整 gRPC 错误信息与日志级别 +* feat(streaming): support detailed tracing events by @DMwangnima in [#1905](https://github.com/cloudwego/kitex/pull/1905) +> 特性:流式调用支持细粒度事件追踪(StreamEventHandler) +* feat(streaming): remove rpcinfo reuse for streaming by @DMwangnima in [#1909](https://github.com/cloudwego/kitex/pull/1909) +> 特性:流式 RPC 不再复用 rpcinfo + +### Fix +* fix(ttstream): ttstream should not recycle connection when Recv timeout with DisableCancelRemote=true by @DMwangnima in [#1952](https://github.com/cloudwego/kitex/pull/1952) +> 修复:开启 `DisableCancelRemote=true` 时,Recv 超时后不再回收长连接,避免丢包 +* fix(gRPC): connection pool leak when connection is closed and there are no more subsequent calls by @DMwangnima in [#1945](https://github.com/cloudwego/kitex/pull/1945) +> 修复:连接关闭且无后续调用时 gRPC 连接池泄漏的问题 +* fix(codec): frugalAvailable for void func result structs by @xiaost in [#1938](https://github.com/cloudwego/kitex/pull/1938) +> 修复:void 函数 result 结构体的 `frugalAvailable` 判断 +* fix(timeout): close ticker in rpctimeout pool to prevent resource leak by @DMwangnima in [#1931](https://github.com/cloudwego/kitex/pull/1931) +> 修复:关闭 rpctimeout pool 中的 ticker,避免资源泄漏 +* fix(streaming): server-side old Stream wrapped in server MW should not be discarded by @DMwangnima in [#1929](https://github.com/cloudwego/kitex/pull/1929) +> 修复:Server 侧旧 Stream 被 Server Middleware 包装后不应被丢弃 +* fix(generic): panic when generic writing different elem types of container by @DMwangnima in [#1926](https://github.com/cloudwego/kitex/pull/1926) +> 修复:泛化调用容器字段写入不同类型元素导致的 panic +* fix: remove streaming rpcstats Reset by @DMwangnima in [#1922](https://github.com/cloudwego/kitex/pull/1922) +> 修复:移除流式 `rpcStats.Reset()` +* fix(ttstream): add server-side information in ttstream errBizHandlerReturnCancel exception by @DMwangnima in [#1921](https://github.com/cloudwego/kitex/pull/1921) +> 修复:ttstream 在 `errBizHandlerReturnCancel` 异常中补充服务端信息 + +### Optimize +* perf(gRPC): reduce object allocations on the gRPC client side for unified cancel scenarios by @DMwangnima in [#1950](https://github.com/cloudwego/kitex/pull/1950) +> 优化:减少 gRPC client 统一 cancel 场景下的对象分配 +* optimize(gRPC): support pooling HTTP2 framer write buffer to reduce idle connection memory by @DMwangnima in [#1944](https://github.com/cloudwego/kitex/pull/1944) +> 优化:支持 HTTP/2 framer write buffer 池化,降低空闲连接内存占用 +* perf(server): use inline RPCInfo fields in LocalCaller by @xiaost in [#1940](https://github.com/cloudwego/kitex/pull/1940) +> 优化:LocalCaller 使用内联 RPCInfo 字段 +* optimize(discovery): reduce object allocation in discovery event queue and support changing default capacity of queue by @DMwangnima in [#1939](https://github.com/cloudwego/kitex/pull/1939) +> 优化:减少服务发现事件队列对象分配,并支持修改队列默认容量 +* perf: rpcinfo inline fields by @xiaost in [#1935](https://github.com/cloudwego/kitex/pull/1935) +> 优化:内联 rpcinfo 字段 +* optimize: remove ttstream connection write goroutine to avoid Sender OOM by @DMwangnima in [#1917](https://github.com/cloudwego/kitex/pull/1917) +> 优化:移除 ttstream 连接独立 write goroutine,避免 Sender OOM + +### Refactor +* refactor: use maphash instead of xxhash3 by @xiaost in [#1924](https://github.com/cloudwego/kitex/pull/1924) +> 重构:consistent-hash 使用 `hash/maphash` 替换 `xxhash3` + +### Chore +* chore: update dependencies and add ErrRPCFinish back for compatibility by @DMwangnima in [#1953](https://github.com/cloudwego/kitex/pull/1953) +> chore:更新依赖,并恢复 `ErrRPCFinish` 以保持兼容性 +* chore(queue): remove unused field tailVersion of Queue by @DMwangnima in [#1947](https://github.com/cloudwego/kitex/pull/1947) +> chore:移除 Queue 中未使用的 tailVersion 字段 +* chore: improve bug report issue template by @xiaost in [#1941](https://github.com/cloudwego/kitex/pull/1941) +> chore:优化 bug report issue 模板 +* chore(codec): log data type before errDecodeMismatchMsgType by @xiaost in [#1937](https://github.com/cloudwego/kitex/pull/1937) +> chore:在 `errDecodeMismatchMsgType` 之前记录数据类型 +* chore(mux): deprecate thrift mux transport by @DMwangnima in [#1933](https://github.com/cloudwego/kitex/pull/1933) +> chore:标记 thrift mux transport 为废弃 +* chore: change tests workflow on go 1.21-1.26 by @GuangmingLuo in [#1923](https://github.com/cloudwego/kitex/pull/1923) +> chore:更新测试 workflow 至 Go 1.21-1.26 +* chore: update dependencies by @DMwangnima in [#1912](https://github.com/cloudwego/kitex/pull/1912) +> chore:更新依赖(sonic、dynamicgo、frugal) +* chore: release version v0.16.2 by @DMwangnima in [#1954](https://github.com/cloudwego/kitex/pull/1954) +> chore:发布 v0.16.2 版本 +* chore: release version v0.16.1 by @DMwangnima in [#1919](https://github.com/cloudwego/kitex/pull/1919) +> chore:发布 v0.16.1 版本 +* chore: release version v0.16.0 by @DMwangnima in [#1913](https://github.com/cloudwego/kitex/pull/1913) +> chore:发布 v0.16.0 版本 + +### Docs +* docs: add changelog by @xiaost in [#1946](https://github.com/cloudwego/kitex/pull/1946) +> docs:增加 changelog 文件 diff --git a/content/zh/docs/kitex/Tutorials/advanced-feature/generic-call/basic_usage.md b/content/zh/docs/kitex/Tutorials/advanced-feature/generic-call/basic_usage.md index 640a80f0388..f8d83d7d596 100644 --- a/content/zh/docs/kitex/Tutorials/advanced-feature/generic-call/basic_usage.md +++ b/content/zh/docs/kitex/Tutorials/advanced-feature/generic-call/basic_usage.md @@ -121,6 +121,20 @@ resp := &HelloEchoResult{} _, err = frugal.DecodeObject(res.([]byte), resp) ``` +###### 动态指定 IDL Service Name + +若需要动态指定每次调用所需要访问的 IDL service name,请使用以下配置: + +```go +import ( + "github.com/cloudwego/kitex/client/callopt" +) + +result, err:= genericCli.GenericCall(ctx, methodName, buf, + callopt.WithBinaryGenericIDLService(svcName), +) +``` + ##### 流式泛化调用 Kitex 流式接口的二进制 payload 即是原始 request/response 序列化后的值,不包含 Args/Results 结构体,这与 unary 接口存在区别。流式接口提供三种流调用模式,相关详细用法可见:[StreamX 基础流编程](../../basic-feature/streamx/)。 @@ -145,6 +159,20 @@ resp := &Response{} _, err = frugal.DecodeObject(rbuf.([]byte), resp) ``` +###### 动态指定 IDL Service Name + +若需要动态指定每次调用所需要访问的 IDL service name,请使用以下配置: + +```go +import ( + "github.com/cloudwego/kitex/client/callopt/streamcall" +) + +stream, err := genericCli.BidirectionalStreaming(ctx, methodName, + streamcall.WithBinaryGenericIDLService(svcName), +) +``` + #### 服务端使用 由于 thrift 二进制泛化 v2 接口必须强制指定 idl service name,而 idl service name 须由 ttheader/grpc header/ttstream header 等头部协议携带,framed/buffered 等协议无法携带 idl service name 的流量就无法命中这些指定的 idl service name,这可能导致 server 无法处理这些请求而报错。 diff --git a/content/zh/docs/kitex/Tutorials/basic-feature/streamx/StreamX_Event_Handler.md b/content/zh/docs/kitex/Tutorials/basic-feature/streamx/StreamX_Event_Handler.md new file mode 100644 index 00000000000..db3213e42a3 --- /dev/null +++ b/content/zh/docs/kitex/Tutorials/basic-feature/streamx/StreamX_Event_Handler.md @@ -0,0 +1,144 @@ +--- +title: "StreamX 流式细粒度事件追踪" +linkTitle: "流式细粒度事件追踪" +weight: 6 +date: 2026-05-08 +keywords: ["流式监控", "StreamEventHandler", "流式细粒度追踪"] +description: "Kitex StreamX 流式细粒度事件追踪机制 StreamEventHandler,支持感知流式协议层面的核心 Event,便于定制细粒度的流式监控。" +--- + +## 背景 + +为了更方便地对流式接口做**细粒度**监控,从协议层面掌控一个流的**全生命周期**,Kitex 从 **v0.16.0** 开始提供全新的流式细粒度监控扩展机制 **StreamEventHandler**,支持感知流式协议层面的核心 Event(Stream 开始、Recv、Send、Recv Header、Stream 结束)。 + +与之前只能感知 Send/Recv 的 `StreamEventReporter` 相比,StreamEventHandler 提供了更完整的生命周期事件,便于实现监控、打点、元信息解析等扩展能力。 + +## 什么是 StreamEventHandler? + +`StreamEventHandler` 是按 Client / Server 视角提供的一组事件回调,业务在感兴趣的事件上挂载回调函数,即可在流式 RPC 的协议层完成监控、打点、元信息解析等工作。 + +### 支持的 Event + +| Event | 含义 | Client | Server | +| --- | --- | --- | --- | +| `StreamStartEvent` | 建流(Client:写出 Header Frame;Server:接收并解析 Header Frame) | ✅ | ✅ | +| `StreamRecvHeaderEvent` | 收到对端 Header Frame | ✅ | ❌ | +| `StreamRecvEvent` | 每次 `Stream.Recv` | ✅ | ✅ | +| `StreamSendEvent` | 每次 `Stream.Send` | ✅ | ✅ | +| `StreamFinishEvent` | 流结束 | ✅ | ✅ | + +### Handler 类型定义 + +定义在 `github.com/cloudwego/kitex/pkg/rpcinfo` 包: + +```go +// Client 侧 +type ClientStreamEventHandler struct { + HandleStreamStartEvent func(ctx context.Context, ri RPCInfo, evt StreamStartEvent) + HandleStreamRecvHeaderEvent func(ctx context.Context, ri RPCInfo, evt StreamRecvHeaderEvent) + HandleStreamRecvEvent func(ctx context.Context, ri RPCInfo, evt StreamRecvEvent) + HandleStreamSendEvent func(ctx context.Context, ri RPCInfo, evt StreamSendEvent) + HandleStreamFinishEvent func(ctx context.Context, ri RPCInfo, evt StreamFinishEvent) +} + +// Server 侧(无 RecvHeader) +type ServerStreamEventHandler struct { + HandleStreamStartEvent func(ctx context.Context, ri RPCInfo, evt StreamStartEvent) + HandleStreamRecvEvent func(ctx context.Context, ri RPCInfo, evt StreamRecvEvent) + HandleStreamSendEvent func(ctx context.Context, ri RPCInfo, evt StreamSendEvent) + HandleStreamFinishEvent func(ctx context.Context, ri RPCInfo, evt StreamFinishEvent) +} +``` + +业务**只需要填关心的事件字段**,其余留 `nil`,框架会自动跳过未注册的事件。 + +## 使用方式 + +### Server 侧 + +1. 实现 `ServerStreamEventHandler`,只填关心的事件字段: + +```go +import ( + "context" + + "github.com/cloudwego/kitex/pkg/rpcinfo" +) + +var serverHandler = rpcinfo.ServerStreamEventHandler{ + HandleStreamStartEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamStartEvent) { + // 流建立时回调 + }, + HandleStreamRecvEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamRecvEvent) { + // 每次 Recv 回调 + }, + HandleStreamSendEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamSendEvent) { + // 每次 Send 回调 + }, + HandleStreamFinishEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamFinishEvent) { + // 流结束时回调 + }, +} +``` + +2. 通过 `server.WithStreamEventHandler` 注入: + +```go +import ( + "github.com/cloudwego/kitex/server" +) + +svr := testservice.NewServer(hdl, + server.WithStreamOptions(server.WithStreamEventHandler(serverHandler)), +) +err := svr.Run() +``` + +### Client 侧 + +1. 实现 `ClientStreamEventHandler`: + +```go +import ( + "context" + + "github.com/cloudwego/kitex/pkg/rpcinfo" +) + +var clientHandler = rpcinfo.ClientStreamEventHandler{ + HandleStreamStartEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamStartEvent) { + // 流建立时回调 + }, + HandleStreamRecvHeaderEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamRecvHeaderEvent) { + // 收到对端 Header Frame + }, + HandleStreamRecvEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamRecvEvent) { + // 每次 Recv 回调 + }, + HandleStreamSendEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamSendEvent) { + // 每次 Send 回调 + }, + HandleStreamFinishEvent: func(ctx context.Context, ri rpcinfo.RPCInfo, evt rpcinfo.StreamFinishEvent) { + // 流结束时回调 + }, +} +``` + +2. 通过 `client.WithStreamEventHandler` 注入: + +```go +import ( + "github.com/cloudwego/kitex/client" +) + +// streamx Client +cli, err := testservice.NewClient("service-name", + client.WithStreamOptions(client.WithStreamEventHandler(clientHandler)), +) +``` + +> 注:使用 gRPC 协议时,记得通过 `client.WithTransportProtocol(transport.GRPC)` 指定。 + +## 与旧机制 `StreamEventReporter` 的关系 + +旧的 `StreamEventReporter` 仍然兼容(不需要改动旧代码),但只能感知到 Send / Recv 两类事件。若需要感知 Start / Finish / RecvHeader 等更完整生命周期事件,请使用 StreamEventHandler。