zoukankan      html  css  js  c++  java
  • gRPC基础

    RPC 框架原理

    RPC 框架的目标就是让远程服务调用更加简单、透明,RPC 框架负责屏蔽底层的传输方式(TCP 或者 UDP)、序列化方式(XML/Json/ 二进制)和通信细节。服务调用者可以像调用本地接口一样调用远程的服务提供者,而不需要关心底层通信细节和调用过程。

    业界主流的 RPC 框架整体上分为三类:

    • 支持多语言的 RPC 框架,比较成熟的有 Google 的 gRPC、facebook的Apache、Thrift;
    • 只支持特定语言的 RPC 框架,例如新浪微博的 Motan;
    • 支持服务治理等服务化特性的分布式服务框架,其底层内核仍然是 RPC 框架, 例如阿里的 Dubbo。

    gRPC是什么

    gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C## 支持.

    gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

    安全

    HTTP2 规范当使用 TLS 时强制使用 TLS 1.2 及以上的版本,并且在部署上对允许的密码施加一些额外的限制以避免已知的比如需要 SNI 支持的问题。并且期待 HTTP2 与专有的传输安全机制相结合,这些传输机制的规格说明不能提供有意义的建议。

    gRPC使用

    使用gRPC, 我们可以一次性的在一个.proto文件中定义服务并使用任何支持它的语言去实现客户端和服务端,反过来,它们可以应用在各种场景中,从Google的服务器到你自己的平板电脑—— gRPC帮你解决了不同语言及环境间通信的复杂性。使用protocol buffers还能获得其他好处,包括高效的序列号,简单的IDL以及容易进行接口更新。总之一句话,使用gRPC能让我们更容易编写跨语言的分布式代码。

    • 通过一个 protocol buffers 模式,定义一个简单的带有 Hello World 方法的 RPC 服务。
    • 用你最喜欢的语言(如果可用的话)来创建一个实现了这个接口的服务端。
    • 用你最喜欢的(或者其他你愿意的)语言来访问你的服务端。

    关于protocol buffers: https://www.cnblogs.com/zhaohaiyu/p/11826162.html

    protubuf文件编写

    syntax = "proto3"; 
    
    package hello; 
    
    // 定义一个服务
    service Hello {
        // SayHello 方法
        rpc SayHello (HelloRequest) returns (HelloRenponse) {}
    }
    
    // 请求输入值
    message HelloRequest {
        string name = 1;
    }
    
    // 请求返回值
    message HelloResponse {
        string message = 1;
    }
    

    golang创建grpc server

    下载包:

    1. go get -u google.golang.org/grpc
    2. https://github.com/google/protobuf/releases在线protoc放到GOPATH/bin下
    3. go get -u github.com/golang/protobuf/protoc-gen-go

    执行:

    protoc  --go_out=plugins=grpc:. .hello.proto
    

    go server代码

    package main
    
    import (
    	"fmt"
    	"net"
    	 pb "test/demo10/server/hello"
    
    	"golang.org/x/net/context"
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/reflection"
    )
    
    type server struct{}
    
    func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
    	fmt.Println("被调用!!!")
    	return &pb.HelloResponse{Message: "hello ---> " + in.Name}, nil
    }
    
    func main() {
    	// 监听本地的8848端口
    	lis, err := net.Listen("tcp", "localhost:8848")
    	if err != nil {
    		fmt.Printf("listen failed: %v", err)
    		return
    	}
    	s := grpc.NewServer() // 创建gRPC服务器
    	pb.RegisterHelloServer(s, &server{}) // 在gRPC服务端注册服务
    
    	reflection.Register(s) //在给定的gRPC服务器上注册服务器反射服务
    	// Serve方法在lis上接受传入连接,为每个连接创建一个ServerTransport和server的goroutine。
    	// 该goroutine读取gRPC请求,然后调用已注册的处理程序来响应它们。
    	err = s.Serve(lis)
    	if err != nil {
    		fmt.Printf("failed to serve: %v", err)
    		return
    	}
    }
    

    golang创建grpc client

    执行:

    protoc  --go_out=plugins=grpc:. .hello.proto
    

    go client代码

    package main
    
    import (
    	"context"
    	"fmt"
    
    	pb "test/demo10/client/hello"
    	"google.golang.org/grpc"
    )
    
    func main() {
    	// 连接服务器
    	conn, err := grpc.Dial("localhost:8848", grpc.WithInsecure())
    	if err != nil {
    		fmt.Printf("connect faild: %v", err)
    	}
    	defer conn.Close()
    
    	c := pb.NewHelloClient(conn)
    	// 调用SayHello
    	r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: "zhaohaiyu"})
    	if err != nil {
    		fmt.Printf("sayHello failed: %v", err)
    	}
    	fmt.Printf("SayHello: %s 
    ", r.Message)
    }
    

    结果:

    SayHello: hello ---> zhaohaiyu
    

    python创建grpc client

    使用python客户端调用golang服务端的方法

    下载依赖:

    pip install grpcio
    pip install protobuf
    pip install grpcio_tools
    

    执行:

    python -m grpc_tools.protoc -I ./ --python_out=./ --grpc_python_out=./ hello.proto
    

    python client代码

    import grpc
    import hello_pb2
    import hello_pb2_grpc
    
    
    def run():
        with grpc.insecure_channel('localhost:8848') as channel:
            stub = hello_pb2_grpc.HelloStub(channel)
            res = stub.SayHello(hello_pb2.HelloRequest(name="赵海宇"))
        print(res.message)
    
    if __name__ == '__main__':
        run()
    

    结果:

    python ./main.go
    hello ---> 赵海宇
    

    gprc的haeder

    • grpc是基于http2.0的rpc框架 -
    • grpc对于http头部传递数据进行了封装 metadata,单独抽象了一个包 google.golang.org/grpc/metadata -
    • type MD map[string][]string 其实就是一个map

    客户端发送方式一:

    // 创建md 并加入ctx
    	md := metadata.Pairs("key1","value1","key2","value2")
    	ctx := metadata.NewOutgoingContext(context.Background(),md)
    	// 从ctx中拿出md
    	md,_ = metadata.FromOutgoingContext(ctx)
    	newMd := metadata.Pairs("key3","value3")
    	ctx = metadata.NewOutgoingContext(ctx,metadata.Join(md,newMd))
    

    客户端发送方式二:

    	ctx := context.Background()
    	ctx = metadata.AppendToOutgoingContext(ctx,"key1","value1","key2","value2")
    	ctx = metadata.AppendToOutgoingContext(ctx,"key3","value3")
    

    服务端接收:

    md,ok := metadata.FromIncomingContext(ctx)
    

    实例:

    1. server:
    package main
    
    import (
    	"fmt"
    	"net"
    	pb "test/demo13/server/hello"
    
    	"github.com/grpc-ecosystem/grpc-gateway/examples/clients/responsebody"
    	"github.com/uber/jaeger-client-go/crossdock/client"
    	"golang.org/x/net/context"
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/metadata"
    	"google.golang.org/grpc/reflection"
    )
    
    type server struct{}
    
    func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
    	
    	md,ok := metadata.FromIncomingContext(ctx)
    	if ok {
    		fmt.Println(md)
    	}
    	return &pb.HelloResponse{Message: "hello ---> " + in.Name}, nil
    }
    
    func main() {
    	// 监听本地的8848端口
    	lis, err := net.Listen("tcp", "localhost:8848")
    	if err != nil {
    		fmt.Printf("listen failed: %v", err)
    		return
    	}
    	s := grpc.NewServer() // 创建gRPC服务器
    	pb.RegisterHelloServer(s, &server{}) // 在gRPC服务端注册服务
    
    	reflection.Register(s) //在给定的gRPC服务器上注册服务器反射服务
    	// Serve方法在lis上接受传入连接,为每个连接创建一个ServerTransport和server的goroutine。
    	// 该goroutine读取gRPC请求,然后调用已注册的处理程序来响应它们。
    	err = s.Serve(lis)
    	if err != nil {
    		fmt.Printf("failed to serve: %v", err)
    		return
    	}
    
    }
    
    1. client
    package main
    
    import (
    	"context"
    	"fmt"
    
    	pb "test/demo13/client/hello"
    
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/metadata"
    )
    
    func main() {
    	// 连接服务器
    	conn, err := grpc.Dial("localhost:8848", grpc.WithInsecure())
    	if err != nil {
    		fmt.Printf("faild to connect: %v", err)
    	}
    	defer conn.Close()
    
    	c := pb.NewHelloClient(conn)
    	// 调用服务端的SayHello
    	ctx := context.Background()
    	ctx = metadata.AppendToOutgoingContext(ctx,"zhyyz","961119")
    	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "zhaohaiyu"})
    	if err != nil {
    		fmt.Printf("sayHello failed: %v", err)
    	}
    	fmt.Printf("SayHello: %s 
    ", r.Message)
    }
    
  • 相关阅读:
    select poll使用
    蓝缘管理系统第二个版本号开源了。springMVC+springSecurity3.x+Mybaits3.x 系统
    Map生成器 map适配器如今能够使用各种不同的Generator,iterator和常量值的组合来填充Map初始化对象
    as3.0 interface接口使用方法
    javascript异步延时载入及推断是否已载入js/css文件
    KMP算法具体解释(转)
    Codeforces #250 (Div. 2) C.The Child and Toy
    与机房收费系统重相见
    /bin/bash: line 0: fg: no job control一般解决方法
    oracle db打one-off-patch 一例
  • 原文地址:https://www.cnblogs.com/zhaohaiyu/p/13608776.html
Copyright © 2011-2022 走看看