What's New in the Qt GRPC library in 6.11
6.11中Qt GRPC库的新增功能
Apr 8, 2026 byDennis Oberst
2026年4月8日丹尼斯上校
Qt 6.11 brings a set of meaningful improvements to the Qt GRPC library, focusing on stability, safety, performance, and new capabilities that make building gRPC™ based applications in Qt more powerful and productive.
Qt 6.11为Qt GRPC库带来了一系列有意义的改进,重点是稳定性、安全性、性能和新功能,使在Qt中构建基于GRPC™的应用程序更加强大和高效。
Stability, Safety, and Performance
稳定性、安全性和性能
Much of the work in this release happened beneath the surface. We significantly improved the internal testing infrastructure for Qt GRPC, which allowed us to exercise a much broader range of edge cases, including protocol corner cases and transport-level behavior that was previously difficult to cover. As a direct result of this expanded test coverage, numerous subtle issues were detected and resolved. Many of these fixes have been backported to older branches, including Qt 6.8, so users on long-term-supported versions benefit as well.
本次发布的大部分工作都发生在表面之下。我们显著改进了Qt GRPC的内部测试基础设施,这使我们能够处理更广泛的边缘情况,包括以前难以涵盖的协议角情况和传输级行为。作为扩大测试覆盖范围的直接结果,发现并解决了许多微妙的问题。其中许多修复程序已被移植到较旧的分支,包括Qt 6.8,因此长期支持版本的用户也会受益。
On the transport side, QGrpcHttp2Channel now supports reading compressed messages. gRPC allows message payloads to be compressed at the protocol level, and Qt GRPC clients can now handle servers that make use of this capability transparently.
在传输端,QGrpcHttp2Channel现在支持读取压缩消息。gRPC允许在协议级别压缩消息有效载荷,Qt gRPC客户端现在可以透明地处理使用此功能的服务器。
New API Additions
新增API
Two smaller but useful additions ship with the API release.
API版本附带了两个较小但有用的附加功能。
serverInitialMetadataReceived: A signal that fires as soon as the server initial metadata arrives, allowing early verification, UI feedback, or cancellation decisions before any response messages.
serverInitialMetadataReceived:服务器初始元数据到达后立即触发的信号,允许在任何响应消息之前进行早期验证、UI反馈或取消决策。
Metadata filtering: Available on both QGrpcChannelOptions and QGrpcCallOptions, this option controls whether protocol-related server metadata is included in the results. By default, it is disabled.
元数据过滤:在QGrpcChannelOptions和QGrpcCallOptions上都可用,此选项控制结果中是否包含与协议相关的服务器元数据。默认情况下,它是禁用的。
Interceptors
拦截器
Qt GRPC 6.11 introduces the client-side interceptor mechanism. Interceptors are a well-established pattern in other gRPC implementations and are now available natively in Qt GRPC.
Qt GRPC 6.11引入了客户端拦截器机制。拦截器在其他gRPC实现中是一种成熟的模式,现在可以在Qt gRPC中原生使用。
What Are Interceptors?
什么是拦截器?
Interceptors provide a composable way to implement behavior that should apply consistently across many RPCs, without modifying your application's call sites. They are the right tool for cross-cutting concerns such as logging, authentication, metrics collection, or policy enforcement.
拦截器提供了一种可组合的方式来实现应在许多RPC中一致应用的行为,而无需修改应用程序的调用站点。它们是跨领域问题的正确工具,如日志记录、身份验证、指标收集或策略执行。
Interceptors are installed on a channel via a QGrpcInterceptorChain and hook into specific stages of the RPC lifecycle. See the Qt GRPC Interceptors Overview for a general introduction and guidance on using interceptors.
拦截器通过QGrpcInterceptorChain安装在通道上,并挂接到RPC生命周期的特定阶段。有关使用拦截器的一般介绍和指导,请参阅Qt GRPC拦截器概述。
Adding Interceptors to the Chat Example
在聊天示例中添加拦截器
Below, we extend the Qt GRPC Chat example by introducing a simple LoggingInterceptor. This interceptor captures activity at every interception point and forwards structured log entries to a LogModel. The model is then exposed to QML and displayed using a ListView.
下面,我们通过引入一个简单的LoggingInterceptor来扩展Qt GRPC聊天示例。此拦截器捕获每个拦截点的活动,并将结构化日志条目转发到LogModel。然后将模型暴露给QML,并使用ListView显示。
The LoggingInterceptor reimplements all available interception hooks, allowing it to observe and log each stage of an RPC. From startup through message exchange to final completion. Its declaration is shown below:
LoggingInterceptor重新实现了所有可用的拦截钩子,使其能够观察和记录RPC的每个阶段。从启动到消息交换再到最终完成。其声明如下:
class LoggingInterceptor final : public QGrpcStartInterceptor,
public QGrpcInitialMetadataInterceptor,
public QGrpcMessageReceivedInterceptor,
public QGrpcWriteMessageInterceptor,
public QGrpcWritesDoneInterceptor,
public QGrpcTrailingMetadataInterceptor,
public QGrpcCancelInterceptor,
public QGrpcFinishedInterceptor
{
public:
LoggingInterceptor(std::shared_ptr<LogModel> logModel);
~LoggingInterceptor() override;
Continuation onStart(QGrpcInterceptionContext &context, QProtobufMessage &message,
QGrpcCallOptions &callOptions) override;
void onInitialMetadata(QGrpcInterceptionContext &context,
QMultiHash<QByteArray, QByteArray> &metadata) override;
void onMessageReceived(QGrpcInterceptionContext &context, QByteArray &messageData) override;
void onWriteMessage(QGrpcInterceptionContext &context, QProtobufMessage &message) override;
void onWritesDone(QGrpcInterceptionContext &context) override;
void onTrailingMetadata(QGrpcInterceptionContext &context,
QMultiHash<QByteArray, QByteArray> &metadata) override;
void onCancel(QGrpcInterceptionContext &context) override;
void onFinished(QGrpcInterceptionContext &context, QGrpcStatus &status) override;
private:
std::shared_ptr<LogModel> mLog;
using Clock = std::chrono::steady_clock;
using Ms = std::chrono::duration<double, std::milli>;
QHash<quint64, Clock::time_point> m_activeRPCs;
};
The implementation itself is fairly straightforward and focuses on logging the most relevant events at each interception point, while also measuring the total duration of each RPC from start to finish.
实现本身相当简单,侧重于记录每个拦截点最相关的事件,同时测量每个RPC从开始到结束的总持续时间。
#include "logginginterceptor.h"
#include <QtGrpc/QGrpcStatus>
using namespace Qt::Literals::StringLiterals;
LoggingInterceptor::LoggingInterceptor(std::shared_ptr<LogModel> logModel)
: mLog(std::move(logModel)) {
Q_ASSERT(mLog);
}
LoggingInterceptor::~LoggingInterceptor() = default;
LoggingInterceptor::Continuation LoggingInterceptor::onStart(QGrpcInterceptionContext &context,
QProtobufMessage &, QGrpcCallOptions &) {
const auto id = context.operationId();
m_activeRPCs.insert(id, Clock::now());
mLog->add(LogModel::Level::Debug, id, context.descriptor(), u"Starting"_s);
return Continuation::Proceed;
}
void LoggingInterceptor::onInitialMetadata(QGrpcInterceptionContext &context,
QMultiHash<QByteArray, QByteArray> &metadata) {
if (metadata.isEmpty())
return;
mLog->add(LogModel::Level::Info, context.operationId(), context.descriptor(),
u"Initial metadata: %1"_s.arg(QDebug::toString(metadata)));
}
void LoggingInterceptor::onMessageReceived(QGrpcInterceptionContext &context,
QByteArray &messageData) {
mLog->add(LogModel::Level::Debug, context.operationId(), context.descriptor(),
u"Received %2 bytes"_s.arg(messageData.size()));
}
void LoggingInterceptor::onWriteMessage(QGrpcInterceptionContext &context, QProtobufMessage &) {
mLog->add(LogModel::Level::Debug, context.operationId(), context.descriptor(),
u"About to write message"_s);
}
void LoggingInterceptor::onWritesDone(QGrpcInterceptionContext &context) {
mLog->add(LogModel::Level::Info, context.operationId(), context.descriptor(),
u"About to finish communication"_s);
}
void LoggingInterceptor::onTrailingMetadata(QGrpcInterceptionContext &context,
QMultiHash<QByteArray, QByteArray> &metadata) {
if (metadata.isEmpty())
return;
mLog->add(LogModel::Level::Info, context.operationId(), context.descriptor(),
u"Trailing metadata: %1"_s.arg(QDebug::toString(metadata)));
}
void LoggingInterceptor::onCancel(QGrpcInterceptionContext &context) {
mLog->add(LogModel::Level::Info, context.operationId(), context.descriptor(),
u"About to cancel the RPC"_s);
}
void LoggingInterceptor::onFinished(QGrpcInterceptionContext &context, QGrpcStatus &status) {
const auto it = m_activeRPCs.find(context.operationId());
Q_ASSERT(it != m_activeRPCs.cend());
const auto duration = Ms(Clock::now() - it.value()).count();
m_activeRPCs.erase(it);
const auto codeStr = QDebug::toString(status.code()).section("::", -1);
auto msg = u"Finished in %1 ms. StatusCode: %2"_s.arg(duration).arg(codeStr);
if (!status.message().isEmpty())
msg += u", Message: "_s + status.message();
const auto level = [&] {
switch (status.code()) {
case QtGrpc::StatusCode::Ok:
return LogModel::Level::Info;
case QtGrpc::StatusCode::NotFound:
case QtGrpc::StatusCode::Unauthenticated:
return LogModel::Level::Warning;
default:
return LogModel::Level::Error;
}
}();
mLog->add(level, context.operationId(), context.descriptor(), msg);
}
Running the QtGrpc Chat client with this interceptor in place now shows detailed logs for every RPC. With a single interceptor, we gain full visibility into the lifecycle of all calls executed over a channel.
使用此拦截器运行QtGrpc聊天客户端现在可以显示每个RPC的详细日志。使用单个拦截器,我们可以完全了解通过通道执行的所有调用的生命周期。

At the time of writing, these updates to the QtGrpc Chat example have not yet been merged, but are expected in the next Qt 6.11 patch release. For a more advanced scenario, the Qt GRPC Client Guide example demonstrates how three independent interceptors can work together, showcasing more complex interception workflows.
在撰写本文时,QtGrpc Chat示例的这些更新尚未合并,但预计将在下一个Qt 6.11补丁中发布。对于更高级的场景,Qt GRPC客户端指南示例演示了三个独立的拦截器如何协同工作,展示了更复杂的拦截工作流程。
If there are features you rely on or areas you’d like to see explored, let us know.
如果有依赖的功能或希望探索的领域,请告诉我们。

2521

被折叠的 条评论
为什么被折叠?



