grpc(golang)支持http(gRpc-Gateway)

步骤介绍
  1. 通过openssl生成密钥公钥
  2. 服务端代码,客户端代码
  3. 通过gomod拉取代码所用的库
  4. 编译proto文件,分为两步->go_out
  5. 运行服务端代码
  6. 通过http(post)访问,通过grpc client访问
    官方文档: https://grpc-ecosystem.github.io/grpc-gateway/docs/tutorials/adding_annotations/
目录结构
.
├── Makefile
├── client
│   └── main.go
├── go.mod
├── go.sum
├── keys
│   ├── server.crt
│   └── server.key
├── proto
│   ├── gateway.pb.go
│   ├── gateway.pb.gw.go
│   └── gateway.proto
└── server
    └── main.go
makefile文件

这里特别指定googleapis所在位置

ECOSYSTEM := /pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.12.1/third_party/googleapis
GOPATH :=$(shell go env GOPATH)

step_key:
	openssl genrsa -out keys/server.key 2048
step_crt:	
	openssl req -new -x509 -sha256 -key keys/server.key \
 	-out keys/server.crt -days 36500 \
 	-subj /C=CN/ST=xxx/L=xxx/O=xxx/OU=tech/CN=xxx/
 	
step1:
	go mod init grpc_project
step2:
	go mod tidy

# 编译proto生成.pb.go
step3:
	protoc -I. -I${GOPATH}${ECOSYSTEM} --go_out=plugins=grpc:. proto/gateway.proto
# 编译proto生成.pb.gw.go
step4:
	protoc -I. -I${GOPATH}${ECOSYSTEM} --grpc-gateway_out=logtostderr=true:. proto/gateway.proto

step5:
	cd server && go run main.go

step6:
	curl -X POST -k https://localhost:8488/v1/example/echo -d '{"value": " world"}'

step7:
	cd client &&  go run main.go


gateway.proto
syntax = "proto3";
package gateway;


import "google/api/annotations.proto";

message StringMessage {
    string value = 1;
}


service Gateway {
   rpc Echo(StringMessage) returns (StringMessage) {
       option (google.api.http) = {
           post: "/v1/example/echo"
           body: "*"
       };
   }
   rpc Print(StringMessage) returns (StringMessage) {
        option (google.api.http) = {
            post: "/v1/example/echo2"
            body: "*"
        };
    }
}

server/main.go
package main

import (
	"crypto/tls"
	"io/ioutil"
	"net"
	"net/http"
	"strings"

	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	pb "grpc_project/proto"
	"golang.org/x/net/context"
	"golang.org/x/net/http2"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"
	"fmt"
)

// 定义helloHTTPService并实现约定的接口
type gatewayServer struct{}


var GatewayServer = gatewayServer{}


func (h gatewayServer) Echo(ctx context.Context, in *pb.StringMessage) (*pb.StringMessage, error) {
	resp := new(pb.StringMessage)
	resp.Value = "Hello " + in.Value + "."
	fmt.Println(resp.Value)
	return resp, nil
}
func (h gatewayServer) Print(ctx context.Context, in *pb.StringMessage) (*pb.StringMessage, error) {
	resp := new(pb.StringMessage)
	resp.Value = "Hello Print" + in.Value + "."

	return resp, nil
}

var  crt string= "../keys/server.crt"
var endpoint = "127.0.0.1:8488"
func main() {
	fmt.Printf("server start : %v\n",endpoint)
	
	conn, err := net.Listen("tcp", endpoint)
	if err != nil {
		grpclog.Fatalf("TCP Listen err:%v\n", err)
	}

	// grpc server
	creds, err := credentials.NewServerTLSFromFile(crt, "../keys/server.key")
	if err != nil {
		grpclog.Fatalf("Failed to create server TLS credentials %v", err)
	}
	s := grpc.NewServer(grpc.Creds(creds))
	pb.RegisterGatewayServer(s, GatewayServer)

	// gw server
	ctx := context.Background()
	dcreds, err := credentials.NewClientTLSFromFile(crt, "xxx")
	if err != nil {
		grpclog.Fatalf("Failed to create client TLS credentials %v", err)
	}
	dopts := []grpc.DialOption{grpc.WithTransportCredentials(dcreds)}
	gwmux := runtime.NewServeMux()
	if err = pb.RegisterGatewayHandlerFromEndpoint(ctx, gwmux, endpoint, dopts); err != nil {
		grpclog.Fatalf("Failed to register gw server: %v\n", err)
	}

	// http服务
	mux := http.NewServeMux()
	mux.Handle("/", gwmux)

	srv := &http.Server{
		Addr:      endpoint,
		Handler:   grpcHandlerFunc(s, mux),
		TLSConfig: getTLSConfig(),
	}

	grpclog.Infof("gRPC and https listen on: %s\n", endpoint)

	if err = srv.Serve(tls.NewListener(conn, srv.TLSConfig)); err != nil {
		grpclog.Fatal("ListenAndServe: ", err)
	}

	return
}

func getTLSConfig() *tls.Config {
	cert, _ := ioutil.ReadFile(crt)
	key, _ := ioutil.ReadFile("../keys/server.key")
	var demoKeyPair *tls.Certificate
	pair, err := tls.X509KeyPair(cert, key)
	if err != nil {
		grpclog.Fatalf("TLS KeyPair err: %v\n", err)
	}
	demoKeyPair = &pair
	return &tls.Config{
		Certificates: []tls.Certificate{*demoKeyPair},
		NextProtos:   []string{http2.NextProtoTLS}, // HTTP2 TLS支持
	}
}

// grpcHandlerFunc returns an http.Handler that delegates to grpcServer on incoming gRPC
// connections or otherHandler otherwise. Copied from cockroachdb.
func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler {
	if otherHandler == nil {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			grpcServer.ServeHTTP(w, r)
		})
	}
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
			grpcServer.ServeHTTP(w, r)
		} else {
			otherHandler.ServeHTTP(w, r)
		}
	})
}
client/main.go
package main

import (
	"log"
	"os"
	pb "grpc_project/proto"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

func main() {
	creds, err := credentials.NewClientTLSFromFile("../keys/server.crt", "xxx")
	if err != nil {
		panic(err)
	}

	// 建立连接到gRPC服务
	conn, err := grpc.Dial("127.0.0.1:8488", grpc.WithTransportCredentials(creds),)
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	// 函数结束时关闭连接
	defer conn.Close()
	t := pb.NewGatewayClient(conn)
	// 模拟请求数据
	res := "world"
	if len(os.Args) > 1 {
		res = os.Args[1]
	}

	// 调用gRPC接口
	tr, err := t.Echo(context.Background(), &pb.StringMessage{Value: res})
	if err != nil {
		log.Fatalf("could not greet: %v", err)
	}
	log.Printf("服务端响应: %s", tr.Value)
}

一个简单demo,再贴一个上面代码使用的相关版本

module grpc_project

go 1.13

require (
	github.com/golang/protobuf v1.4.2
	github.com/grpc-ecosystem/grpc-gateway v1.12.1
	golang.org/x/net v0.0.0-20200707034311-ab3426394381
	google.golang.org/genproto v0.0.0-20200726014623-da3ae01ef02d
	google.golang.org/grpc v1.30.0
)
Go语言中,GRPC Gateway是一个工具,它允许你将现有的gRPC服务转换成HTTP API,而无需修改服务端代码。下面是一个简单的gRPCHTTP的示例,我们将创建一个简单的`HelloService` gRPC服务,并通过Gateway将其暴露为RESTful API。 首先,安装必要的依赖: ```sh go get google.golang.org/grpc go get github.com/grpc-ecosystem/grpc-gateway/v2 ``` 然后,假设我们有一个名为`hello.proto`的协议定义文件,内容如下: ```protobuf syntax = "proto3"; package hello; service Hello { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; } ``` 接下来,生成服务器和客户端代码: ```sh protoc -I=$GOPATH/src -I=$SRC_DIR -o $GOPATH/src/hello/hello.pb.go $SRC_DIR/hello.proto protoc -I=$GOPATH/src -I=$SRC_DIR --grpc-gateway_out=logtostderr=true:. $SRC_DIR/hello.proto ``` 创建一个简单的`server/main.go`: ```go package main import ( "context" "fmt" "log" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "google.golang.org/grpc" _ "github.com/grpc-ecosystem/grpc-gateway/v2/examples/helloworld" "golang.org/x/net/context" "hello/hellopb" ) func main() { lis, err := grpc.Listen("0.0.0.0:50051", nil) if err != nil { log.Fatalf("failed to listen: %v", err) } srv := grpc.NewServer() hellopb.RegisterHelloServer(srv, &HelloServiceImpl{}) runtime.ServeGRPC(mux, srv) fmt.Println("Starting server on port 50051") if err := lis.Serve(); err != nil { log.Fatal(err) } } type HelloServiceImpl struct{} func (h *HelloServiceImpl) SayHello(ctx context.Context, req *hellopb.HelloRequest) (*hellopb.HelloReply, error) { return &hellopb.HelloReply{Message: fmt.Sprintf("Hello, %s!", req.Name)}, nil } ``` 最后,在`gateway/gateway.yaml`里配置路由: ```yaml # gateway.yaml openapi: 3.0.2 info: title: Simple Greeting Service version: 1.0.0 servers: - url: http://localhost:8000/ paths: /hello: post: summary: Greet a user operationId: SayHello requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: The user's name responses: '200': description: A successful response with the greeting. content: application/json: schema: type: object properties: message: type: string ``` 运行`server`和`gateway`: ```sh go run server/main.go go run gateway/gateway_main.go ``` 现在你可以访问`http://localhost:8000/hello`并发送POST请求,数据包含`name`字段,服务器会返回一个问候消息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值