QGrpcClientInterceptor 缓存示例

缓存拦截器

缓存拦截器可以是提高您的 Qt GRPC 连接性能的多用途工具。通过创建一个自定义拦截器,您可以设定缓存行为以满足应用程序的具体要求。

创建缓存拦截器

为了创建缓存拦截器,您需要从 QGrpcClientInterceptor 派生一个子类,并覆盖适当的拦截方法以添加缓存功能。

先决条件

为了确定我们可以处理哪些类型的拦截器,让我们假设我们的 .proto 文件是

syntax = "proto3";

message SimpleStringMessage {
    string testFieldString = 6;
}

service TestService {
    rpc testMethod(SimpleStringMessage) returns (SimpleStringMessage) {}
    rpc testMethodServerStream(SimpleStringMessage) returns (stream SimpleStringMessage) {}
}

我们还将快速确定 MyCacheStorage API 的样子

  • insert() 方法,可以向缓存中插入一个新的条目(在我们的情况下,条目将以 QString 存储为)
  • insert_or_append() 方法,类似于 insert(),但是在 methodservice 的条目已存在且尚未终结的情况下,它会追加数据 - 这将被用于缓存流式响应。
  • find(),这个方法可以根据 methodservice 关键字找到条目。
  • finalize(),这个方法将终结缓存中的条目,对于流用例,调用该方法意味着完整的响应已经缓存。

缓存拦截器实现

这里是一个简单的缓存拦截器的示例

class CachingInterceptor : public QGrpcClientInterceptor
{
protected:
    void interceptCall(std::shared_ptr<QGrpcChannelOperation> operation,
                                                  std::shared_ptr<QGrpcCallReply> response,
                                                  QGrpcInterceptorContinuation<QGrpcCallReply> &continuation) override
    {
        // Intercept the response
        QObject::connect(response.get(), &QGrpcCallReply::finished, this,
                             [operation, response] {
                                SimpleStringMessage mess = response->read<SimpleStringMessage>();
                                cache.insert(operation->method(), operation->service(), mess.testFieldString());
                             });
        // Deserialize the request
        SimpleStringMessage deserializedArg;
        if (!operation->serializer()->deserialize(&deserializedArg, operation->arg())) {
            qError() << "Deserialization of arg failed.";
            return;
        }
        std::optional<QString> cachedStr = cache.find(operation->method(), operation->service(), deserializedArg);
        if (cachedStr) {
            // Serialize cached response
            SimpleStringMessage val;
            val.setTestFieldString(cachedStr);
            const auto serializedValue = operation->serializer()->serialize<SimpleStringMessage>(&val);
            emit operation->dataReady(serializedValue);
            emit operation->finished();
            // Set server metadata cached field
            auto metadata = operation->serverMetadata();
            metadata.insert({ "cached", "true" });
            operation->setServerMetadata(metadata);
            return;
        }
        continuation(std::move(response), operation);
    }

    void interceptServerStream(std::shared_ptr<QGrpcChannelOperation> operation,
                                                  std::shared_ptr<QGrpcServerStream> stream,
                                                  QGrpcInterceptorContinuation<QGrpcServerStream> &continuation) override
    {
        // Intercept the response
        QObject::connect(stream.get(), &QGrpcServerStream::messageReceived, this,
                             [operation, stream] {
                                SimpleStringMessage mess = stream->read<SimpleStringMessage>();
                                cache.insert_or_append(operation->method(), operation->service(), mess.testFieldString());
                             });
        QObject::connect(stream.get(), &QGrpcServerStream::finished, this,
                             [operation] {
                                cache.finalize(operation->method(), operation->service());
                             });
        // Deserialize the request
        SimpleStringMessage deserializedArg;
        if (!operation->serializer()->deserialize(&deserializedArg, operation->arg())) {
            qError() << "Deserialization of arg failed.";
            return;
        }
        std::optional<QString> cachedStr = cache.find(operation->method(), operation->service(), deserializedArg);
        if (cachedStr) {
            // Serialize cached response
            SimpleStringMessage val;
            val.setTestFieldString(cachedStr);
            const auto serializedValue = operation->serializer()->serialize<SimpleStringMessage>(&val);
            emit operation->dataReady(serializedValue);
            emit operation->finished();
            // Set server metadata cached field
            auto metadata = operation->serverMetadata();
            metadata.insert({ "cached", "true" });
            operation->setServerMetadata(metadata);
            return;
        }
        continuation(std::move(response), operation);
    }

    MyCacheStorage<QString> cache;
};

缓存拦截器中的 interceptCall()interceptServerStream() 方法都拦截了 Qt GRPC 调用和流,并为响应实现了缓存机制。两者都建立连接来处理传入的消息并尝试反序列化请求参数。两种方法都检查是否有缓存的响应,如果找到,则会进行序列化并发出正确的信号以设置响应数据。如果响应已在缓存中找到,则将服务器元数据键 cached 设置为 true

它们之间的主要区别在于它们处理服务器流交互和缓存终结的方式:interceptCall() 主要依赖于响应的完成信号进行缓存,而 interceptServerStream() 使用服务器流的 messageReceived 和 finished 信号的连接,以便全面处理流式交互和缓存终结,这样,只有当整个流都被缓存时,interceptServerStream() 才会返回缓存的响应。

注册缓存拦截器

接下来,您需要将缓存拦截器注册到 QGrpcClientInterceptorManager。这确保了它成为拦截链的一部分。

QGrpcClientInterceptorManager manager;
auto cachingInterceptor = std::make_shared<CachingInterceptor>();
manager.registerInterceptor(cachingInterceptor);

© 2024 Qt公司有限公司。本文档中包含的文档贡献归其各自所有者所有。本提供的文档遵守自由软件基金会发布的自由文档许可协议版本1.3的条款。Qt及其相关商标是芬兰的Qt公司及/或其他国家的商标。所有其他商标均为其各自所有者的财产。