grpc  是一种跨语言的rpc 的框架。跨语言,主要通过具体的插件根据 .proto 文件生成具体的代码。具体proto 语法请看文件:

syntax = "proto3";  //如果不写,默认proto2

option go_package = "google.golang.org/grpc/examples/route_guide/routeguide";
option java_multiple_files = true;
option java_package = "io.grpc.examples.routeguide";
option java_outer_classname = "RouteGuideProto";

package routeguide;


service RouteGuide {

  rpc GetFeature(Point) returns (Feature) {}

  rpc ListFeatures(Rectangle) returns (stream Feature) {}

  rpc TestGetContent(FileName) returns (stream Content) {}

  rpc RecordRoute(stream Point) returns (RouteSummary) {}

  rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
}

//在定义消息参数时,每个消息类型内 每个字段都有一个唯一的数字表示,主要为了当消息以二进制格式表示时,可以区分。而且当字段用数字  1 到 15 之间的数字表示时,用一个 byte 编码,而 16 到 2047  表示的字段需要两个字节,所以使用频繁的字段需要用小的数字,或者需要为使用频繁的字段预留部分数字。

message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

message FileName {
     string name=1;
}

message Content {
     string content=1;
}


message Rectangle {

  Point lo = 1;

  Point hi = 2;
}


message Feature {

  string name = 1;

  Point location = 2;
}


message RouteNote {

  Point location = 1;

  string message = 2;
}


message RouteSummary {

  int32 point_count = 1;

  int32 feature_count = 2;

  int32 distance = 3;

  int32 elapsed_time = 4;
}

字段的生成规则可以查看连接 https://developers.google.com/protocol-buffers/docs/reference/go-generated

使用protoc 编译器生成 go 代码需要的插件有 protoc-gen-go  和  protoc-gen-go-grpc  。

安装完插件后执行 如下命令,就会生成对应的代码。具体请看连接:https://developers.google.com/protocol-buffers/docs/reference/go-generated

protoc  --go_out=.  --go-grpc_out=.   --go_opt=paths=source_relative   --go-gr
pc_opt=paths=source_rative  routeguide/route_guide.proto

grpc  包括四种服务模式。 

1、一元模式(普通模式)

2、服务端向客户端流式发送数据

3、客户端向服务端流式发送数据

4、服务端向客户端流式发送数据,客户端向服务端流式发送数据

首先我们需要启动grpc服务,在启动服务时,可以设置一系列选项。如下:

serverOptions  配置
statsHandler          stats.Handler

使用实例如下

http://www.cppblog.com/jinq0123/archive/2017/12/25/215444.aspx

 

type serverOptions struct {
	creds                 credentials.TransportCredentials
	codec                 baseCodec 
	cp                    Compressor    //压缩
	dc                    Decompressor
	unaryInt              UnaryServerInterceptor  //普通模式拦截器
	streamInt             StreamServerInterceptor //流式服务拦截器
	chainUnaryInts        []UnaryServerInterceptor
	chainStreamInts       []StreamServerInterceptor
	inTapHandle           tap.ServerInHandle
	statsHandler          stats.Handler
	maxConcurrentStreams  uint32
	maxReceiveMessageSize int
	maxSendMessageSize    int
	unknownStreamDesc     *StreamDesc
	keepaliveParams       keepalive.ServerParameters
	keepalivePolicy       keepalive.EnforcementPolicy
	initialWindowSize     int32
	initialConnWindowSize int32
	writeBufferSize       int
	readBufferSize        int
	connectionTimeout     time.Duration
	maxHeaderListSize     *uint32
	headerTableSize       *uint32
}

使用实例:

//类似中间件的 拦截器
	var interceptor  grpc.UnaryServerInterceptor
	interceptor= func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
		fmt.Println("拦截成功!")
		err=auth(ctx)
		if err != nil  {
			return
		}
		return handler(ctx,req)
	}


	var  tt  grpc.StreamServerInterceptor
	tt= func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
		err=auth(ss.Context())
		if err != nil  {
			fmt.Println(err)
			return err
		}
		fmt.Println("拦截成功哈哈哈")
		return handler(srv,ss)
	}

	opts=append(opts,grpc.UnaryInterceptor(interceptor))
	opts=append(opts,grpc.StreamInterceptor(tt))
func auth(ctx context.Context)error{

	md,ok:=metadata.FromIncomingContext(ctx)
	if !ok {
		return status.Errorf(codes.Unauthenticated,"")
	}
	fmt.Println(md)
	if val,ok:=md["appid"];ok {
		fmt.Println(val)
		return nil
	}
	return grpc.Errorf(codes.Unauthenticated,"yanzheng")
}

 

普通的拦截器无法拦截流式服务接口。

 

在使用客户端调用时,也可以设置一些配置。

type  customCredential struct {

}

func (c customCredential)GetRequestMetadata(ctx context.Context,uri ...string)(map[string]string,error)  {

	return map[string]string{
		"appid":"101010",
		"appkey":"I am  key",
	},nil
}

func (c customCredential)RequireTransportSecurity() bool  {
	return false
}
flag.Parse()
	var opts []grpc.DialOption
	if *tls {
		if *caFile == "" {
			*caFile = testdata.Path("ca.pem")
		}
		creds, err := credentials.NewClientTLSFromFile(*caFile, *serverHostOverride)
		if err != nil {
			log.Fatalf("Failed to create TLS credentials %v", err)
		}
		opts = append(opts, grpc.WithTransportCredentials(creds))
	} else {
		opts = append(opts, grpc.WithInsecure())
	}

	opts = append(opts, grpc.WithBlock())
	opts = append(opts, grpc.WithPerRPCCredentials(new(customCredential)))
	opts=append(opts,grpc.WithDefaultCallOptions(grpc.UseCompressor(gzip.Name)))

	conn, err := grpc.Dial(*serverAddr, opts...)
	if err != nil {
		log.Fatalf("fail to dial: %v", err)
	}
	defer conn.Close()
	client := pb.NewRouteGuideClient(conn)

	printFeature(client, &pb.Point{Latitude: 409146138, Longitude: -746188906})
	printContent(client,&pb.FileName{
		Name: "test",
	})

服务端打印结果显示:

拦截成功!
map[:authority:[localhost:9000] appkey:[I am  key] content-type:[application/grpc] grpc-accept-encoding:[gzip] user-agent:[grpc-go/1.30.0-dev]]
map[:authority:[localhost:9000] appkey:[I am  key] content-type:[application/grpc] grpc-accept-encoding:[gzip] user-agent:[grpc-go/1.30.0-dev]]
拦截成功哈哈哈
sldfksjldgsdg;fgfgf
sdfsdlf;sdf
sdfsdg;lsdkg;sdg
sgmvlmgioreoemlfdmgl
拦截成功!
map[:authority:[localhost:9000] appkey:[I am  key] content-type:[application/grpc] grpc-accept-encoding:[gzip] user-agent:[grpc-go/1.30.0-dev]]
map[:authority:[localhost:9000] appkey:[I am  key] content-type:[application/grpc] grpc-accept-encoding:[gzip] user-agent:[grpc-go/1.30.0-dev]]
拦截成功哈哈哈
sldfksjldgsdg;fgfgf
sdfsdlf;sdf
sdfsdg;lsdkg;sdg
sgmvlmgioreoemlfdmgl

 

http2.0  特点

帧:通信的最小单元

数据流:已经建立连接的双向字节流。可以承载一条或多条消息

消息:与逻辑请求或响应消息对应的一系列帧

http2.0  将消息分解成独立的帧,交错发送,在另一端再重新组装。

Grpc plaintext模式 grpc中文文档_字段

根据图片可以看出 数据流分成多个帧进行发送,又有不同的帧类型,而且不同的数据流交错发送。

Grpc plaintext模式 grpc中文文档_Grpc plaintext模式_02

 

而具体的某一帧一般包括  以上内容。