apache / brpc

brpc is an Industrial-grade RPC framework using C++ Language, which is often used in high performance system such as Search, Storage, Machine learning, Advertisement, Recommendation etc. "brpc" means "better RPC".

Home Page:https://brpc.apache.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

brpc异步server使用方式

zhangyachen opened this issue · comments

Is your feature request related to a problem? (你需要的功能是否与某个问题有关?)
我正在从grpc切换到brpc框架,想请教一下brpc异步server的使用方式问题。

背景:

在我们需要实现rpc server接口中,会调用一个异步接口async(),调用后立即返回。 当async背后的逻辑处理完成,会调用提前设置好的callback。从调用async到再调用callback大概10ms。

Describe the solution you'd like (描述你期望的解决方法)
在之前grpc中使用的异步处理,利用ServerCompletionQueue结构。
我想请教一下,在brpc中,如果使用异步接口,内部再调用另外一个异步接口async,有什么最佳实践吗?
我想到的是在调用async后使用future.get(),在callback中set_value。不知道这种方式在brpc异步接口中是否会有什么性能问题和额外考虑的问题?

谢谢。

Describe alternatives you've considered (描述你想到的折衷方案)

Additional context/screenshots (更多上下文/截图)
打算实现的简单伪代码,主要逻辑在AsyncInferJob::run():

struct AsyncInferJob {
    brpc::Controller* cntl;
    const helloworld::HelloRequest* request;
    helloworld::HelloReply* response;
    google::protobuf::Closure* done;
    TRITONSERVER_Server* server;
    TRITONSERVER_ResponseAllocator* allocator;

    void run();
    void run_and_delete() {
        run();
        delete this;
    }
};

static void* process_thread(void* args) {
    AsyncInferJob* job = static_cast<AsyncInferJob*>(args);
    job->run_and_delete();
    return NULL;
}

class GreeterServiceImpl : public helloworld::Greeter {
public:
    GreeterServiceImpl(TRITONSERVER_Server* server, TRITONSERVER_ResponseAllocator* allocator)
        : server_(server), allocator_(allocator) {}

    void SayHello(google::protobuf::RpcController* cntl_base,
                  const helloworld::HelloRequest* request,
                  helloworld::HelloReply* response,
                  google::protobuf::Closure* done) override {
        brpc::ClosureGuard done_guard(done);
        brpc::Controller* cntl = static_cast<brpc::Controller*>(cntl_base);

        // Process the request asynchronously.
        AsyncInferJob* job = new AsyncInferJob;
        job->cntl = cntl;
        job->request = request;
        job->response = response;
        job->done = done;
        job->server = server_;
        job->allocator = allocator_;
        bthread_t th;
        CHECK_EQ(0, bthread_start_background(&th, NULL, process_thread, job));

        // We don't want to call done->Run() here, release the guard.
        done_guard.release();
    }

private:
    TRITONSERVER_Server* server_;
    TRITONSERVER_ResponseAllocator* allocator_;
};

void AsyncInferJob::run() {
    brpc::ClosureGuard done_guard(done);

    // 1. 使用promise同步response
    auto promise = new std::promise<TRITONSERVER_InferenceResponse*>();
    std::future<TRITONSERVER_InferenceResponse*> future = promise->get_future();

    // 2. 设置callback
    TRITONSERVER_InferenceRequestSetResponseCallback(
        irequest, allocator, nullptr, 
        [](TRITONSERVER_InferenceResponse* response, const uint32_t flags, void* userp)
        {
            auto p = reinterpret_cast<std::promise<TRITONSERVER_InferenceResponse*>*>(userp);
            **p->set_value(response);**
        },
        reinterpret_cast<void*>(promise));

    // 3. 调用异步接口
    TRITONSERVER_ServerInferAsync(server, irequest, nullptr);

    // 4. 等待response
    TRITONSERVER_InferenceResponse* completed_response = future.get();
}

你通过future.get()的方式本质上还是阻塞当前线程来等待结果,那么使用异步接口就意义不大了。你可以不使用future.get(),而是在TRITONSERVER_InferenceRequestSetResponseCallback 设置的callback函数中调用done->Run()来发送回复。
这样的话,使用bthread_start_background来启动新线程也是没有必要的,因为新线程里只是发起了一个异步请求。所以,可以直接在SayHello里面同步调用process_thread方法。

好的,我想再确认一下您的建议:

  1. SayHello中直接调用 process_thread函数。
  2. process_thread中也没有必要进行promise和future的操作。
  3. TRITONSERVER_InferenceRequestSetResponseCallback中捕获done,,callback收到response后在done->Run()。

期待您的回复。 ^_^

好的,我想再确认一下您的建议:

  1. SayHello中直接调用 process_thread函数。
  2. process_thread中也没有必要进行promise和future的操作。
  3. TRITONSERVER_InferenceRequestSetResponseCallback中捕获done,,callback收到response后在done->Run()。

期待您的回复。 ^_^

是的