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
|
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 将消息分解成独立的帧,交错发送,在另一端再重新组装。
根据图片可以看出 数据流分成多个帧进行发送,又有不同的帧类型,而且不同的数据流交错发送。
而具体的某一帧一般包括 以上内容。