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

  • 相关阅读:
    Angular总结一:环境搭建
    适应移动端的地址四级(省市区街道)联动选择
    插入换行符
    自定义input[type="checkbox"]的样式
    使用zepto实现QQ消息左滑删除效果
    windows 下更新 npm 和 node
    [attribute |= value] 与 [attribute ^= value],[attribute ~= value] 与 [attribute *= value] 的联系与区别
    小程序父子组件onLoad和Created之间的问题
    小程序行内元素且有border的情况下,根据文字宽度自动调节元素宽度
    块级元素水平居中
  • 原文地址:https://www.cnblogs.com/majiang/p/14218750.html
Copyright © 2011-2022 走看看