go學習筆記 grpc-gateway和swagger


有關GPRC 的創建 大家 請參考 go學習筆記 Windows Go 1.15 以上版本的 GRPC 通信【自簽CA和雙向認證】,本文同樣會用上文創建的證書。【注意我的環境是win7+go1.15.6】

1:將REST注釋添加到API定義,我們必須安裝grpc-gateway和swagger文檔生成器插件

go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
go get -u github.com/golang/protobuf/protoc-gen-go
go get -u -v github.com/golang/protobuf/protoc-gen-g

先看看我文件夾的結構吧:

2創建 /api/proto/v1/todo.protor然后生成 ,你可以在這里獲得proto語言規范:https://developers.google.com/protocol-buffers/docs/proto3,這里增加了swagger的配置,這個配置的作用是讓swagger把遠程調用配置成http,如果沒有這些配置,swagger默認的遠程調用就是https的,本文的gRPC-Gateway提供的是http服務,所以要加上這些配置

syntax = "proto3";
 
package protos;
 
 
// 1 導入 gateway 相關的proto 以及 swagger 相關的 proto
import "google/api/annotations.proto";
import "protoc-gen-swagger/options/annotations.proto";
 
// 2 定義 swagger 相關的內容
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
  info: {
        title: "grpc gateway sample";
        version: "1.0";    
        license: {
            name: "MIT";            
        };
  };
  schemes: HTTP;
  consumes: "application/json";
  produces: "application/json";
};
 
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {
      // 3 標識接口路由
      option (google.api.http) = {
                post: "/hello_world"
                body: "*"
            };
  }
}
 
message HelloRequest {
  string name = 1;
}
 
message HelloReply {
  string message = 1;
}

生成命令如下,

1.如果編譯遇到Interpreting non ascii codepoint 194. 類似錯誤【說白了就是有肉眼看不見的一些東西】,我用的是VSCode,所以安裝插件 vscode-proto3 及其依賴的 Clang-Format 【如果存在報錯 clang-format not installed,需要在系統里安裝 clang-format】安裝后一眼就可以看出來了

2.如果編譯遇到 google/api/annotations.proto: File not found.之類問題,需要指定protoc的include 文件夾路徑【我把protoc-3.14.0-win64.zip 解壓到D:\Go ,所以我的是D:\Go\include】  ,如果里面沒有 可以搜索電腦 , 然后拷貝過去【比如我的是在 D:\GoProject\pkg\mod\cache\download\github.com\grpc-ecosystem\grpc-gateway\@v\v1.16.0\github.com\grpc-ecosystem\grpc-gateway@v1.16.0\third_party\googleapis\google\api  和 D:\GoProject\pkg\mod\cache\download\github.com\grpc-ecosystem\grpc-gateway\@v\v1.16.0\github.com\grpc-ecosystem\grpc-gateway@v1.16.0\protoc-gen-swagger\options 這個 路徑太長, 我拷貝到D:\Go\include 】

編譯命令:[google/api/annotations.proto 在D:\Go\include里面]

//我是在hello 目錄下執行的
protoc -ID:\Go\include -I.  --go_out=plugins=grpc:. ./protos/hello.proto
protoc -ID:\Go\include -I.  --grpc-gateway_out=logtostderr=true:. ./protos/hello.proto
protoc -ID:\Go\include -I.  --swagger_out=logtostderr=true:.         ./protos/hello.proto

如果你的VScode 安裝了 swagger viewer版本,使用快捷鍵Shift + Alt + P可以啟動預覽模式,一邊編寫配置文件,一邊查看效果,還可以進行語法檢查

 3..創建hello/server/services/services.go 

package services
 
import (
    "context"
    "fmt"
    pb "hello/protos"
)
 
type server struct{}
 
func NewServer() *server {
    return &server{}
}
 
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    fmt.Println("request: ", in.Name)
    return &pb.HelloReply{Message: "hello, " + in.Name}, nil
}

4.先下載swagger ui的靜態文件放到 hello\third_party下面。 https://github.com/swagger-api/swagger-ui  把這些文件打包成go文件。

go get -u github.com/jteeuwen/go-bindata/...     //安裝
go-bindata --nocompress -pkg swagger -o gateway/swagger/datafile.go third_party/swagger-ui/...
go get github.com/elazarl/go-bindata-assetfs/...

5.准備網關/hello/gateway/gateway.go

package gateway
 
import (
    "fmt"
    "net/http"
    "path"
    "strings"
 
    assetfs "github.com/elazarl/go-bindata-assetfs"
 
    swagger "hello/gateway/swagger"
    gw "hello/protos"
 
    "github.com/grpc-ecosystem/grpc-gateway/runtime"
    "golang.org/x/net/context"
    "google.golang.org/grpc"
)
 
///
func HttpRun(gprcPort, httpPort string) error {
    ctx := context.Background()
    ctx, cancel := context.WithCancel(ctx)
    defer cancel()
 
    gwmux, err := newGateway(ctx, gprcPort)
    if err != nil {
        panic(err)
    }
 
    mux := http.NewServeMux()
    mux.Handle("/", gwmux)
    mux.HandleFunc("/swagger/", serveSwaggerFile)
    serveSwaggerUI(mux)
 
    fmt.Println("grpc-gateway listen on localhost" + httpPort)
    return http.ListenAndServe(httpPort, mux)
}
 
func newGateway(ctx context.Context, gprcPort string) (http.Handler, error) {
    opts := []grpc.DialOption{grpc.WithInsecure()}
 
    gwmux := runtime.NewServeMux()
    if err := gw.RegisterGreeterHandlerFromEndpoint(ctx, gwmux, gprcPort, opts); err != nil {
        return nil, err
    }
 
    return gwmux, nil
}
 
func serveSwaggerFile(w http.ResponseWriter, r *http.Request) {
    if !strings.HasSuffix(r.URL.Path, "swagger.json") {
        fmt.Printf("Not Found: %s\r\n", r.URL.Path)
        http.NotFound(w, r)
        return
    }
 
    p := strings.TrimPrefix(r.URL.Path, "/swagger/")
    p = path.Join("../protos", p)
 
    fmt.Printf("Serving swagger-file: %s\r\n", p)
 
    http.ServeFile(w, r, p)
}
 
func serveSwaggerUI(mux *http.ServeMux) {
    fileServer := http.FileServer(&assetfs.AssetFS{
        Asset:    swagger.Asset,
        AssetDir: swagger.AssetDir,
        Prefix:   "third_party/swagger-ui",
    })
    prefix := "/swagger-ui/"
    mux.Handle(prefix, http.StripPrefix(prefix, fileServer))
}

6.准備f服務 /hello/server/main.go

package main
 
import (
    "fmt"
    pb "hello/protos"
    "hello/server/services"
    "log"
    "net"
    "hello/gateway"
    "google.golang.org/grpc"
)
 
func main() {
    grpcPort := ":9090"
    httpPort := ":8080"
    lis, err := net.Listen("tcp", grpcPort)
 
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    go gateway.HttpRun(grpcPort, httpPort)
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, services.NewServer())
    fmt.Println("rpc services started, listen on localhost" + grpcPort)
    s.Serve(lis)
}

運行結果如下:

D:\GoProject\src\hello>cd server
 
D:\GoProject\src\hello\server>go run main.go
rpc services started, listen on localhost:9090
grpc-gateway listen on localhost:8080

訪問 http://localhost:8080/swagger/hello.swagger.json 正常

7.創建客戶端的main.go【分為 gprc 和http 部分】hello/client/main.go

package main
 
import (
    "context"
    "encoding/json"
    "fmt"
    pb "hello/protos"
    sv "hello/server/services"
    "io/ioutil"
    "net/http"
    "strings"
 
    "google.golang.org/grpc"
)
 
func main() {
    req := pb.HelloRequest{Name: "gavin"}
    // GRPC
    conn, err := grpc.Dial("localhost:9090", grpc.WithInsecure())
    if err != nil {
        fmt.Println(err)
        return
    }
    defer conn.Close()
    c := sv.NewServer()
    r, err := c.SayHello(context.Background(), &req)
    if err != nil {
        fmt.Println(err)
    }
    // 打印返回值
    fmt.Println(r)
    fmt.Println("http Start......................")
    //http
    requestByte, _ := json.Marshal(req)
    request, _ := http.NewRequest("POST", "http://localhost:8080/hello_world", strings.NewReader(string(requestByte)))
    request.Header.Set("Content-Type", "application/json")
    response, _ := http.DefaultClient.Do(request)
 
    bodyBytes, err := ioutil.ReadAll(response.Body)
    defer response.Body.Close()
    fmt.Println(string(bodyBytes))
}

運行效果:

D:\GoProject\src\hello>cd client
 
D:\GoProject\src\hello\client>go run main.go
request:  gavin
message:"hello, gavin"
http Start......................
{"message":"hello, gavin"}

--------------------------------------------------------------------------------------------------------------------

8.開啟grpc的雙向認證

受到 asp.ner core 5.0 Grpc雙向認證 和 restful api包裝 外加swagger啟用【VSCode創建】,決定多開一個 端口 用於grpc tsl的雙向認證, 修改后的server/main.go代碼如下:

package main

import (
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "hello/gateway"
    pb "hello/protos"
    "hello/server/services"
    "io/ioutil"
    "log"
    "net"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    "google.golang.org/grpc/reflection"
)

func main() {
    //grpc
    grpctslPort := ":9090"
    grpcPort := ":8081"
    httpPort := ":8080"
    ///grpc tsl 用於雙向認證
    go GrpcTslServer(grpctslPort)

    ///普通的主要是便於gateway使用
    lis, err := net.Listen("tcp", grpcPort)

    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    //gateway
    go gateway.HttpRun(grpcPort, httpPort)
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, services.NewServer())
    fmt.Println("rpc services started, listen on localhost" + grpcPort)
    s.Serve(lis)
}
func GrpcTslServer(grpctslPort string) error {
    //證書
    cert, _ := tls.LoadX509KeyPair("../cert/server.pem", "../cert/server.key")
    certPool := x509.NewCertPool()
    ca, _ := ioutil.ReadFile("../cert/ca.pem")
    certPool.AppendCertsFromPEM(ca)

    creds := credentials.NewTLS(&tls.Config{
        Certificates: []tls.Certificate{cert},
        ClientAuth:   tls.RequireAndVerifyClientCert,
        ClientCAs:    certPool,
    })
    sl := grpc.NewServer(grpc.Creds(creds)) // 創建GRPC

    pb.RegisterGreeterServer(sl, services.NewServer())

    reflection.Register(sl) // 在GRPC服務器注冊服務器反射服務
    listener, err := net.Listen("tcp", grpctslPort)
    if err != nil {
        fmt.Println(err)
        return err
    }
    fmt.Println("rpc tsl services started, listen on localhost" + grpctslPort)
    return sl.Serve(listener)

}

client/main.go如下:

package main

import (
    "context"
    "crypto/tls"
    "crypto/x509"
    "encoding/json"
    "fmt"
    pb "hello/protos"
    sv "hello/server/services"
    "io/ioutil"
    "net/http"
    "strings"
    "time"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
)

func main() {
    ///grpc client
    req := pb.HelloRequest{Name: "gavin"}
    cert, err := tls.LoadX509KeyPair("../certs/client.pem", "../certs/client.key")
    certPool := x509.NewCertPool()
    ca, _ := ioutil.ReadFile("ca.pem")
    certPool.AppendCertsFromPEM(ca)

    creds := credentials.NewTLS(&tls.Config{
        Certificates: []tls.Certificate{cert},
        ServerName:   "localhost",
        RootCAs:      certPool,
    })
    // GRPC
    conn, err := grpc.Dial("localhost:9090", grpc.WithTransportCredentials(creds))
    if err != nil {
        fmt.Println(err)
        return
    }
    defer conn.Close()
    c := sv.NewServer()
    r, err := c.SayHello(context.Background(), &req)
    if err != nil {
        fmt.Println(err)
    }
    // 打印返回值
    fmt.Println(r)
    fmt.Println("http Start......................")
    //http
    requestByte, _ := json.Marshal(req)
    client := http.Client{Timeout: 15 * time.Second}
    resp, err := client.Post("http://localhost:8080/hello_world", "application/json", strings.NewReader(string(requestByte)))

    bodyBytes, err := ioutil.ReadAll(resp.Body)
    defer resp.Body.Close()
    fmt.Println(string(bodyBytes))
}

運行效果:

D:\GoProject\src\gogrpccertAndgateway\server>go run main.go
rpc tsl services started, listen on localhost:9090
rpc services started, listen on localhost:8081
grpc-gateway listen on localhost:8080
request:  gavin
D:\GoProject\src\gogrpccertAndgateway\client>go run main.go
request:  gavin
message:"hello, gavin"
http Start......................
{"message":"hello, gavin"}

源碼下載 https://download.csdn.net/download/dz45693/13995056   https://github.com/dz45693/gogrpccertAndgateway.git

參考:

https://www.cnblogs.com/catcher1994/archive/2004/01/13/11869532.html

http://weekly.dockerone.com/article/10028

https://segmentfault.com/a/1190000013513469

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM