Golang grpc:如何确定服务器何时开始监听?

Golang grpc: how to determine when the server has started listening?

所以我有以下内容:

type Node struct {
    Table map[string]string
    thing.UnimplementedGreeterServer
    address string
}




func (n *Node) Start() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }

    s := grpc.NewServer()
    thing.RegisterGreeterServer(s, n)
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

在我的主要功能中,我将启动多个节点,如下所示:

func main() {
    n :=Node{Table: map[string]string{}}
    go n.Start()
    conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())

}

问题是,因为我正在同时启动节点,所以拨号连接可能无法正常工作,因为节点可能尚未设置。

理想情况下,我想要一个 done 通道来告诉我 grpc 服务器何时真正开始监听。我该如何实现?

这与没有答案的本质上是同一个问题

s.Serve(listener) 块,所以你不能通过完成 chan 来实现你的目的,相反你必须实施 healthcheck 和准备好你的服务,并在执行任何请求之前检查这些由客户。 服务器应实现以下协议:

syntax = "proto3";

package grpc.health.v1;

message HealthCheckRequest {
  string service = 1;
}

message HealthCheckResponse {
  enum ServingStatus {
    UNKNOWN = 0;
    SERVING = 1;
    NOT_SERVING = 2;
    SERVICE_UNKNOWN = 3;  // Used only by the Watch method.
  }
  ServingStatus status = 1;
}

service Health {
  rpc Check(HealthCheckRequest) returns (HealthCheckResponse);

  rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
}

例如,envoy proxy grpc_health_check 适用于上述协议。

阅读 GRPC Health Checking Protocol 了解更多信息。

只要 net.Listen returns 没有错误,就可以拨打服务器。拨号将阻塞,直到服务器调用 Accept(在这种情况下,这将发生在 s.Serve 的某处)。

将侦听器的创建移动到调用者并将其作为参数传递:

func (n *Node) Start(lis net.Listener) {
    s := grpc.NewServer()
    thing.RegisterGreeterServer(s, n)
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }

    n := Node{Table: map[string]string{}}
    go n.Start(lis)
    conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
}

或者在 Listen returns:

之后发出监听器已启动的信号
func (n *Node) Start(up chan struct{}) {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }

    if up != nil {
        close(up)
    }

    s := grpc.NewServer()
    thing.RegisterGreeterServer(s, n)
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

func main() {
    n := Node{Table: map[string]string{}}

    up := make(chan struct{})
    go n.Start(up)
    <-up

    conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
}

简单的解决方案,没有第三方,listen/accept之间没有差距,没有随机超时。

func main() {
    n := Node{Table: map[string]string{}}
    go n.Start()

    conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
    if err != nil {
        for i := 0; i < 10; i++ {
            conn, err = grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
            <-time.After(time.Second)
        }
    }
    if err != nil {
        panic(err)
    }
}

对于所有仍在寻找答案的人,这是另一种简单的方法。在子例程中启动服务器。这是一个代码片段:

// Start the server in a child routine
    go func() {
        if err := s.Serve(listener); err != nil {
            log.Fatalf("Failed to serve: %v", err)
        }
    }()
    fmt.Println("Server succesfully started on port :50051")

在我的例子中,我也在使用 MongoDB,所以当你 运行 它时,你会得到:

grpc-go-mongodb-cobra>go run server/main.go
Starting server on port :50051...
Connecting to MongoDB...
Connected to MongoDB
Server succesfully started on port :50051

我还写了一篇关于此的博客 post,工作代码在 GitHub 中。这是link:https://softwaredevelopercentral.blogspot.com/2021/03/golang-grpc-microservice.html