galang 学习之grpc+ protobuf(二)
来源:互联网 发布:什么软件测试兼容性 编辑:程序博客网 时间:2024/05/16 23:48
上一篇介绍了grpc+ protobuf的一个helloworld,下面介绍一个多接口,并且具有验证功能等辅助一点的例子。这个例子主要是一个坐标使用获取的例子,先看看proto文件定义:
yntax = "proto3";option java_multiple_files = true;option java_package = "io.grpc.examples.routeguide";option java_outer_classname = "RouteGuideProto";package routeguide;// Interface exported by the server.service RouteGuide { //获取单个Feature方法定义 rpc GetFeature(Point) returns (Feature) {} // 获取Feature列表方法定义 rpc ListFeatures(Rectangle) returns (stream Feature) {} // RecordRoute记录point,客户端通过stream发送到服务端 rpc RecordRoute(stream Point) returns (RouteSummary) {} // 双向stream的例子 rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}}// Points定义包括经度纬度message Point { int32 latitude = 1; int32 longitude = 2;}// 长方形定义message Rectangle { // One corner of the rectangle. Point lo = 1; // The other corner of the rectangle. Point hi = 2;}// 位置定义,包括坐标和名称message Feature { // The name of the feature. string name = 1; // The point where the feature is detected. Point location = 2;}//RouteNote包含位置和信息message RouteNote { // The location from which the message is sent. Point location = 1; // The message to be sent. string message = 2;}// RouteSummary 路程总结定义message RouteSummary { // The number of points received. int32 point_count = 1; // The number of known features passed while traversing the route. int32 feature_count = 2; // The distance covered in metres. int32 distance = 3; // The duration of the traversal in seconds. int32 elapsed_time = 4;}
通过命令
protoc -I routeguide/ routeguide/route_guide.proto --go_out=plugins=grpc:routeguide
生成客户端和服务端调用文件route_guide.pb.go
下面看服务端实现这些接口代码如下:
package mainimport ( "encoding/json" "flag" "fmt" "io" "io/ioutil" "math" "net" "time" "strings" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/grpclog" "github.com/golang/protobuf/proto" pb "google.golang.org/grpc/examples/route_guide/routeguide" "path/filepath" "os" "log")//支持ca认证服务端需要秘钥key以及私钥var ( tls = flag.Bool("tls", false, "Connection uses TLS if true, else plain TCP") certFile = flag.String("cert_file", "testdata/server1.pem", "The TLS cert file") keyFile = flag.String("key_file", "testdata/server1.key", "The TLS key file") jsonDBFile = flag.String("json_db_file", "/Users/chenxy/go/src/google.golang.org/grpc/examples/route_guide/testdata/route_guide_db.json", "A json file containing a list of features") port = flag.Int("port", 10000, "The server port"))type routeGuideServer struct { savedFeatures []*pb.Feature routeNotes map[string][]*pb.RouteNote}// GetFeature returns the feature at the given point.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 } } // No feature was found, return an unnamed feature return &pb.Feature{Location: point}, nil}// ListFeatures lists all features contained within the given bounding Rectangle.func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error { for _, feature := range s.savedFeatures { if inRange(feature.Location, rect) { if err := stream.Send(feature); err != nil { return err } } } return nil}// RecordRoute records a route composited of a sequence of points.//// It gets a stream of points, and responds with statistics about the "trip":// number of points, number of known features visited, total distance traveled, and// total time spent.func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error { var pointCount, featureCount, distance int32 var lastPoint *pb.Point startTime := time.Now() for { 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 }}// RouteChat receives a stream of message/location pairs, and responds with a stream of all// previous messages at each of those locations.func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error { for { in, err := stream.Recv() if err == io.EOF { return nil } if err != nil { return err } key := serialize(in.Location) if _, present := s.routeNotes[key]; !present { s.routeNotes[key] = []*pb.RouteNote{in} } else { s.routeNotes[key] = append(s.routeNotes[key], in) } for _, note := range s.routeNotes[key] { if err := stream.Send(note); err != nil { return err } } }}// loadFeatures从JSON文件中加载featuresfunc (s *routeGuideServer) loadFeatures(filePath string) { file, err := ioutil.ReadFile(filePath) if err != nil { grpclog.Fatalf("Failed to load default features: %v", err) } if err := json.Unmarshal(file, &s.savedFeatures); err != nil { grpclog.Fatalf("Failed to load default features: %v", err) }}func toRadians(num float64) float64 { return num * math.Pi / float64(180)}// calcDistance calculates the distance between two points using the "haversine" formula.// This code was taken from http://www.movable-type.co.uk/scripts/latlong.html.func calcDistance(p1 *pb.Point, p2 *pb.Point) int32 { const CordFactor float64 = 1e7 const R float64 = float64(6371000) // metres lat1 := float64(p1.Latitude) / CordFactor lat2 := float64(p2.Latitude) / CordFactor lng1 := float64(p1.Longitude) / CordFactor lng2 := float64(p2.Longitude) / CordFactor φ1 := toRadians(lat1) φ2 := toRadians(lat2) Δφ := toRadians(lat2 - lat1) Δλ := toRadians(lng2 - lng1) a := math.Sin(Δφ/2)*math.Sin(Δφ/2) + math.Cos(φ1)*math.Cos(φ2)* math.Sin(Δλ/2)*math.Sin(Δλ/2) c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a)) distance := R * c return int32(distance)}func inRange(point *pb.Point, rect *pb.Rectangle) bool { left := math.Min(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude)) right := math.Max(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude)) top := math.Max(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude)) bottom := math.Min(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude)) if float64(point.Longitude) >= left && float64(point.Longitude) <= right && float64(point.Latitude) >= bottom && float64(point.Latitude) <= top { return true } return false}func serialize(point *pb.Point) string { return fmt.Sprintf("%d %d", point.Latitude, point.Longitude)}func newServer() *routeGuideServer { s := new(routeGuideServer) s.loadFeatures(*jsonDBFile) s.routeNotes = make(map[string][]*pb.RouteNote) return s}func main() { flag.Parse() lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) if err != nil { grpclog.Fatalf("failed to listen: %v", err) } var opts []grpc.ServerOption if *tls { creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile) if err != nil { grpclog.Fatalf("Failed to generate credentials %v", err) } opts = []grpc.ServerOption{grpc.Creds(creds)} } grpcServer := grpc.NewServer(opts...) pb.RegisterRouteGuideServer(grpcServer, newServer()) grpcServer.Serve(lis)}
服务端实现了ListFeatures等这些方法,为了测试服务端需要预先从JSON文件加载Features,格式如下:
[{ "location": { "latitude": 409146138, "longitude": -746188906 }, "name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA"}, { "location": { "latitude": 404701380, "longitude": -744781745 },]
先启动服务端,客户端调用代码如下:
package mainimport ( "flag" "io" "math/rand" "time" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/credentials" pb "google.golang.org/grpc/examples/route_guide/routeguide" "google.golang.org/grpc/grpclog")//指定ca证书var ( tls = flag.Bool("tls", false, "Connection uses TLS if true, else plain TCP") caFile = flag.String("ca_file", "testdata/ca.pem", "The file containning the CA root cert file") serverAddr = flag.String("server_addr", "127.0.0.1:10000", "The server address in the format of host:port") serverHostOverride = flag.String("server_host_override", "x.test.youtube.com", "The server name use to verify the hostname returned by TLS handshake"))// printFeature gets the feature for the given point.func printFeature(client pb.RouteGuideClient, point *pb.Point) { grpclog.Printf("Getting feature for point (%d, %d)", point.Latitude, point.Longitude) feature, err := client.GetFeature(context.Background(), point) if err != nil { grpclog.Fatalf("%v.GetFeatures(_) = _, %v: ", client, err) } grpclog.Println(feature)}// printFeatures lists all the features within the given bounding Rectangle.func printFeatures(client pb.RouteGuideClient, rect *pb.Rectangle) { grpclog.Printf("Looking for features within %v", rect) stream, err := client.ListFeatures(context.Background(), rect) if err != nil { grpclog.Fatalf("%v.ListFeatures(_) = _, %v", client, err) } for { feature, err := stream.Recv() if err == io.EOF { break } if err != nil { grpclog.Fatalf("%v.ListFeatures(_) = _, %v", client, err) } grpclog.Println(feature) }}// runRecordRoute sends a sequence of points to server and expects to get a RouteSummary from server.func runRecordRoute(client pb.RouteGuideClient) { // Create a random number of random points 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)) } grpclog.Printf("Traversing %d points.", len(points)) stream, err := client.RecordRoute(context.Background()) if err != nil { grpclog.Fatalf("%v.RecordRoute(_) = _, %v", client, err) } for _, point := range points { if err := stream.Send(point); err != nil { grpclog.Fatalf("%v.Send(%v) = %v", stream, point, err) } } reply, err := stream.CloseAndRecv() if err != nil { grpclog.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil) } grpclog.Printf("Route summary: %v", reply)}// runRouteChat receives a sequence of route notes, while sending notes for various locations.func runRouteChat(client pb.RouteGuideClient) { notes := []*pb.RouteNote{ {&pb.Point{Latitude: 0, Longitude: 1}, "First message"}, {&pb.Point{Latitude: 0, Longitude: 2}, "Second message"}, {&pb.Point{Latitude: 0, Longitude: 3}, "Third message"}, {&pb.Point{Latitude: 0, Longitude: 1}, "Fourth message"}, {&pb.Point{Latitude: 0, Longitude: 2}, "Fifth message"}, {&pb.Point{Latitude: 0, Longitude: 3}, "Sixth message"}, } stream, err := client.RouteChat(context.Background()) if err != nil { grpclog.Fatalf("%v.RouteChat(_) = _, %v", client, err) } waitc := make(chan struct{}) go func() { for { in, err := stream.Recv() if err == io.EOF { // read done. close(waitc) return } if err != nil { grpclog.Fatalf("Failed to receive a note : %v", err) } grpclog.Printf("Got message %s at point(%d, %d)", in.Message, in.Location.Latitude, in.Location.Longitude) } }() for _, note := range notes { if err := stream.Send(note); err != nil { grpclog.Fatalf("Failed to send a note: %v", err) } } stream.CloseSend() <-waitc}func randomPoint(r *rand.Rand) *pb.Point { lat := (r.Int31n(180) - 90) * 1e7 long := (r.Int31n(360) - 180) * 1e7 return &pb.Point{Latitude: lat, Longitude: long}}func main() { flag.Parse() var opts []grpc.DialOption if *tls { var sn string if *serverHostOverride != "" { sn = *serverHostOverride } var creds credentials.TransportCredentials if *caFile != "" { var err error creds, err = credentials.NewClientTLSFromFile(*caFile, sn) if err != nil { grpclog.Fatalf("Failed to create TLS credentials %v", err) } } else { creds = credentials.NewClientTLSFromCert(nil, sn) } opts = append(opts, grpc.WithTransportCredentials(creds)) } else { opts = append(opts, grpc.WithInsecure()) } conn, err := grpc.Dial(*serverAddr, opts...) if err != nil { grpclog.Fatalf("fail to dial: %v", err) } defer conn.Close() client := pb.NewRouteGuideClient(conn) // 获取一个合法的Feature printFeature(client, &pb.Point{Latitude: 409146138, Longitude: -746188906}) // 这个Feature在服务端没有回返回空 printFeature(client, &pb.Point{Latitude: 0, Longitude: 0}) // Looking for features between 40, -75 and 42, -73. printFeatures(client, &pb.Rectangle{ Lo: &pb.Point{Latitude: 400000000, Longitude: -750000000}, Hi: &pb.Point{Latitude: 420000000, Longitude: -730000000}, }) // RecordRoute runRecordRoute(client) // RouteChat runRouteChat(client)}
这个例子里面涉及到多个函数接口的调用,以及stream的使用。
0 0
- galang 学习之grpc+ protobuf(二)
- galang 学习之grpc+ protobuf(一)
- protobuf + grpc 使用入门 二
- Protobuf和GRPC(一)
- protobuf+grpc+examples
- docker学习笔记-----grpc 填坑记(二)
- GRPC学习笔记(一)
- protobuf + grpc 使用入门 一
- 学习GRPC
- rpc框架之gRPC 学习 - hello world
- gRPC的使用(二)之异步服务器流RPC的使用流程
- Google protobuf使用之proto文件编写规则(二)
- grpc+protobuf 的C++ service 实例解析
- java中使用grpc和protobuf
- 在python中使用grpc和protobuf
- 初识google多语言通信框架gRPC系列(二)编译gRPC
- TCP/Protobuf之Netty学习
- Protobuf学习(1)
- Qt--openGL
- JSP的入门简介
- svn 冲突
- Java经典算法
- 虚拟IP原理
- galang 学习之grpc+ protobuf(二)
- 基本TCP套接字编程
- 仿微信朋友圈日期显示 时间戳格式化
- Compound Words UVA
- (转)Android事件总线(一)EventBus3.0用法全解析
- spring线程池配置及使用
- linux中不同系统之间的文件传输 文件归档,压缩
- Android动画插值器之PathInterpolator浅析
- svn 查看项目的 svn 服务器地址