go语言gRPC系列(三) - 使用grpc-gateway同时提供HTTP和gRPC服务
<ul><li>1. gRPC提供HTTP服务
<ul>
<li>1.1 存在的意义</li>
<li>1.2 代码示例</li>
<li>1.3 使用postman尝试调用</li>
<li>1.4 gRPC客户端代码调用</li>
</ul>
</li>
<li>2. 使用grpc-gateway同时提供HTTP和gRPC服务
<ul>
<li>2.1 前言</li>
<li>2.2 安装</li>
<li>2.3 目录结构</li>
<li>2.4 示例代码
<ul>
<li>2.4.1 编写proto描述文件:proto/hello_http.proto</li>
<li>2.4.2 编译proto</li>
<li>2.4.3 实现HTTP服务端</li>
<li>2.4.4 实现gRPC服务端</li>
<li>2.4.5 实现客户端</li>
</ul>
</li>
<li>2.5 运行并调用</li>
</ul>
</li>
</ul>
<h2 id="1-grpc提供http服务"><span id="head1">1. gRPC提供HTTP服务</span></h2>
<h3 id="11-存在的意义"><span id="head2">1.1 存在的意义</span></h3>
<p>在某些场景下单纯的RPC服务不能满足提供的服务需求的话,还是需要提供HTTP服务作为补充,gRPC一样可以提供<code>HTTP</code>服务。</p>
<ul>
<li><strong>注意:gRPC提供的HTTP接口是基于<code>HTTP 2.0</code>的</strong></li>
</ul>
<h3 id="12-代码示例"><span id="head3">1.2 代码示例</span></h3>
<pre><code class="language-go">package main
import (
"fmt"
"gomicro-quickstart/grpc_server/service"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"log"
"net/http"
)
func main() {
// 1. 引用证书
tls, err := credentials.NewServerTLSFromFile("grpc_server/keys/server.crt", "grpc_server/keys/server_no_password.key")
if err != nil {
log.Fatal("服务端获取证书失败: ", err)
}
// 2. new一个grpc的server,并且加入证书
rpcServer := grpc.NewServer(grpc.Creds(tls))
// 3. 将刚刚我们新建的ProdService注册进去
service.RegisterProdServiceServer(rpcServer, new(service.ProdService))
// 4. 新建一个路由,并传入rpcServer
mux := http.NewServeMux()
mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
fmt.Println(request)
rpcServer.ServeHTTP(writer, request)
})
// 5. 定义httpServer,监听8082
httpServer := http.Server{
Addr: ":8082",
Handler: mux,
}
// 6. 以https形式监听httpServer
httpServer.ListenAndServeTLS("grpc_server/keys/server.crt", "grpc_server/keys/server_no_password.key")
}
</code></pre>
<h3 id="13-使用postman尝试调用"><span id="head4">1.3 使用postman尝试调用</span></h3>
<p>运行上述的代码,然后<code>postman</code>访问<code>8082</code>端口,提示访问这个接口需要http/2协议</p>
<p><img src="http://pic.codepie.fun/picgo/20200815181112.png" alt="" loading="lazy"></p>
<h3 id="14-grpc客户端代码调用"><span id="head5">1.4 gRPC客户端代码调用</span></h3>
<p>针对上一节的客户端调用的代码,我们不需要修改即可以直接访问</p>
<blockquote>
<p>即直接调用protoc产生的go文件中的方法</p>
</blockquote>
<p>我们服务端代码因为打印出了,http request的内容</p>
<p><img src="http://pic.codepie.fun/picgo/20200815181616.png" alt="" loading="lazy"></p>
<p>所以我们查看一下通过客户端调用,会打印出什么,可以看到</p>
<ul>
<li>请求的路径是<code>/service.ProdService/GetProductStock</code>,是<code>{服务名}/{方法名}</code>的格式</li>
<li>协议是:http/2</li>
</ul>
<p><img src="http://pic.codepie.fun/picgo/20200815181959.png" alt="" loading="lazy"></p>
<h2 id="2-使用grpc-gateway同时提供http和grpc服务"><span id="head6">2. 使用grpc-gateway同时提供HTTP和gRPC服务</span></h2>
<h3 id="21-前言"><span id="head7">2.1 前言</span></h3>
<p>某些场景下需要同时要提供<code>REST API服务</code>和<code>gRPC服务</code>,维护两个版本的服务显然不太合理,所以grpc-gateway诞生了。</p>
<p><strong>原理</strong>:通过protobuf的自定义option实现了一个网关,服务端同时开启<strong>gRPC</strong>和<strong>HTTP 1.1</strong>服务,HTTP服务接收客户端请求后转换为grpc请求数据,获取响应后转为json数据返回给客户端。</p>
<p><strong>按照官方的结构说明如图</strong>:</p>
<p><img src="http://pic.codepie.fun/picgo/20200815183001.png" alt="" loading="lazy"></p>
<h3 id="22-安装"><span id="head8">2.2 安装</span></h3>
<p>执行安装以下三个</p>
<pre><code class="language-shell">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
</code></pre>
<h3 id="23-目录结构"><span id="head9">2.3 目录结构</span></h3>
<p>这里用到了google官方Api中的两个proto描述文件,直接拷贝不要做修改,里面定义了<code>protocol buffer</code>扩展<code>的HTTP option</code>,为grpc的http转换提供支持。</p>
<p><img src="http://pic.codepie.fun/picgo/20200815222233.png" alt="" loading="lazy"></p>
<pre><code>|—— hello_http/
|—— client/
|—— main.go // 客户端
|—— server/
|—— main.go // GRPC服务端
|—— server_http/
|—— main.go // HTTP服务端
|—— proto/
|—— google // googleApi http-proto定义
|—— api
|—— annotations.proto
|—— annotations.pb.go
|—— http.proto
|—— http.pb.go
|—— hello_http/
|—— hello_http.proto // proto描述文件
|—— hello_http.pb.go // proto编译后文件
|—— hello_http_pb.gw.go // gateway编译后文件
</code></pre>
<h3 id="24-示例代码"><span id="head10">2.4 示例代码</span></h3>
<h4 id="241-编写proto描述文件protohello_httpproto"><span id="head11">2.4.1 编写proto描述文件:proto/hello_http.proto</span></h4>
<p>在<code>SayHello</code>方法定义中增加了<code>http option, POST</code>方式,路由为<code>/example/echo</code></p>
<pre><code>syntax = "proto3";
package hello_http;
option go_package = "hello_http";
import "google/api/annotations.proto";
// 定义Hello服务
service HelloHTTP {
// 定义SayHello方法
rpc SayHello(HelloHTTPRequest) returns (HelloHTTPResponse) {
// http option
option (google.api.http) = {
post: "/example/echo"
body: "*"
};
}
}
// HelloRequest 请求结构
message HelloHTTPRequest {
string name = 1;
}
// HelloResponse 响应结构
message HelloHTTPResponse {
string message = 1;
}
</code></pre>
<h4 id="242-编译proto"><span id="head12">2.4.2 编译proto</span></h4>
<pre><code class="language-shell">$ cd proto
# 编译google.api
$ protoc -I . --go_out=plugins=grpc,Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor:. google/api/*.proto
# 编译hello_http.proto
$ protoc -I . --go_out=plugins=grpc,Mgoogle/api/annotations.proto=github.com/jergoo/go-grpc-example/proto/google/api:. hello_http/*.proto
# 编译hello_http.proto gateway
$ protoc --grpc-gateway_out=logtostderr=true:. hello_http/hello_http.proto
</code></pre>
<h4 id="243-实现http服务端"><span id="head13">2.4.3 实现HTTP服务端</span></h4>
<pre><code class="language-go">package main
import (
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
gw "github.com/jergoo/go-grpc-example/proto/hello_http"
)
func main() {
// 1. 定义一个context
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// grpc服务地址
endpoint := "127.0.0.1:50052"
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
// HTTP转grpc
err := gw.RegisterHelloHTTPHandlerFromEndpoint(ctx, mux, endpoint, opts)
if err != nil {
grpclog.Fatalf("Register handler err:%v\n", err)
}
grpclog.Println("HTTP Listen on 8080")
http.ListenAndServe(":8080", mux)
}
</code></pre>
<h4 id="244-实现grpc服务端"><span id="head14">2.4.4 实现gRPC服务端</span></h4>
<pre><code class="language-go">package main
import (
"fmt"
"net"
"net/http"
pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入编译生成的包
"golang.org/x/net/context"
"golang.org/x/net/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
)
const (
// Address gRPC服务地址
Address = "127.0.0.1:50052"
)
// 定义helloService并实现约定的接口
type helloService struct{}
// HelloService Hello服务
var HelloService = helloService{}
// SayHello 实现Hello服务接口
func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
resp := new(pb.HelloResponse)
resp.Message = fmt.Sprintf("Hello %s.", in.Name)
return resp, nil
}
func main() {
listen, err := net.Listen("tcp", Address)
if err != nil {
grpclog.Fatalf("failed to listen: %v", err)
}
// 实例化grpc Server
s := grpc.NewServer()
// 注册HelloService
pb.RegisterHelloServer(s, HelloService)
grpclog.Println("Listen on " + Address)
s.Serve(listen)
}
</code></pre>
<h4 id="245-实现客户端"><span id="head15">2.4.5 实现客户端</span></h4>
<pre><code class="language-go">package main
import (
pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入proto包
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
)
const (
// Address gRPC服务地址
Address = "127.0.0.1:50052"
)
func main() {
// 连接
conn, err := grpc.Dial(Address, grpc.WithInsecure())
if err != nil {
grpclog.Fatalln(err)
}
defer conn.Close()
// 初始化客户端
c := pb.NewHelloClient(conn)
// 调用方法
req := &pb.HelloRequest{Name: "gRPC"}
res, err := c.SayHello(context.Background(), req)
if err != nil {
grpclog.Fatalln(err)
}
grpclog.Println(res.Message)
}
</code></pre>
<h3 id="25-运行并调用"><span id="head16">2.5 运行并调用</span></h3>
<p>依次开启gRPC服务端和HTTP服务端</p>
<pre><code class="language-shel">$ cd hello_http/server && go run main.go
Listen on 127.0.0.1:50052
$ cd hello_http/server_http && go run main.go
HTTP Listen on 8080
</code></pre>
<p>然后调用gRPC的客户端</p>
<pre><code>$ cd hello_http/client && go run main.go
Hello gRPC.
# HTTP 请求
$ curl -X POST -k http://localhost:8080/example/echo -d '{"name": "gRPC-HTTP is working!"}'
{"message":"Hello gRPC-HTTP is working!."}
</code></pre><br><br>
来源:https://www.cnblogs.com/baoshu/p/13510854.html
頁:
[1]