• go语言gRPC系列(三)


    1. gRPC提供HTTP服务

    1.1 存在的意义

    在某些场景下单纯的RPC服务不能满足提供的服务需求的话,还是需要提供HTTP服务作为补充,gRPC一样可以提供HTTP服务。

    • 注意:gRPC提供的HTTP接口是基于HTTP 2.0

    1.2 代码示例

    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")
    }
    
    

    1.3 使用postman尝试调用

    运行上述的代码,然后postman访问8082端口,提示访问这个接口需要http/2协议

    1.4 gRPC客户端代码调用

    针对上一节的客户端调用的代码,我们不需要修改即可以直接访问

    即直接调用protoc产生的go文件中的方法

    我们服务端代码因为打印出了,http request的内容

    所以我们查看一下通过客户端调用,会打印出什么,可以看到

    • 请求的路径是/service.ProdService/GetProductStock,是{服务名}/{方法名}的格式
    • 协议是:http/2

    2. 使用grpc-gateway同时提供HTTP和gRPC服务

    2.1 前言

    某些场景下需要同时要提供REST API服务gRPC服务,维护两个版本的服务显然不太合理,所以grpc-gateway诞生了。

    原理:通过protobuf的自定义option实现了一个网关,服务端同时开启gRPCHTTP 1.1服务,HTTP服务接收客户端请求后转换为grpc请求数据,获取响应后转为json数据返回给客户端。

    按照官方的结构说明如图

    2.2 安装

    执行安装以下三个

    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
    

    2.3 目录结构

    这里用到了google官方Api中的两个proto描述文件,直接拷贝不要做修改,里面定义了protocol buffer扩展的HTTP option,为grpc的http转换提供支持。

    |—— 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编译后文件
    

    2.4 示例代码

    2.4.1 编写proto描述文件:proto/hello_http.proto

    SayHello方法定义中增加了http option, POST方式,路由为/example/echo

    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;
    }
    

    2.4.2 编译proto

    $ 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
    

    2.4.3 实现HTTP服务端

    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
    ", err)
        }
    
        grpclog.Println("HTTP Listen on 8080")
        http.ListenAndServe(":8080", mux)
    }
    

    2.4.4 实现gRPC服务端

    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)
    }
    

    2.4.5 实现客户端

    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)
    }
    

    2.5 运行并调用

    依次开启gRPC服务端和HTTP服务端

    $ 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
    

    然后调用gRPC的客户端

    $ 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!."}
    
  • 相关阅读:
    短信编码总结
    在Linux下用C语言实现短信收发
    sshd_config配置详解
    SSH的通讯和认证
    linux安装tacacs+服务器
    Tacacs+认证详细调研
    伪分布配置完成启动jobtracker和tasktracker没有启动
    Hadoop学习记录(7)|Eclipse远程调试Hadoop
    Hadoop学习记录(6)|Eclipse安装Hadoop 插件
    Hadoop学习记录(5)|集群搭建|节点动态添加删除
  • 原文地址:https://www.cnblogs.com/baoshu/p/13510854.html
Copyright © 2020-2023  润新知