Bring in gRPC: Let your service provide HTTP interface at the same time

  golang, grpc, http, php, restful

Bring in gRPC: Let your service provide HTTP interface at the same time

Original address:Bring in gRPC: Let your service provide HTTP interface at the same time
Project address:https://github.com/EDDYCJY/go …

Preface

  • The interface needs to be provided for other business groups to access, but different RPC protocols cannot be internally tuned. The other party asks if it is possible to use the HTTP interface and what to do?
  • Third-party callback interfaces such as WeChat (public numbers, applets) only support HTTP interfaces. What should I do

I believe that you will encounter the above problems in your actual work, and there are solutions in gRPC, which will be introduced in this chapter.

Why can HTTP interfaces be provided at the same time

Crucially, gRPC’s protocol is based on HTTP/2, so applications can provide HTTP/1.1 and gRPC interface services (two different flows) on a single TCP port.

How to Provide HTTP Interface at the Same Time

Detection protocol

if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
    server.ServeHTTP(w, r)
} else {
    mux.ServeHTTP(w, r)
}

Process

  1. Detecting whether the request protocol is HTTP/2
  2. Judging whether the Content-Type is application/grpc(gRPC (default identification bit of grpc)
  3. According to different protocols

gRPC

TLS

In the previous chapters, there was no simple package for ease of display.

In this section, the code needs to be reused and re-packaged. Please refer to:go-grpc-example

directory structure

New simple_http_client and simple_http_server directories are created with the following directory structure:

go-grpc-example
├── client
│   ├── simple_client
│   ├── simple_http_client
│   └── stream_client
├── conf
├── pkg
│   └── gtls
├── proto
├── server
│   ├── simple_http_server
│   ├── simple_server
│   └── stream_server

Server

Create a new server.go in the simple_http_server directory and write the contents of the file:

package main

import (
    "context"
    "log"
    "net/http"
    "strings"

    "github.com/EDDYCJY/go-grpc-example/pkg/gtls"
    pb "github.com/EDDYCJY/go-grpc-example/proto"

    "google.golang.org/grpc"
)

type SearchService struct{}

func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) {
    return &pb.SearchResponse{Response: r.GetRequest() + " HTTP Server"}, nil
}

const PORT = "9003"

func main() {
    certFile := "../../conf/server/server.pem"
    keyFile := "../../conf/server/server.key"
    tlsServer := gtls.Server{
        CertFile: certFile,
        KeyFile:  keyFile,
    }

    c, err := tlsServer.GetTLSCredentials()
    if err != nil {
        log.Fatalf("tlsServer.GetTLSCredentials err: %v", err)
    }

    mux := GetHTTPServeMux()

    server := grpc.NewServer(grpc.Creds(c))
    pb.RegisterSearchServiceServer(server, &SearchService{})

    http.ListenAndServeTLS(":"+PORT,
        certFile,
        keyFile,
        http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
                server.ServeHTTP(w, r)
            } else {
                mux.ServeHTTP(w, r)
            }

            return
        }),
    )
}

func GetHTTPServeMux() *http.ServeMux {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("eddycjy: go-grpc-example"))
    })

    return mux
}
  • Http.NewServeMux: create a new ServeMux, which is essentially a routing table. It implements ServeHTTP by default, so after returning to Handler, you can register pattern and method of processing logic directly through HandleFunc.
  • Http.ListenAndServeTLS: it can be simply understood as a method to provide HTTPS service monitoring. the key protocol to judge forwarding is also here

In fact, after you understand it, you will find it very simple. the core steps are: judgment-> forwarding-> response. We changed the default logic for the first two steps, that’s all.

Client

Create a new client.go in the simple_http_server directory and write the contents of the file:

package main

import (
    "context"
    "log"

    "google.golang.org/grpc"

    "github.com/EDDYCJY/go-grpc-example/pkg/gtls"
    pb "github.com/EDDYCJY/go-grpc-example/proto"
)

const PORT = "9003"

func main() {
    tlsClient := gtls.Client{
        ServerName: "go-grpc-example",
        CertFile:   "../../conf/server/server.pem",
    }
    c, err := tlsClient.GetTLSCredentials()
    if err != nil {
        log.Fatalf("tlsClient.GetTLSCredentials err: %v", err)
    }

    conn, err := grpc.Dial(":"+PORT, grpc.WithTransportCredentials(c))
    if err != nil {
        log.Fatalf("grpc.Dial err: %v", err)
    }
    defer conn.Close()

    client := pb.NewSearchServiceClient(conn)
    resp, err := client.Search(context.Background(), &pb.SearchRequest{
        Request: "gRPC",
    })
    if err != nil {
        log.Fatalf("client.Search err: %v", err)
    }

    log.Printf("resp: %s", resp.GetResponse())
}

Verification

gRPC Client

$ go run client.go 
2018/10/04 14:56:56 resp: gRPC HTTP Server

HTTP/1.1 access

image

Summary

Through this chapter, the function of providing dual services with the same port is ostensibly completed, but in fact, it should deepen the understanding and use of HTTP/2, which is the essence.

Expand

If you have a need, it is toAt the same timeRPC and RESTful JSON API interface, don’t hesitate, click in:GRPC+gRPC Gateway practice

Problem

Do you think this plan is all-powerful, no. Envoy Proxy’s support is not perfect, and it cannot monitor two kinds of traffic on one port at the same time.

References

This series of sample codes

Series catalog