简体   繁体   English

GRPC Web 使用golang客户端请求?

[英]GRPC Web Request using golang client?

I am trying to create a grpc server with the hep of grpc-web wrapper.我正在尝试使用 grpc-web 包装器的 hep 创建一个 grpc 服务器。 The idea is to use this grpc server both with browser based application as well as with the normal grpc client.这个想法是将此 grpc 服务器与基于浏览器的应用程序以及普通的 grpc 客户端一起使用。 But i am confused how can i make it work for both the applications?但我很困惑如何让它适用于这两个应用程序?

package main

import (
    "context"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
    "time"

    "github.com/repo/test-grpc-server/greet/greetpb"
    "github.com/improbable-eng/grpc-web/go/grpcweb"
    "google.golang.org/grpc"
)

type server struct{}

func (*server) Greet(ctx context.Context, req *greetpb.GreetRequest) (*greetpb.GreetResponse, error) {
    fmt.Printf("Greet function was invoked with %v", req)
    firstName := req.GetGreeting().GetFirstName()
    result := "Hello " + firstName
    res := greetpb.GreetResponse{
        Result: result,
    }
    return &res, nil
}

func (*server) GreetManyTimes(req *greetpb.GreetManyTimesRequest, stream greetpb.GreetService_GreetManyTimesServer) error {
    fmt.Printf("GreetMany function was invoked with %v", req)

    firstName := req.GetGreeting().GetFirstName()

    for i := 0; i < 10; i++ {
        result := "Hello " + firstName + " number " + strconv.Itoa(i)
        res := &greetpb.GreetManyTimesResponse{
            Result: result,
        }
        stream.Send(res)
        time.Sleep(1000 * time.Millisecond)
    }
    return nil

}
func (*server) LongGreet(stream greetpb.GreetService_LongGreetServer) error {
    fmt.Printf("LongGreet function was invoked with a streaming request\n")
    result := ""
    for {
        req, err := stream.Recv()
        if err == io.EOF {
            // we have finished reading the client stream
            return stream.SendAndClose(&greetpb.LongGreetResponse{
                Result: result,
            })
        }
        if err != nil {
            log.Fatalf("Error while reading client stream: %v", err)
        }

        firstName := req.GetGreeting().GetFirstName()
        result += "Hello " + firstName + "! "
    }
}



func main() {
    fmt.Println("Go gRPC Server")
    /*lis, err := net.Listen("tcp", ":5051")
    if err != nil {
        log.Fatalf("Failed to listen: %v", err)
    }*/
    grpcServer := grpc.NewServer()
    greetpb.RegisterGreetServiceServer(grpcServer, &server{})
    grpc := grpcweb.WrapServer(grpcServer)
    http.HandleFunc("/", func(resp http.ResponseWriter, req *http.Request) {
        allowCors(resp, req)
        if grpc.IsGrpcWebRequest(req) || grpc.IsAcceptableGrpcCorsRequest(req) {
            grpc.ServeHTTP(resp, req)
        }
    })
    httpPort := ":50051"
    fmt.Println("HTTP server listening on", httpPort)
    err := http.ListenAndServe(httpPort, nil)
    if err != nil {
        log.Fatal("Failed to start a HTTP server:", err)
    }

}

func allowCors(resp http.ResponseWriter, req *http.Request) {
    resp.Header().Set("Access-Control-Allow-Origin", "*")
    resp.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
    resp.Header().Set("Access-Control-Expose-Headers", "grpc-status, grpc-message")
    resp.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, XMLHttpRequest, x-user-agent, x-grpc-web, grpc-status, grpc-message")
}

Now i am trying to call the grpc function from my client like this below.现在我正在尝试从我的客户那里调用 grpc function,如下所示。 but it is not working..但它不工作..

package main

import (
    "context"
    "fmt"
    "io"
    "log"
    "time"

    "github.com/repo/test-grpc-server/greet/greetpb"
    "github.com/repo/test-grpc-server/sum/sumpb"
    "google.golang.org/grpc"
)

func main() {

    fmt.Println("Hello I'm a client")
    conn, err := grpc.Dial("0.0.0.0:50051", grpc.WithInsecure())
    if err != nil {
        log.Fatalf("could not connect: %v", err)
    }
    defer conn.Close()

    c := greetpb.NewGreetServiceClient(conn)

    doUnary(c)
}
func doUnary(c greetpb.GreetServiceClient) {
    fmt.Println("do unary from the client")
    req := &greetpb.GreetRequest{
        Greeting: &greetpb.Greeting{
            FirstName: "Hsn",
            LastName:  "Hrn",
        },
    }
    res, err := c.Greet(context.Background(), req)
    if err != nil {
        log.Fatalf("error while calling Greet RPC: %v", err)
    }
    log.Printf("Response from Greet: %v", res.Result)

}

My.prot file look like this..我的.prot 文件看起来像这样..

syntax = "proto3";

package greet;
option go_package="./greet/greetpb";

message Greeting {
    string first_name =1 ;
    string last_name = 2;
}

message GreetRequest {
    Greeting greeting = 1;
}

message GreetResponse {
    string result = 1;
}

message GreetManyTimesRequest{
    Greeting greeting =1;
}

message GreetManyTimesResponse{
    string result=1;
}
message LongGreetRequest {
    Greeting greeting = 1;


}

message LongGreetResponse{
    string result = 1;
}


service GreetService{
    //Unary
    rpc Greet (GreetRequest) returns (GreetResponse) {};
    //Server Streaming 
    rpc GreetManyTimes(GreetManyTimesRequest) returns (stream GreetManyTimesResponse) {};
    //Client Streaming 
    rpc LongGreet(stream LongGreetRequest) returns (LongGreetResponse) {};

}

The error logs i get are..我得到的错误日志是..

Hello I'm a client
INFO: 2021/04/27 12:53:17 [core] parsed scheme: ""
INFO: 2021/04/27 12:53:17 [core] scheme "" not registered, fallback to default scheme
INFO: 2021/04/27 12:53:17 [core] ccResolverWrapper: sending update to cc: {[{0.0.0.0:50051  <nil> 0 <nil>}] <nil> <nil>}
INFO: 2021/04/27 12:53:17 [core] ClientConn switching balancer to "pick_first"
INFO: 2021/04/27 12:53:17 [core] Channel switches to new LB policy "pick_first"
INFO: 2021/04/27 12:53:17 [core] Subchannel Connectivity change to CONNECTING
INFO: 2021/04/27 12:53:17 [core] parsed scheme: ""
INFO: 2021/04/27 12:53:17 [core] scheme "" not registered, fallback to default scheme
INFO: 2021/04/27 12:53:17 [core] ccResolverWrapper: sending update to cc: {[{0.0.0.0:50051  <nil> 0 <nil>}] <nil> <nil>}
INFO: 2021/04/27 12:53:17 [core] ClientConn switching balancer to "pick_first"
INFO: 2021/04/27 12:53:17 [core] Channel switches to new LB policy "pick_first"
INFO: 2021/04/27 12:53:17 [core] Subchannel Connectivity change to CONNECTING
do unary from the client
INFO: 2021/04/27 12:53:17 [core] blockingPicker: the picked transport is not ready, loop back to repick
INFO: 2021/04/27 12:53:17 [core] Subchannel picks a new address "0.0.0.0:50051" to connect
INFO: 2021/04/27 12:53:17 [core] pickfirstBalancer: UpdateSubConnState: 0xc000021cd0, {CONNECTING <nil>}
INFO: 2021/04/27 12:53:17 [core] Channel Connectivity change to CONNECTING
INFO: 2021/04/27 12:53:17 [core] Subchannel picks a new address "0.0.0.0:50051" to connect
INFO: 2021/04/27 12:53:17 [core] pickfirstBalancer: UpdateSubConnState: 0xc000021ed0, {CONNECTING <nil>}
INFO: 2021/04/27 12:53:17 [core] Channel Connectivity change to CONNECTING
INFO: 2021/04/27 12:53:17 [core] Subchannel Connectivity change to TRANSIENT_FAILURE
INFO: 2021/04/27 12:53:17 [transport] transport: loopyWriter.run returning. connection error: desc = "transport is closing"
INFO: 2021/04/27 12:53:17 [transport] transport: loopyWriter.run returning. connection error: desc = "transport is closing"
INFO: 2021/04/27 12:53:17 [core] Subchannel Connectivity change to TRANSIENT_FAILURE
INFO: 2021/04/27 12:53:17 [core] pickfirstBalancer: UpdateSubConnState: 0xc000021ed0, {TRANSIENT_FAILURE connection closed}
INFO: 2021/04/27 12:53:17 [core] Channel Connectivity change to TRANSIENT_FAILURE
INFO: 2021/04/27 12:53:17 [core] pickfirstBalancer: UpdateSubConnState: 0xc000021cd0, {TRANSIENT_FAILURE connection closed}
INFO: 2021/04/27 12:53:17 [core] Channel Connectivity change to TRANSIENT_FAILURE
2021/04/27 12:53:17 error while calling Greet RPC: rpc error: code = Unavailable desc = connection closed
exit status 1

Someone help would be really appreciated.有人帮助将不胜感激。 Thank you!谢谢!

As per the comments the issue is that you are were attempting to connect to a gRPC-Web server using a gRPC client.根据评论,问题是您正在尝试使用 gRPC 客户端连接到 gRPC-Web 服务器。 gRPC and gRPC-Web are different wire protocols (gRPC-Web was created because web browser APIs don't provide sufficient control over HTTP/2 requests to implement gRPC). gRPC 和 gRPC-Web 是不同的有线协议(创建 gRPC-Web 是因为 web 浏览器 API 没有对 HTTP/2 请求提供足够的控制来实现 gRPC)。 This blog post provides a good overview.这篇博文提供了一个很好的概述。

Because you are building a web-app you will need to use gRPC-Web;因为您正在构建一个网络应用程序,所以您需要使用 gRPC-Web; if you also wish to connect to your server using a go client then the preferred option is to use gRPC (the server can both simultaneously).如果您还希望使用 go 客户端连接到您的服务器,那么首选选项是使用 gRPC(服务器可以同时使用两者)。 Another option that could work would be to use a gRPC-Web client but I've not tried this (it will be less efficient).另一个可行的选择是使用gRPC-Web 客户端,但我没有尝试过(效率会降低)。

The 'official' way of running gRPC-Web is via an envoy plugin but as you are writing this in Go improbable-eng/grpc-web provides another, simpler, option which you are already utilising (they also have a proxy but that makes deployment more complex).运行 gRPC-Web 的“官方”方式是通过一个特使插件,但是当您在 Go 中编写此内容时 improbable -eng/grpc-web提供了另一个更简单的选项,您已经在使用(他们也有一个代理,但这使得部署更复杂)。

Your server needs to be altered to run both gRPC and gRPC-Web.您的服务器需要更改以同时运行 gRPC 和 gRPC-Web。 The simplest option is to run these on different ports (it may be possible to use a mux to detect the content-type but this is not something I've tried; it does work well if you want to serve html/js and gRPC-Web on a single port ).最简单的选择是在不同的端口上运行它们(可以使用多路复用器来检测内容类型,但这不是我尝试过的;如果你想提供 html/js 和 gRPC-它确实工作得很好- 单个端口上的 Web )。

The approach I'd take to run both servers follows (please treat this as incomplete pseudo code, I have pulled bits from a few of my applications but have not compiled/tested etc; feel free to update when you discover issues:):以下是我运行两台服务器所采用的方法(请将此视为不完整的伪代码,我已经从我的一些应用程序中提取了一些信息,但尚未编译/测试等;当您发现问题时,请随时更新:):

grpcServer := grpc.NewServer()
greetpb.RegisterGreetServiceServer(grpcServer, &server{})

// Your application is probably doing other things and you will want to be
// able to shutdown cleanly; passing in a context is a good method..
ctx, cancel = context.Cancel(context.Background())
defer cancel()   // Ensure cancel function is called eventually

// Start the grpc server on port 50050
grpcTerminated := make(chan struct{})
lis, err := net.Listen("tcp", ":50050")
if err != nil {
    panic(fmt.Sprintf("gRPC - failed to listen: %s", err))
}

go func() {
    if sErr := grpcServer.Serve(lis); sErr != nil {
       fmt.Printf("grpc server shutdown: %s", err)       
    }
    close(grpcTerminated) // In case server is terminated without us requesting this
}()

// Start the grpc-Web server on port 5051
grpcWebTerminated := make(chan struct{})
grpc := grpcweb.WrapServer(grpcServer)
mux := http.NewServeMux()
mux.HandleFunc("/", func(resp http.ResponseWriter, req *http.Request) {
   allowCors(resp, req)
   if grpc.IsGrpcWebRequest(req) || grpc.IsAcceptableGrpcCorsRequest(req) {
      grpc.ServeHTTP(resp, req)
    }
}))
rpcWebServer := &http.Server{
        Handler: mux,
        Addr:    ":50051"}

wg.Add(1)
go func() {
   defer wg.Done()
   if err := rpcWebServer.ListenAndServe(); err != nil {
      fmt.Printf("Web server (GRPC) shutdown: %s", err)
   }
   close(grpcWebTerminated) // In case server is terminated without us requesting this
}()

// Wait for the web server to shutdown OR the context to be cancelled...
select {
   case <-ctx.Done():
      // Shutdown the servers (there are shutdown commands to request this)
   case <-grpcTerminated:
      // You may want to exit if this happens (will be due to unexpected error)
   case <-grpcWebTerminated:
      // You may want to exit if this happens (will be due to unexpected error)
}

// Wait for the goRoutines to complete
<-grpcTerminated:
<-grpcWebTerminated

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM