zoukankan      html  css  js  c++  java
  • 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:Goinclude】  ,如果里面没有 可以搜索电脑 , 然后拷贝过去【比如我的是在 D:GoProjectpkgmodcachedownloadgithub.comgrpc-ecosystemgrpc-gateway@vv1.16.0github.comgrpc-ecosystemgrpc-gateway@v1.16.0 hird_partygoogleapisgoogleapi  和 D:GoProjectpkgmodcachedownloadgithub.comgrpc-ecosystemgrpc-gateway@vv1.16.0github.comgrpc-ecosystemgrpc-gateway@v1.16.0protoc-gen-swaggeroptions 这个 路径太长, 我拷贝到D:Goinclude 】

    编译命令:[google/api/annotations.proto 在D:Goinclude里面]

    //我是在hello 目录下执行的
    protoc -ID:Goinclude -I.  --go_out=plugins=grpc:. ./protos/hello.proto
    protoc -ID:Goinclude -I.  --grpc-gateway_out=logtostderr=true:. ./protos/hello.proto
    protoc -ID:Goinclude -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 hird_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.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
    ", 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:GoProjectsrchello>cd server
     
    D:GoProjectsrchelloserver>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:GoProjectsrchello>cd client
     
    D:GoProjectsrchelloclient>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:GoProjectsrcgogrpccertAndgatewayserver>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:GoProjectsrcgogrpccertAndgatewayclient>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

  • 相关阅读:
    Windows JScript 在 游览器 中运行 调试 Shell 文件系统
    autohotkey 符号链接 软连接 symbolink
    软链接 硬链接 测试
    SolidWorks 修改 基准面 标准坐标系
    手机 路径 WebDAV 映射 驱动器
    Win10上手机路径
    explorer 命令行
    单位公司 网络 封锁 屏蔽 深信 AC
    cobbler自动化部署原理篇
    Docker四种网络模式
  • 原文地址:https://www.cnblogs.com/majiang/p/14218750.html
Copyright © 2011-2022 走看看