Tonic server uses encapsulation

proto file

syntax = "proto3";

package logic;

service Logic { rpc Conn(ConnRequest) returns (ConnReply); }

message ConnRequest { string name = 1; }

message ConnReply { string message = 1; }

build.rs code

fn main() {
    tonic_build::configure()
        .build_server(true)
        .build_client(true)
        .out_dir("src/api")
        .compile(&["src/api/logic.proto"], &["src/api/"])
        .expect("Failed to generate logic grpc");
}

server side original call code

#[tokio::main]
async fn main() {
    let addr = "[::1]:50052".parse().unwrap();
    let greeter = LogicSrv::default();

    Server::builder()
        .add_service(LogicServer::new(greeter))
        .serve(addr)
        .await?;
}

#[derive(Debug, Default)]
pub struct LogicSrv;

#[tonic::async_trait]
impl Logic for LogicSrv {
    async fn conn(&self, request: Request<ConnRequest>) -> Result<Response<ConnReply>, Status> {
        println!("Got a request: {:#?}", request);

        let reply = ConnReply {
            message: format!("Hello {}", request.into_inner().name),
        };

        Ok(Response::new(reply))
    }
}

I now want to implement the encapsulation of the server-side code call, convenient call (some people may say, why to encapsulate, my purpose is, after encapsulation, I can put the server-side logic to the parameter, there is no need to repeat the use of Server::builder()), my approach is:

#[tokio::main]
async fn main() {
    grpc_srv_fun("[::1]:50052".to_string(), || {
            LogicServer::new(LogicSrv::default())
        }).await?;
}

async fn grpc_srv_fun<S, F>(addr: String, srv: F) -> Result<(), std::io::Error>
where
    S: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible>
        + NamedService
        + Clone
        + Send
        + 'static,
    S::Future: Send + 'static,
    F: Fn() -> S + Send + 'static,
{
    let addr = addr.parse().unwrap();
    Server::builder()
        .add_service(srv())
        .serve(addr)
        .await
        .unwrap();
    Ok(())
}

#[derive(Debug, Default)]
pub struct LogicSrv;

#[tonic::async_trait]
impl Logic for LogicSrv {
    async fn conn(&self, request: Request<ConnRequest>) -> Result<Response<ConnReply>, Status> {
        println!("Got a request: {:#?}", request);

        let reply = ConnReply {
            message: format!("Hello {}", request.into_inner().name),
        };

        Ok(Response::new(reply))
    }
}

Question:
grpc_srv_fun() function of the srv parameter type is always an error, can I have the tonic::transport::server::Server.add_service() constraint parameter type, or an error, turn to your bosses

It looks like you're trying to encapsulate the server-side gRPC code to make it more convenient to call without repeating the use of Server::builder(). However, there are a few issues in your code that are causing errors.

The main issue is with the type constraints in the grpc_srv_fun function. The srv parameter should be of type impl IntoIterator<Item = (String, S)>, which allows you to add multiple services to the server. Additionally, you need to specify the correct types for S and F in the function signature.

Here's an updated version of your code with the correct type constraints:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    grpc_srv_fun("[::1]:50052".to_string(), || {
        vec![("logic.Logic".to_string(), LogicServer::new(LogicSrv::default()))]
    })
    .await?;

    Ok(())
}

async fn grpc_srv_fun<S, F, Srv>(addr: String, srv: F) -> Result<(), Box<dyn std::error::Error>>
where
    S: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible> + NamedService + Clone + Send + 'static,
    S::Future: Send + 'static,
    F: Fn() -> Vec<(String, Srv)> + Send + 'static,
    Srv: Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible> + NamedService + Clone + Send + 'static,
    Srv::Future: Send + 'static,
{
    let addr = addr.parse().unwrap();
    let mut server = Server::builder();

    for (name, service) in srv().into_iter() {
        server = server.add_service(service);
        println!("Added service: {}", name);
    }

    server.serve(addr).await?;
    Ok(())
}


In this updated code:

  • The grpc_srv_fun function now has correct type constraints for S, F, and Srv.
  • The srv parameter is expected to return a Vec of tuples where each tuple contains the service name (String) and the service instance (Srv).
  • Inside main, we call grpc_srv_fun with a closure that returns a vector containing the service name and instance.

Make sure to replace "logic.Logic" with the actual name of your gRPC service defined in your proto file. This should resolve the errors you were encountering.

No, the following type definition does not pass, the compiler reported an error

Service<Request<Body>, Response = Response<BoxBody>, Error = Infallible> + NamedService + Clone + Send + 'static

Resolved, the reference dependency version is not the same issue

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.