如何在Golang中使用gRPC从服务器向客户端发起HTTP请求

huangapple go评论78阅读模式
英文:

How to make a HTTP request from server to client using grpc in golang

问题

问题陈述

我有一个用golang编写的客户端(用于拨号到服务器)和服务器(用于监听传入请求),并且定义了RPC调用。我试图在服务器端发起一个HTTP请求,该请求将执行流式传输的RPC调用,并将JSON响应发送回用户。

挑战

我能够在不同的端口上处理grpc和HTTP请求,但在将HTTP请求中的参数传递到服务器端的RPC调用时遇到问题。

服务器代码

log.Println("Listening for connections from client ........")
lis, err := net.Listen("tcp", ":9000")
if err != nil {
    log.Fatalf("failed to listen: %v", err)
}

s := testApi.Server{}

grpcServer := grpc.NewServer()

testApi.RegisterTestApiServiceServer(grpcServer, &s)

if err := grpcServer.Serve(lis); err != nil {
    log.Fatalf("failed to serve: %s", err)
}

func main() {
    go runGrpc()

    log.Printf("*------ Waiting for requests from users ------*")
    router := mux.NewRouter().StrictSlash(true)
    router.HandleFunc("/exchangeId/{test_id}", ConnectAndExchange).Methods("GET")
    log.Fatal(http.ListenAndServe(":8080", router))
}

func ConnectAndExchange(w http.ResponseWriter, r *http.Request){
    vars := mux.Vars(r)
    test_id, _ := strconv.Atoi(vars["test_id"])
    log.Println("Test id request from user : ", test_id)
}

func (s *Server) ConnectAndStream(channelStream TestApiService_ConnectAndStreamServer) error {
    // 问题:这个id必须来自上面的http请求- test_id
    var id int32 = 1234566
    // id := a.ConnectAndExchange
    log.Println("Id from sam user ", id)

    // var id int32 = 1234566    
    for i := 1; i <= 2; i++ {
        id += 1
        log.Println("Speed Server is sending data : ", id)
        channelStream.Send(&Input{Id: id})
    }

    for i := 1; i <= 2; i++ {
        log.Println("now time to receive")
        client_response, err := channelStream.Recv()
        log.Println("Response from samd client : ", client_response.Id)

        if err != nil {
            log.Println("Error while receiving from samd : ", err)
        }
    }

    return nil
}

我被无法将curl请求中的test_id传递给上述的RPC调用所困扰。非常感谢任何输入。

注意
客户端-拨入并连接到服务器,并开始接收和发送数据(双向流式传输)

英文:

Problem Statement

I have a client (which dials to the server) and server (that listens for incoming requests) written in golang and with the RPC calls defined. I am trying to initiate an HTTP request on the server side which would in turn execute the RPC call for streaming and send a JSON response back to the user

Challenge

I was able to handle both grpc and HTTP requests on different ports but having issues with passing parameters from the HTTP request onto the RPC call on the server side

Server Code

log.Println(&quot;Listening for connections from client ........&quot;)
lis, err := net.Listen(&quot;tcp&quot;, &quot;:9000&quot;)
if err != nil {
log.Fatalf(&quot;failed to listen: %v&quot;, err)
}
s := testApi.Server{}
grpcServer := grpc.NewServer()
testApi.RegisterTestApiServiceServer(grpcServer, &amp;s)
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf(&quot;failed to serve: %s&quot;, err)
}
func main() {
go runGrpc()
log.Printf(&quot;*------ Waiting for requests from users ------*&quot;)
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc(&quot;/exchangeId/{test_id}&quot;, ConnectAndExchange).Methods(&quot;GET&quot;)
log.Fatal(http.ListenAndServe(&quot;:8080&quot;, router))
}
func ConnectAndExchange(w http.ResponseWriter, r *http.Request){
vars := mux.Vars(r)
test_id, _ := strconv.Atoi(vars[&quot;test_id&quot;])
log.Println(&quot;Test id request from user : &quot;, test_id)
func (s * Server) ConnectAndStream(channelStream TestApiService_ConnectAndStreamServer) error {
// Question: This Id has to come from http request above- test_id
var id int32 = 1234566
// id := a.ConnectAndExchange
log.Println(&quot;Id from sam user &quot;, id)
// var id int32 = 1234566	
for i := 1; i &lt;= 2; i++ {
id += 1
log.Println(&quot;Speed Server is sending data : &quot;, id)
channelStream.Send(&amp;Input{Id: id})
}
for i := 1; i &lt;= 2; i++ {
log.Println(&quot;now time to receive&quot;)
client_response, err := channelStream.Recv()
log.Println(&quot;Response from samd client : &quot;, client_response.Id)
if err != nil {
log.Println(&quot;Error while receiving from samd : &quot;, err)
}
}
return nil

}

I am stuck with being able to pass the test_id from the curl request to the RPC call as above. Any input is greatly appreciated

Note
Client - Dials in and connects to the server and starts receiving and sending data (bi-directional streaming)

答案1

得分: 1

Http和GRPC客户端都是同一个服务器应用程序的一部分。那么为什么要从Http处理程序调用RPC方法呢?Http处理程序应该可以访问相同的后端功能。

你的问题有点不清楚,但是如果你想让客户端通过HTTP处理程序与服务器建立GRPC连接,这是行不通的。在这种情况下建立的GRPC连接是服务器与自身之间的连接。

编辑 - 感谢你的澄清。现在我更好地理解你想要实现的流程了。你的Http处理程序方法可以向服务器发出外部的GRPC调用,并通过http.ResponseWriter返回响应。

为了简单起见,我使用了https://github.com/grpc/grpc-go/tree/master/examples/helloworld 上的hello world示例。

运行下面的代码示例,并访问 http://localhost:1000/exchangeId/Test 将显示以下输出:

Starting
*------ Waiting for http requests from users on port 1000 ------*
server listening at 127.0.0.1:1001
Test id request from user :  Test
Server Received: Test
Greeting: Hello Test

代码示例:

import (
"context"
"log"
"net"
"net/http"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
"github.com/gorilla/mux"
)
var (
grpcserver = "localhost:1001"
)
func main() {
log.Print("Starting")
go StartGrpcServer()
log.Printf("*------ Waiting for http requests from users on port 1000 ------*")
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/exchangeId/{test_id}", ConnectAndExchange).Methods("GET")
log.Fatal(http.ListenAndServe(":1000", router))
}
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Server Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func StartGrpcServer() {
lis, err := net.Listen("tcp", grpcserver)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
func ConnectAndExchange(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
test_id := vars["test_id"]
log.Println("Test id request from user : ", test_id)
// Set up a connection to the server.
conn, err := grpc.Dial(grpcserver, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
resp, err := c.SayHello(ctx, &pb.HelloRequest{Name: test_id})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", resp.GetMessage())
w.Write([]byte(resp.GetMessage()))
}
英文:

Both the Http and GRPC client are part of the same server application. So why call the RPC method from the Http handler? The Http handler should have access to the same backend functionality.

Your question is slightly unclear but if you are trying to have your client establish a GRPC connection to the server via the HTTP handler this will not work. The GRPC connection established in this situation is between the server and its self.

Edit - thanks for the clarification. Now I understand better the flow that you are trying to achieve. Your http handler method can make the outgoing grpc call to the server and return the response back via the http.ResponseWriter

For simplicity I have used the hello world example on https://github.com/grpc/grpc-go/tree/master/examples/helloworld

Running the code sample below and hitting http://localhost:1000/exchangeId/Test will show the output

Starting
*------ Waiting for http requests from users on port 1000 ------*
server listening at 127.0.0.1:1001
Test id request from user :  Test
Server Received: Test
Greeting: Hello Test

Code sample:

import (
&quot;context&quot;
&quot;log&quot;
&quot;net&quot;
&quot;net/http&quot;
&quot;time&quot;
&quot;google.golang.org/grpc&quot;
&quot;google.golang.org/grpc/credentials/insecure&quot;
pb &quot;google.golang.org/grpc/examples/helloworld/helloworld&quot;
&quot;github.com/gorilla/mux&quot;
)
var (
grpcserver = &quot;localhost:1001&quot;
)
func main() {
log.Print(&quot;Starting&quot;)
go StartGrpcServer()
log.Printf(&quot;*------ Waiting for http requests from users on port 1000 ------*&quot;)
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc(&quot;/exchangeId/{test_id}&quot;, ConnectAndExchange).Methods(&quot;GET&quot;)
log.Fatal(http.ListenAndServe(&quot;:1000&quot;, router))
}
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf(&quot;Server Received: %v&quot;, in.GetName())
return &amp;pb.HelloReply{Message: &quot;Hello &quot; + in.GetName()}, nil
}
func StartGrpcServer() {
lis, err := net.Listen(&quot;tcp&quot;, grpcserver)
if err != nil {
log.Fatalf(&quot;failed to listen: %v&quot;, err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &amp;server{})
log.Printf(&quot;server listening at %v&quot;, lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf(&quot;failed to serve: %v&quot;, err)
}
}
func ConnectAndExchange(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
test_id := vars[&quot;test_id&quot;]
log.Println(&quot;Test id request from user : &quot;, test_id)
// Set up a connection to the server.
conn, err := grpc.Dial(grpcserver, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf(&quot;did not connect: %v&quot;, err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
resp, err := c.SayHello(ctx, &amp;pb.HelloRequest{Name: test_id})
if err != nil {
log.Fatalf(&quot;could not greet: %v&quot;, err)
}
log.Printf(&quot;Greeting: %s&quot;, resp.GetMessage())
w.Write([]byte(resp.GetMessage()))
}

huangapple
  • 本文由 发表于 2022年7月19日 00:52:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/73025910.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定