Fabric源码基础-grpc的使用02

Fabric的节点通过grpc向内部或外部提供接口,在学习源码之前,需要对grpc的基本使用有所了解,并了解如何在grpc中配置tls。因此,本节整理了go语言中grpc四种类型的服务模式

示例来源:http://doc.oschina.net/grpc?t=60133
具体代码可见:https://github.com/ssj234/fabric/tree/master/grpc-go

2.定义服务

2.1 定义服务

RouteGuide服务有四个方法,代表了四种模式,

service RouteGuide {
    // 根据点获取Feature
    rpc GetFeature(Point) returns (Feature){}
    // 矩形内包含多个点,获取矩形内点对于的Feature,返回是一个流类型
    rpc ListFeatures(Rectangle) returns (stream Feature){}
    // 输入是一个流,服务器可以不断的获取客户端发生的点,最后返回RouteSummary
    rpc RecordRoute(stream Point) returns (RouteSummary){}
   // 输入和输出都是流式的,可以不断的获取输入并返回
    rpc RouteChat(stream RouteNote) returns (stream RouteNote){}
}

2.2生成代码

使用protoc3可以生成go语言代码

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

2.3 非stream服务

服务器端程序在使用grpc时,首先需要定义一个结构体,并为其实现服务

type routeGuideServer struct {
    savedFeatures []*pb.Feature // 初始化后就是只读的了
    mu         sync.Mutex // 多线程,保护routeNotes
    routeNotes map[string][]*pb.RouteNote // 保存了名称与RouteNote
}

服务端程序
对于非stream服务,我们需要实现rpc GetFeature(Point) returns (Feature){},逻辑是通过客户端发生的Point,在savedFeatures中查找这个点所在的feature,如果没有有将传入的Point包装为Feature并返回。

// 通过Point查找Feature,Feature有个变量叫做location
func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) {
    for _,feature := range s.savedFeatures{
        if proto.Equal(feature.Location,point){
            return feature,nil
        }
    }
    // 没有的话就创建一个,设置location为point
    return &pb.Feature{Location:point},nil
}

客户端程序

客户端程序与上一章节中一样,建立连接后获取client并调用即可

var opts []grpc.DialOption
    opts = append(opts, grpc.WithInsecure())
    conn, err := grpc.Dial("127.0.0.1:9999", opts...)
    if err != nil {
        log.Fatalf("fail to dial: %v", err)
    }
    defer conn.Close()
    client := pb.NewRouteGuideClient(conn) // 使用pb生成client

    // 调用GetFeature
    feature, err := client.GetFeature(context.Background(), &pb.Point{Latitude:409146138, Longitude:-746188906})
    if err != nil {
        log.Fatalf("%v.GetFeatures(_) = _, %v: ", client, err)
    }
    log.Println(feature)

2.3 服务端stream服务

服务端程序
服务端steam服务是服务器收到客户端的请求后不断的返回数据,rpc ListFeatures(Rectangle) returns (stream Feature){} 这个服务用来返回矩形内的Feature

func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error {
     // 遍历savedFeatures,查看是否在rect里面
    for _, feature := range s.savedFeatures {
        if inRange(feature.Location, rect) {
            if err := stream.Send(feature);err != nil{
                return err
            }
        }
    }
    return nil
}

客户端程序

point1 := &pb.Point{Longitude:-743977337,Latitude:407033786}
    point2 := &pb.Point{Longitude:-740477477,Latitude:414653148}
    rectangle := &pb.Rectangle{Lo:point1,Hi:point2}
    stream, err := client.ListFeatures(context.Background(),rectangle)
    for {
        feature, err := stream.Recv()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatalf("%v.ListFeatures(_) = _, %v", client, err)
        }
        log.Println(feature)
    }

2.4 客户端stream服务

rpc RecordRoute(stream Point) returns (RouteSummary){},这个服务不断的接收客户端请求,最后收到EOF后返回RouteSummary

服务端程序

func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error {
    var pointCount, featureCount, distance int32
    var lastPoint *pb.Point // 最后一个点
    startTime := time.Now() // 开始时间
    for{  // 不断的收到客户端轻轻,直到EOF
        point,err := stream.Recv()
        if err == io.EOF{
            endTime := time.Now()
            return stream.SendAndClose(&pb.RouteSummary{
                PointCount:pointCount,
                FeatureCount:featureCount,
                Distance:distance,
                ElapsedTime:int32(endTime.Sub(startTime).Seconds()),
            })
        }
        if err != nil{
            return  err
        }
        pointCount++
        for _,feature := range s.savedFeatures{
            if proto.Equal(feature.Location,point){
                featureCount++
            }
        }

        if lastPoint != nil{
            distance += calcDistance(lastPoint,point)
        }
        lastPoint = point
    }

}

客户端程序

// 构造一些point
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    pointCount := int(r.Int31n(100)) + 2 // Traverse at least two points
    var points []*pb.Point
    for i := 0; i < pointCount; i++ {
        points = append(points, randomPoint(r))
    }
    // 调用服务
    stream, err := client.RecordRoute(context.Background())
    for _, point := range points {
        if err := stream.Send(point); err != nil {
            log.Fatalf("%v.Send(%v) = %v", stream, point, err)
        }
    }
    // 结束调用
    reply, err := stream.CloseAndRecv()
    log.Printf("Route summary: %v", reply)

2.5 双向stream服务

服务端程序

func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error {

    for{
        in,err := stream.Recv() // RouteNote error
        if err == io.EOF{
            return nil
        }
        if err != nil{
            return  err
        }

        key := serialize(in.Location)
        s.mu.Lock()
        s.routeNotes[key] = append(s.routeNotes[key],in)
        rn := make([]*pb.RouteNote, len(s.routeNotes[key]))
        copy(rn,s.routeNotes[key])
        s.mu.Unlock()

        for _, note := range rn { // 不断的发出
            if err := stream.Send(note); err != nil {
                return err
            }
        }

    }
}

客户端程序

stream, err := client.RouteChat(context.Background())
    // 由于在发送RouteNode的同时,还要接收RouteNode,因此使用go
    waitc := make(chan struct{})
    go func() {
        for {
            in, err := stream.Recv()
            if err == io.EOF {
                close(waitc) // 关闭chan
                return
            }
            log.Printf("Got message %s at point(%d, %d)", in.Message, in.Location.Latitude, in.Location.Longitude)
        }
    }()

    for _, note := range getNodes() {
        if err := stream.Send(note); err != nil {
            log.Fatalf("Failed to send a note: %v", err.Error())
        }
    }
    stream.CloseSend()
    <-waitc // 等待chan关闭
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,809评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,189评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,290评论 0 359
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,399评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,425评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,116评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,710评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,629评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,155评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,261评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,399评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,068评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,758评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,252评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,381评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,747评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,402评论 2 358

推荐阅读更多精彩内容