sogou / srpc

RPC framework based on C++ Workflow. Supports SRPC, Baidu bRPC, Tencent tRPC, thrift protocols.
Apache License 2.0
1.96k stars 387 forks source link

SRPCHttpServer中不支持自定url映射方法 #404

Open rayinengineer opened 3 months ago

rayinengineer commented 3 months ago

像BRPC的服务框架,支持自定义url的映射,如

if (server.AddService(&queue_svc,
                      brpc::SERVER_DOESNT_OWN_SERVICE,
                      "/v1/queue/start   => start,"
                      "/v1/queue/stop    => stop,"
                      "/v1/queue/stats/* => get_stats") != 0) {
    LOG(ERROR) << "Fail to add queue_svc";
    return -1;
}

目前这边修改了一个

srpc/src/rpc_client.h 中增加两个模板特例

static inline void __set_request_uri_by_uri(const ParsedURI *uri,std::string& request_uri)
{
    if (uri->path && uri->path[0])
        request_uri = uri->path;
    else
        return;

    if (uri->query && uri->query[0])
    {
        request_uri += "?";
        request_uri += uri->query;
    }
}

template<>
inline void RPCClient<RPCTYPESRPCHttp>::task_init(COMPLEXTASK *task) const
{
    __task_init(task);
    std::string header_host;

    if (this->has_addr_info)
        header_host += this->params.host + ":" + std::to_string(this->params.port);
    else
        __set_host_by_uri(task->get_current_uri(), this->params.is_ssl, header_host);

    task->get_req()->set_header_pair("Host", header_host.c_str());

    std::string request_uri;
    __set_request_uri_by_uri(task->get_current_uri(),request_uri);
    if (!request_uri.empty())
    {
        task->get_req()->set_request_uri(request_uri);
    }
}

template<>
inline void RPCClient<RPCTYPEThriftHttp>::task_init(COMPLEXTASK *task) const
{
    __task_init(task);
    std::string header_host;

    if (this->has_addr_info)
        header_host += this->params.host + ":" + std::to_string(this->params.port);
    else
        __set_host_by_uri(task->get_current_uri(), this->params.is_ssl, header_host);

    task->get_req()->set_header_pair("Host", header_host.c_str());

    std::string request_uri;
    __set_request_uri_by_uri(task->get_current_uri(),request_uri);
    if (!request_uri.empty())
    {
        task->get_req()->set_request_uri(request_uri);
    }
}

srpc/src/message/rpc_message_srpc.cc

bool SRPCHttpRequest::serialize_meta()
{
    if (this->buf->size() > 0x7FFFFFFF)
        return false;

    RPCMeta *meta = static_cast<RPCMeta *>(this->meta);
    int data_type = meta->data_type();
    int compress_type = meta->compress_type();

    set_http_version("HTTP/1.1");
    set_method("POST");

    if (NULL == get_request_uri())
    {
        set_request_uri("/" + meta->mutable_request()->service_name() +
                        "/" + meta->mutable_request()->method_name());
    }

    ...

}

srpc/src/rpc_server.h中增加该接口,该接口比add_service多了一个原来的路径映射到service 方法,如"Test/abc => Echo,Test/cde => Echo2"

int add_service(RPCService *service, const std::string& mapping);

template<class RPCTYPE>
inline int RPCServer<RPCTYPE>::add_service(RPCService* service,const std::string& mapping)
{
    size_t pos = 0;
    size_t equalPos;
    while ((equalPos = mapping.find("=>", pos)) != std::string::npos) 
    {
        size_t nextKeyPos = mapping.find(',', equalPos);
        if (nextKeyPos == std::string::npos) {
            nextKeyPos = mapping.length();
            }
        if (equalPos - pos > 0 && nextKeyPos - equalPos - 2 > 0)//key value
            {
            std::string key = mapping.substr(pos, equalPos - pos);
                    std::string value = "/" + service->get_name() +
                            "/" + mapping.substr(equalPos + 2, nextKeyPos - equalPos - 2);
            this->path_map.emplace(key,value);
        }
        pos = nextKeyPos + 1;
    }

    return add_service(service);
}

增加特例实现,因为get_request_uri、set_request_uri只有在HTTP请求中才能获取到

template<>
void RPCServer<RPCTYPESRPCHttp>::server_process(NETWORKTASK *task) const
{
    auto *req = task->get_req();
    auto *resp = task->get_resp();
    int status_code;

    const auto it = this->path_map.find(req->get_request_uri());
    if (it != this->path_map.cend())
    {
        req->set_request_uri(it->second);
    }

    if (!req->deserialize_meta())
        status_code = RPCStatusMetaError;
    else
    {
        auto *server_task = static_cast<TASK *>(task);
        RPCModuleData *task_data = server_task->mutable_module_data();
        req->get_meta_module_data(*task_data);

        RPCTYPESRPCHttp::server_reply_init(req, resp);
        auto *service = this->find_service(req->get_service_name());

        if (!service)
            status_code = RPCStatusServiceNotFound;
        else
        {
            auto *rpc = service->find_method(req->get_method_name());

            if (!rpc)
                status_code = RPCStatusMethodNotFound;
            else
            {
                for (auto *module : this->modules)
                {
                    if (module)
                        module->server_task_begin(server_task, *task_data);
                }

                status_code = req->decompress();
                if (status_code == RPCStatusOK)
                    status_code = (*rpc)(server_task->worker);
            }
        }

        SERIES *series = static_cast<SERIES *>(series_of(task));
        series->set_module_data(task_data);
    }

    resp->set_status_code(status_code);
}
rayinengineer commented 3 months ago

上述的srpc/src/rpc_client.h 中可以自定义url,如果有,就使用自定义的,如果没有就采用默认的;

struct srpc::RPCClientParams params = srpc::RPC_CLIENT_PARAMS_DEFAULT;
params.url = "http://127.0.0.1:1415/Test/Echo";
Example::SRPCHttpClient client(&params);

srpc/src/rpc_server.h中加url映射,调用方式:

server.add_service(&impl,"/Test/Echo=>Echo," "/Test/xxx=>Test");
Barenboim commented 3 months ago

感谢感谢!我们看一下。也可以发PR讨论。

holmes1412 commented 2 months ago

@rayinengineer hi,感谢你提的这个需求,感觉非常合理呀~ 参考了下其他rpc系统的trans coding做法,没有统一的规范,所以我们当前使用了json来表达。比如:

SRPCHttpServer server;
ExampleServiceImpl impl;
server.add_service(&impl, "{ \"/test/echo\": \"Echo\"," "\"/no/method/for/test\": \"Test\" }");

麻烦有空了看看这个格式是否方便呢?