go grpc protobuf restfull api swagger 安装 及 各种用法示例

目录

grpc安装

安装protobuf

go hello 示例

如何即提供 grpc 又提供 RESTfull 接口?

安装

编写proto文件

生成rpc对应go文件

gw.go

运行

试试看命令

swagger

OpenAPI规范

文档已经生成了,通过 swagger-ui 展现

运行 

借助 go-bindata 把 swagger 和 json 文件 转换为一份 go 源码 


grpc安装

mkdir -p $GOPATH/src/google.golang.org
cd $GOPATH/src/google.golang.org
git clone https://github.com/grpc/grpc-go.git grpc

 

安装依赖包, 注意路径必须完全对的上
cd $GOPATH/src/golang.org/x
git clone https://github.com/golang/net.git
git clone https://github.com/golang/text.git
cd $GOPATH/src/google.golang.org
git clone https://github.com/google/go-genproto.git
mv go-genproto/ genproto

安装protobuf

按照github提示

For non-C++ users, the simplest way to install the protocol compiler is to

download a pre-built binary from our release page:

 

[https://github.com/google/protobuf/releases]

centos 下的话

要先安装下依赖库 sudo yum install libatomic

然后把下载到的 bin  include 拷贝到合适的目录就行

 

我下了个 all的 protobuf-all-3.6.0.tar.gz

还以为已经编译好了,没想是份源码

那就练练手,源码构建个出来吧

切换到src目录下 打开 README.md

 

sudo apt-get install autoconf automake libtool curl make g++ unzip

貌似我原来就装好了

 

    $ ./configure
    $ make
    $ make check
    $ sudo make install
    $ sudo ldconfig # refresh shared library cache.

make check 非常慢 耐心等待


装好后可以看看版本号

~/go/gopath $ protoc --version
libprotoc 3.6.0 

安装 protoc-gen-go

go get github.com/golang/protobuf
go install github.com/golang/protobuf/protoc-gen-go/





go hello 示例

提供2个接口  一个SayHello 一个 Ls (ls 当前目录)

syntax = "proto3";
 
package hello;
 
// The greeting service definition.
service Hello {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}
 
// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}
 
// The response message containing the greetings
message HelloReply {
  string message = 1;
}

然后新建 hello.go 

//go:generate protoc -I . --go_out=plugins=grpc:. hello.proto
package hello

之后 go generate 生成 hello.pb.go  go接口文件

 

这个是 ser.go

package main
  
import (
    log "github.com/sirupsen/logrus"
    "net"
  
    "golang.org/x/net/context"
    "google.golang.org/grpc"
    pb "hellogw"
)
  
const (
    port = ":50051"
)
  
type server struct {}
  
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
  
func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatal("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterHelloServer(s, &server{})
    s.Serve(lis)
}

 

下面是 client.go

ppackage main
  
import (
    log "github.com/sirupsen/logrus"
    "os"
  
    "golang.org/x/net/context"
    "google.golang.org/grpc"
    pb "hellogw"
)
  
const (
    address     = "localhost:50051"
    defaultName = "world wjs"
)
  
func main() {
    conn, err := grpc.Dial(address, grpc.WithInsecure())
    if err != nil {
        log.Fatal("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewHelloClient(conn)
  
    name := defaultName
    if len(os.Args) >1 {
        name = os.Args[1]
    }
    r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
    if err != nil {
        log.Fatal("could not greet: %v", err)
    }
    log.Printf("%s", r.Message)
}

 

---------------------------------------------------------------华丽的分割线---------------------------------------------------------------

如何即提供 grpc 又提供 RESTfull 接口?

借助 https://github.com/grpc-ecosystem/grpc-gateway,简单的说就是有一个网关服务器负责转化和代理转发。

安装

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

编写proto文件

hellogateway.proto

syntax = "proto3";
 
package hello;

import "google/api/annotations.proto";
 
// The greeting service definition.
service Hello {
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
    post: "/v1/example/echo"
    body: "*"
    };
  }
}
 
// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}
 
// The response message containing the greetings
message HelloReply {
  string message = 1;
}

关键性改动如下

import "google/api/annotations.proto";
rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
    post: "/v1/example/echo"
    body: "*"
    };
  }

生成rpc对应go文件

生成 hello.go 文件,内容如下。 这个文件啥代码没有,就给 go generate 留了个命令:执行 gen.sh 

~/go/gopath/src/hellogw $ cat hello.go
//go:generate sh gen.sh

package hello

编写 gen.sh

#!/usr/bin/env bash

# Since GOPATH can be a path, we can't just use it as a variable.  Split it up 
# to the various paths, and append the subpaths.
GOSUBPATHS="/src:/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis"
GOPATHLIST=""
OIFS=$IFS
IFS=':' 
for GOBASEPATH in $GOPATH; do
    for GOSUBPATH in $GOSUBPATHS; do
    	if [ -e ${GOBASEPATH}${GOSUBPATH} ]; then
        	GOPATHLIST="${GOPATHLIST} -I${GOBASEPATH}${GOSUBPATH}"
        fi
    done
done
IFS=$OIFS

# generate the gRPC code
protoc -I/usr/local/include -I. ${GOPATHLIST} --go_out=plugins=grpc:. \
    hellogateway.proto

# generate the JSON interface code
protoc -I/usr/local/include -I. ${GOPATHLIST} --grpc-gateway_out=logtostderr=true:. \
    hellogateway.proto


# generate the swagger definitions
# protoc -I/usr/local/include -I. ${GOPATHLIST} --swagger_out=logtostderr=true:./swagger \
#    hellogateway.proto

# merge the swagger code into one file
# go run swagger/main.go swagger > ../static/swagger/api.swagger.json

命令行下执行  go generate 即可生成

~/go/gopath/src/hellogw $ go generate
~/go/gopath/src/hellogw $ ls
gen.sh   hello.go   hellogateway.pb.go     hellogateway.proto
hellogateway.pb.gw.go  

gw.go

package main
 
import (
    "net/http"
 
    "github.com/golang/glog"
    "github.com/grpc-ecosystem/grpc-gateway/runtime"
    "golang.org/x/net/context"
    "google.golang.org/grpc"
 
    gw "hellogw"
)
 
func run() error {
    ctx := context.Background()
    ctx, cancel := context.WithCancel(ctx)
    defer cancel()

    mux := runtime.NewServeMux()
    opts := []grpc.DialOption{grpc.WithInsecure()}
    err := gw.RegisterHelloHandlerFromEndpoint(ctx, mux, "localhost:50051", opts)
    if err != nil {
        return err
    }
 
    return http.ListenAndServe(":8080", mux)
}
 
func main() {
    defer glog.Flush()
 
    if err := run(); err != nil {
        glog.Fatal(err)
    }
}

运行

go run ser.go &
go run gw.go

试试看命令

curl -X POST -k http://localhost:8080/v1/example/echo -d '{"name": " world wjs"}'

{"message":"Hello  world wjs"}

当然也可以写个小小的go程序 

package main

import (
	"net/http"
	"encoding/json"
	"bytes"
	
	log "github.com/sirupsen/logrus"
	pb "hellogw"	
)

const (
	url = "http://localhost:8080/v1/example/echo"
)

func SayHello() error {
	msg := pb.HelloRequest{Name:"wjs"}
	js, err := json.Marshal(msg)
	if err != nil {
		return err
	}
	log.Printf("%q", js)
	
	req,_ := http.NewRequest("POST", url, 	bytes.NewReader(js))
	res,err := http.DefaultClient.Do(req)
	defer res.Body.Close()

	reply := pb.HelloReply{}
	err = json.NewDecoder(res.Body).Decode(&reply)
	log.Print(reply.Message, err)
	return err
}

func main() {
	SayHello()
}

 

前面的方法要运行2个服务器,好麻烦啊

我把它们写到一个

package main

import (
	log "github.com/sirupsen/logrus"
	"net"
	"net/http"

	"github.com/golang/glog"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	pb "hellogw"
)

const (
	port = ":50051"
)

type server struct{}

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

func run() error {
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	mux := runtime.NewServeMux()
	opts := []grpc.DialOption{grpc.WithInsecure()}
	err := pb.RegisterHelloHandlerFromEndpoint(ctx, mux, "localhost"+port, opts)
	if err != nil {
		return err
	}

	return http.ListenAndServe(":8080", mux)
}

func main() {
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatal("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterHelloServer(s, &server{})
	go s.Serve(lis)

	defer glog.Flush()

	if err := run(); err != nil {
		glog.Fatal(err)
	}
}

8080 端口过于通用,可能被别的程序干扰,遇到莫名问题的时候 换成 8777 之类的试试

 

以下介绍 是到  Grpc+Grpc Gateway实践三 Swagger了解一下 抄的

swagger

Swagger是全球最大的OpenAPI规范(OAS)API开发工具框架,支持从设计和文档到测试和部署的整个API生命周期的开发

Swagger是目前最受欢迎的RESTful Api文档生成工具之一,主要的原因如下

  • 跨平台、跨语言的支持
  • 强大的社区
  • 生态圈 Swagger Tools(Swagger Editor、Swagger Codegen、Swagger UI ...)
  • 强大的控制台

同时grpc-gateway也支持Swagger

 

OpenAPI规范

OpenAPI规范是Linux基金会的一个项目,试图通过定义一种用来描述API格式或API定义的语言,来规范RESTful服务开发过程。OpenAPI规范帮助我们描述一个API的基本信息,比如:

  • 有关该API的一般性描述
  • 可用路径(/资源)
  • 在每个路径上的可用操作(获取/提交...)
  • 每个操作的输入/输出格式

目前V2.0版本的OpenAPI规范(也就是SwaggerV2.0规范)已经发布并开源在github上。该文档写的非常好,结构清晰,方便随时查阅。

注:OpenAPI规范的介绍引用自原文

 

安装 swagger, 文章前面部分似乎已经装好了

go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger

修改 gen.sh (参考文章前面部分的 gen.sh), 在尾部添加

# generate the swagger definitions
protoc -I/usr/local/include -I. ${GOPATHLIST} --swagger_out=logtostderr=true:. \
    hellogateway.proto

生成  swagger 文档

在 gen.sh 目录下 执行 

go generate

会在同目录下生成 hellogateway.swagger.json

 

文档已经生成了,通过 swagger-ui 展现

下载

https://github.com/swagger-api/swagger-ui/releases

我下了个当前最新版本

其实也可以到 https://github.com/swagger-api/swagger-ui/tree/master/dist 里拿。

下载后,我在工程目录里新建了 swagger 目录,把下载好的这些文件都放了进去,并把 hellogateway.swagger.json 也放了进去

~/gol/grpc_rest/swagger $ ls
favicon-16x16.png          swagger-ui-bundle.js.map
favicon-32x32.png          swagger-ui-standalone-preset.js
hellogateway.swagger.json  swagger-ui-standalone-preset.js.map
index.html                 swagger-ui.css
index.html~                swagger-ui.css.map
oauth2-redirect.html       swagger-ui.js
swagger-ui-bundle.js       swagger-ui.js.map

编辑 index.html

把 url 那行改成我们刚刚生成的 json 文件

        url: "hellogateway.swagger.json",

 

修改 gw.go

package main
 
import (
	log "github.com/sirupsen/logrus"
	"net"
	"net/http"
 
	"github.com/golang/glog"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	pb "hellogw"
)

const (
	rpcPort = ":50051"
	swaggerPort = ":8100"
)
 
type server struct{}
 
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

func serveSwaggerUI(mux *http.ServeMux) {
	dir := "swagger"
	mux.Handle("/api/", http.StripPrefix("/api/", http.FileServer(http.Dir(dir))))
	log.Printf("Serving %s on HTTP port: %s\n", dir, swaggerPort)
}

func swaggerServer() error {

	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
 
	gwmux := runtime.NewServeMux()
	opts := []grpc.DialOption{grpc.WithInsecure()}
	err := pb.RegisterHelloHandlerFromEndpoint(ctx, gwmux, "localhost"+rpcPort, opts)
	if err != nil {
		return err
	}

	mux := http.NewServeMux()
	mux.Handle("/", gwmux)
	serveSwaggerUI(mux)
	return http.ListenAndServe(swaggerPort, mux)
}
 
func main() {
	lis, err := net.Listen("tcp", rpcPort)
	if err != nil {
		log.Fatal("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterHelloServer(s, &server{})
	go s.Serve(lis)
 
	defer glog.Flush()

	swaggerServer()
}

 

运行 

go run gw.go

在浏览器里打开,   把 ip 换成自己的。  本地的话就用 localhost

http://192.168.255.129:8100/api/

大功告成

go grpc protobuf restfull api swagger 安装 及 各种用法示例_第1张图片

 

借助 go-bindata 把 swagger 和 json 文件 转换为一份 go 源码 

go-bindata 用法

gw.go 头部加入   调整下里面的路径

//go:generate go-bindata -prefix static/ -pkg static -o internal/static/static_gen.go static/...

修改 gw.go 的 serveSwaggerUI 函数

func serveSwaggerUI(mux *http.ServeMux) {
	fileServer := http.FileServer(&assetfs.AssetFS{
        Asset:    static.Asset,
        AssetDir: static.AssetDir,
        Prefix:   "swagger",
    })
	prefix := "/api/"
    mux.Handle(prefix, http.StripPrefix(prefix, fileServer))
}

这部分写得比较简洁,下面链接有完整工程

https://download.csdn.net/download/wangjunsheng/11584429

 

rpc 和 restfull api 共用一个端口

参考 grpc 和 restfull 共用一个端口

//go:generate go-bindata -prefix static/ -pkg static -o internal/static/static_gen.go static/...
package main
 
import (
	log "github.com/sirupsen/logrus"
	"io/ioutil"
	"crypto/tls"
	"crypto/x509"
	"net/http"
	"strings"
 
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"github.com/elazarl/go-bindata-assetfs"
	"github.com/tmc/grpc-websocket-proxy/wsproxy"
	"google.golang.org/grpc/credentials"
	
	pb "learn-swagger/api"
	"learn-swagger/internal/static"
)

const (
	port = ":8100"
	crtFile = "server.crt"
	keyFile = "server.key"
)
 
type server struct{}
 
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

func serveSwaggerUI(mux *http.ServeMux) {
	fileServer := http.FileServer(&assetfs.AssetFS{
        Asset:    static.Asset,
        AssetDir: static.AssetDir,
        Prefix:   "swagger",
    })
	prefix := "/api/"
    mux.Handle(prefix, http.StripPrefix(prefix, fileServer))
}

func getApiHandle(ctx context.Context) (http.Handler, error) {
	rpcmux := runtime.NewServeMux()
	
	b, err := ioutil.ReadFile(crtFile)
	if err != nil {
		return nil, err
	}
	cp := x509.NewCertPool()
	if !cp.AppendCertsFromPEM(b) {
		return nil, err
	}
	opts := []grpc.DialOption{grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
		// given the grpc-gateway is always connecting to localhost, does
		// InsecureSkipVerify=true cause any security issues?
		InsecureSkipVerify: true,
		RootCAs:            cp,
	}))}
 
	err = pb.RegisterHelloHandlerFromEndpoint(ctx, rpcmux, "localhost"+port, opts)
	if err != nil {
		return nil, err
	}

	mux := http.NewServeMux()
	mux.Handle("/", rpcmux)
	serveSwaggerUI(mux)
	
	return mux,nil
}

// 自己生成的证书会被显示不安全证书
// 试试导入证书的时候, 导入2次,一次在个人下面,一次在受信任的颁发机构下
func main() {
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	
	api := grpc.NewServer()
	pb.RegisterHelloServer(api, &server{})
	
	var apiHttp http.Handler
	
	handler := http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
		if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
			api.ServeHTTP(w, r)
		} else {
			if apiHttp  == nil {
				w.WriteHeader(http.StatusNotImplemented)
				return
			}
			apiHttp.ServeHTTP(w, r)
		}
	})
	
	apimux,_ := getApiHandle(ctx)
	apiHttp = wsproxy.WebsocketProxy(apimux)

	err := http.ListenAndServeTLS(port, crtFile, keyFile, handler)
	if err != nil {
		log.Error(err)
	}
}

 

你可能感兴趣的:(go)