gRPC简介及其在Python中使用



前言

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 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

简介

gRPC是什么

在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。

grpc_concept_diagram_00

gRPC 客户端和服务端可以在多种环境中运行和交互 - 从 google 内部的服务器到你自己的笔记本,并且可以用任何 gRPC 支持的语言来编写。所以,你可以很容易地用 Java 创建一个 gRPC 服务端,用 Go、Python、Ruby 来创建客户端。此外,Google 最新 API 将有 gRPC 版本的接口,使你很容易地将 Google 的功能集成到你的应用里。

使用protocol buffers

gRPC 默认使用 protocol buffers,这是 Google 开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如 JSON)。正如你将在下方例子里所看到的,你用 proto files 创建 gRPC 服务,用 protocol buffers 消息类型来定义方法参数和返回类型。你可以在 Protocol Buffers 文档找到更多关于 Protocol Buffers 的资料。

安装

准备Python环境

1
2
3
4
5
6
$ python -m pip install --upgrade pip

$ python -m pip install virtualenv
$ virtualenv venv
$ source venv/bin/activate
$ python -m pip install --upgrade pip

安装gRPC

1
2
# 在之前激活的虚拟环境下运行
pip install grpcio

同时还要安装gRPC tools:

1
pip install grpcio-tools googleapis-common-protos

示例

下载官方例子

1
2
3
git clone -b v1.15.0 https://github.com/grpc/grpc
# Navigate to the "hello, world" Python example:
$ cd grpc/examples/python/helloworld

运行一个gRPC应用

examples/python/helloworld 目录中:

  1. 运行服务端:
1
python greeter_server.py
  1. 在另一个terminal,运行客户端:
1
python greeter_client.py

Congratulations! You’ve just run a client-server application with gRPC.

python编写一个RPC服务完整过程

定义服务

创建我们例子的第一步是定义一个服务:一个 RPC 服务通过参数和返回类型来指定可以远程调用的方法。 gRPC 通过 protocol buffers 来实现。
我们使用 protocol buffers 接口定义语言来定义服务方法,用 protocol buffer 来定义参数和返回类型。客户端和服务端均使用服务定义生成的接口代码。

这里有我们服务定义的例子,在 helloworld.proto 里用 protocol buffers IDL 定义的。Greeter 服务有一个方法 SayHello ,可以让服务端从远程客户端接收一个包含用户名的 HelloRequest 消息后,在一个 HelloReply 里发送回一个 Greeter。这是你可以在 gRPC 里指定的最简单的 RPC - 你可以在教程里找到针对你选择的语言更多类型的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
string name = 1;
}

// The response message containing the greetings
message HelloReply {
string message = 1;
}

生成gRPC

一旦定义好服务,我们可以使用 protocol buffer 编译器 protoc 来生成创建应用所需的特定客户端和服务端的代码 - 你可以生成任意 gRPC 支持的语言的代码,当然 PHP 和 Objective-C 仅支持创建客户端代码。生成的代码同时包括客户端的存根和服务端要实现的抽象接口,均包含 Greeter 所定义的方法。

1
2
python -m grpc_tools.protoc -I . --python_out=. --grpc_python_out=. helloworld.proto
# helloworld.proto为上面我们编写的proto文件

这生成了 helloworld_pb2.pyhelloworld_pb2_grpc.py两个文件 ,包含我们生成的客户端和服务端类,此外还有用于填充、序列化、提取 HelloRequest 和 HelloResponse 消息类型的类。

编写服务器端代码

服务实现

greeter_server.py 实现了 Greeter 服务所需要的行为。
正如你所见,Greeter 类通过实现 sayHello 方法,实现了从 proto 服务定义生成的helloworld_pb2.BetaGreeterServicer 接口:

1
2
3
4
class Greeter(helloworld_pb2.BetaGreeterServicer)

def SayHello(self, request, context)
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)

为了返回给客户端应答并且完成调用:

用我们的激动人心的消息构建并填充一个在我们接口定义的 HelloReply 应答对象。将 HelloReply 返回给客户端。

服务端实现

需要提供一个 gRPC 服务的另一个主要功能是让这个服务实在在网络上可用。

greeter_server.py 提供了以下代码作为 Python 的例子。

1
2
3
4
5
6
7
8
server = helloworld_pb2.beta_create_Greeter_server(Greeter())
server.add_insecure_port('[::]:50051')
server.start()
try
while True
time.sleep(_ONE_DAY_IN_SECONDS)
except KeyboardInterrupt:
server.stop()

在这里我们创建了合理的 gRPC 服务器,将我们实现的 Greeter 服务绑定到一个端口。然后我们启动服务器:服务器现在已准备好从 Greeter 服务客户端接收请求。我们将在具体语言对应的文档里更深入地了解这所有的工作是怎样进行的。

编写客户端代码

连接服务

首先我们看一下我们如何连接 Greeter 服务器。我们需要创建一个 gRPC 频道,指定我们要连接的主机名和服务器端口。然后我们用这个频道创建存根实例。

1
2
3
channel = implementations.insecure_channel('localhost', 50051)
stub = helloworld_pb2.beta_create_Greeter_stub(channel)
...

调用 RPC

现在我们可以联系服务并获得一个 greeting :

  1. 我们创建并填充一个 HelloRequest 发送给服务。
  2. 我们用请求调用存根的 SayHello(),如果 RPC 成功,会得到一个填充的 HelloReply ,从其中我们可以获得 greeting。
1
2
response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'), _TIMEOUT_SECONDS)
print "Greeter client received: " + response.message

greeter_client.py完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import grpc

import helloworld_pb2
import helloworld_pb2_grpc


def run():
# NOTE(gRPC Python Team): .close() is possible on a channel and should be
# used in circumstances in which the with statement does not fit the needs
# of the code.
with grpc.insecure_channel('localhost:50051') as channel:
stub = helloworld_pb2_grpc.GreeterStub(channel)
response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
print("Greeter client received: " + response.message)


if __name__ == '__main__':
run()

运行并测试服务

你可以尝试用同一个语言在客户端和服务端构建并运行例子。或者你可以尝试 gRPC 最有用的一个功能 - 不同的语言间的互操作性,即在不同的语言运行客户端和服务端。每个服务端和客户端使用从同一过 proto 文件生成的接口代码,则意味着任何 Greeter 客户端可以与任何 Greeter 服务端对话。

  1. 运行服务端程序,程序会监听 50051端口:
1
python route_guide_server.py
  1. 运行客户端程序
1
python route_guide_client.py

grpc: 4种通信方式

helloworld 使用了最简单的 grpc 通信方式: 类似 http 协议的一次 request+response.

4种通信方式

根据不同的业务场景, grpc 支持 4 种通信方式:

  • 客服端一次请求, 服务器一次应答
  • 客服端一次请求, 服务器多次应答(流式)
  • 客服端多次请求(流式), 服务器一次应答
  • 客服端多次请求(流式), 服务器多次应答(流式)

官方提供了一个 route guide service 的 demo, 应用到了这 4 种通信方式, 具体的业务如下:

  • 数据源: json 格式的数据源, 存储了很多地点, 每个地点由经纬度(point)和地名(location)组成
  • 通信方式 1: 客户端请求一个地点是否在数据源中
  • 通信方式 2: 客户端指定一个矩形范围(矩形的对角点坐标), 服务器返回这个范围内的地点信息
  • 通信方式 3: 客户端给服务器发送多个地点信息, 服务器返回汇总信息(summary)
  • 通信方式 4: 客户端和服务器使用地点信息 聊天(chat)

对应的proto文件

route_guide.proto
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
syntax = "proto3";


option java_multiple_files = true;
option java_package = "io.grpc.examples.routeguide";
option java_outer_classname = "RouteGuideProto";
option objc_class_prefix = "RTG";

package routeguide;

// Interface exported by the server.
service RouteGuide {

rpc GetFeature(Point) returns (Feature) {}

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

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

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

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

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;
}

proto 中想要表示流式传输, 只需要添加 stream 关键字即可

同样的, 使用 protoc 生成代码:

1
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. route_guide.proto

生成了 route_guide_pb2.pyroute_guide_pb2_grpc.py 文件

处理数据源文件(route_guide_db.json)

route_guide_db.py
1
2
3
4
5
6
7
8
9
10
11
12
13
import json
import route_guide_pb2


def read_route_guide_db():
feature_list = []
with open('route_guide_db.json') as f:
for item in json.load(f):
feature = route_guide_pb2.Feature(name=item['name'],
location=route_guide_pb2.Point(latitude=item['location']['latitude'],
longitude=item['location']['longitude']))
feature_list.append(feature)
return feature_list

处理 json 的过程很简单, 解析 json 数据得到由坐标点组成的数组

怎么处理流式数据呢?. 答案是 for ... in + yield

完整服务器端代码

route_guide_server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
"""The Python implementation of the gRPC route guide server."""
from concurrent import futures
import math
import time

import grpc

import route_guide_pb2
import route_guide_pb2_grpc
import route_guide_db

_ONE_DAY_IN_SECONDS = 60 * 60 * 24


def get_feature(feature_db, point):
"""returns feature at given location or None"""
for feature in feature_db:
if feature.location == point:
return feature
return None


def get_distance(start, end):
"""Distance between two points."""
coord_factor = 10000000.0
lat_1 = start.latitude / coord_factor
lat_2 = end.latitude / coord_factor
lon_1 = start.longitude / coord_factor
lon_2 = end.longitude / coord_factor

lat_rad_1 = math.radians(lat_1)
lat_rad_2 = math.radians(lat_2)
delta_lat_rad = math.radians(lat_2 - lat_1)
delta_lon_rad = math.radians(lon_2 - lon_2)

# Formula is based on http://mathforum.org/library/drmath/view/51879.html
a = (pow(math.sin(delta_lat_rad / 2), 2) +
(math.cos(lat_rad_1) * math.cos(lat_rad_2) * pow(math.sin(delta_lon_rad / 2), 2)))
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
R = 6371000
return R * c


class RouteGuideServicer(route_guide_pb2_grpc.RouteGuideServicer):
"""Provides methods that implement functionality of route guide server."""

def __init__(self):
self.db = route_guide_db.read_route_guide_db()

def GetFeature(self, request, context):
feature = get_feature(self.db, request)
if feature is None:
return route_guide_pb2.Feature(name="", location=request)
else:
return feature

def ListFeatures(self, request, context):
left = min(request.lo.longitude, request.hi.longitude)
right = max(request.lo.longitude, request.hi.longitude)
top = max(request.lo.latitude, request.hi.latitude)
bottom = min(request.lo.latitude, request.hi.latitude)
for feature in self.db:
if(feature.location.longitude >= left and
feature.location.longitude <=right and
feature.location.latitude >= bottom and
feature.location.latitude <= top):
yield feature

def RecordRoute(self, request_iterator, context):
point_count = 0
feature_count = 0
distance = 0.0
prev_point = None

start_time = time.time()
for point in request_iterator:
point_count += 1
if get_feature(self.db, point):
feature_count += 1
if prev_point:
distance += get_distance(prev_point, point)
prev_point = point

elapsed_time = time.time() - start_time
return route_guide_pb2.RouteSummary(
point_count=point_count,
feature_count=feature_count,
distance=int(distance),
elapsed_time=int(elapsed_time))

def RouteChat(self, request_iterator, context):
prev_notes = []
for new_note in request_iterator:
for prev_note in prev_notes:
if prev_note.location == new_note.location:
yield prev_note
prev_notes.append(new_note)


def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
route_guide_pb2_grpc.add_RouteGuideServicer_to_server(RouteGuideServicer(), server)
server.add_insecure_port('[::]:50051')
server.start()
try:
while True:
time.sleep(_ONE_DAY_IN_SECONDS)
except KeyboardInterrupt:
server.stop(0)


if __name__ == '__main__':
print("route guide server is running...")
serve()

完整客户端代码

route_guide_client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
"""The Python implementation of the gRPC route guide client."""

from __future__ import print_function

import random
import grpc
import route_guide_pb2
import route_guide_pb2_grpc
import route_guide_db

def make_route_note(message, latitude, longitude):
return route_guide_pb2.RouteNote(
message=message,
location=route_guide_pb2.Point(latitude=latitude, longitude=longitude)
)


def guide_get_one_feature(stub, point):
feature = stub.GetFeature(point)
if not feature.location:
print("server returned incomplete feature")
return

if feature.name:
print("Feature called %s at %s" % (feature.name, feature.location))
else:
print("Found no feature at %s" % feature.location)


def guide_get_feature(stub):
guide_get_one_feature(stub,
route_guide_pb2.Point(latitude=409146138, longitude=-746188906))
guide_get_one_feature(stub, route_guide_pb2.Point(latitude=0, longitude=0))


def guide_list_features(stub):
rectangle = route_guide_pb2.Rectangle(
lo=route_guide_pb2.Point(latitude=400000000, longitude=-750000000),
hi=route_guide_pb2.Point(latitude=420000000, longitude=-730000000))
print("looking for features between 40, -75 and 42, -73")

features = stub.ListFeatures(rectangle)

for feature in features:
print("Feature called %s at %s" % (feature.name, feature.location))


def generate_route(feature_list):
for _ in range(0, 10):
random_feature = feature_list[random.randint(0, len(feature_list))]
print("Visiting point %s" % random_feature.location)
yield random_feature.location


def guide_record_route(stub):
feature_list = route_guide_db.read_route_guide_db()

route_iterator = generate_route(feature_list)
route_summary = stub.RecordRoute(route_iterator)
print("Finished trip with %s points " % route_summary.point_count)
print("Passed %s features " % route_summary.feature_count)
print("Travelled %s meters " % route_summary.distance)
print("It took %s seconds " % route_summary.elapsed_time)


def generate_messages():
messages = [
make_route_note("First message", 0, 0),
make_route_note("Second message", 0, 1),
make_route_note("Third message", 1, 0),
make_route_note("Fourth message", 0, 0),
make_route_note("Fifth message", 1, 0),
]

for msg in messages:
print("Sending %s at %s" % (msg.message, msg.location))
yield msg


def guide_route_chat(stub):
responses = stub.RouteChat(generate_messages())
for response in responses:
print("Received message %s at %s" % (response.message, response.location))



def run():
with grpc.insecure_channel('localhost:50051') as channel:
stub = route_guide_pb2_grpc.RouteGuideStub(channel)
print("-------------- GetFeature --------------")
guide_get_feature(stub)
print("-------------- ListFeatures --------------")
guide_list_features(stub)
print("-------------- RecordRoute --------------")
guide_record_route(stub)
print("-------------- RouteChat --------------")
guide_route_chat(stub)


if __name__ == '__main__':
run()

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
-------------- GetFeature --------------
Feature called Berkshire Valley Management Area Trail, Jefferson, NJ, USA at latitude: 409146138
longitude: -746188906

Found no feature at
-------------- ListFeatures --------------
looking for features between 40, -75 and 42, -73
Feature called Patriots Path, Mendham, NJ 07945, USA at latitude: 407838351
longitude: -746143763

Feature called 101 New Jersey 10, Whippany, NJ 07981, USA at latitude: 408122808
longitude: -743999179

Feature called U.S. 6, Shohola, PA 18458, USA at latitude: 413628156
longitude: -749015468

Feature called 5 Conners Road, Kingston, NY 12401, USA at latitude: 419999544
longitude: -740371136

Feature called Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA at latitude: 414008389
longitude: -743951297

Feature called 287 Flugertown Road, Livingston Manor, NY 12758, USA at latitude: 419611318
longitude: -746524769

Feature called 4001 Tremley Point Road, Linden, NJ 07036, USA at latitude: 406109563
longitude: -742186778

Feature called 352 South Mountain Road, Wallkill, NY 12589, USA at latitude: 416802456
longitude: -742370183

Feature called Bailey Turn Road, Harriman, NY 10926, USA at latitude: 412950425
longitude: -741077389

Feature called 193-199 Wawayanda Road, Hewitt, NJ 07421, USA at latitude: 412144655
longitude: -743949739

Feature called 406-496 Ward Avenue, Pine Bush, NY 12566, USA at latitude: 415736605
longitude: -742847522

Feature called 162 Merrill Road, Highland Mills, NY 10930, USA at latitude: 413843930
longitude: -740501726

Feature called Clinton Road, West Milford, NJ 07480, USA at latitude: 410873075
longitude: -744459023

Feature called 16 Old Brook Lane, Warwick, NY 10990, USA at latitude: 412346009
longitude: -744026814

Feature called 3 Drake Lane, Pennington, NJ 08534, USA at latitude: 402948455
longitude: -747903913

Feature called 6324 8th Avenue, Brooklyn, NY 11220, USA at latitude: 406337092
longitude: -740122226

Feature called 1 Merck Access Road, Whitehouse Station, NJ 08889, USA at latitude: 406421967
longitude: -747727624

Feature called 78-98 Schalck Road, Narrowsburg, NY 12764, USA at latitude: 416318082
longitude: -749677716

Feature called 282 Lakeview Drive Road, Highland Lake, NY 12743, USA at latitude: 415301720
longitude: -748416257

Feature called 330 Evelyn Avenue, Hamilton Township, NJ 08619, USA at latitude: 402647019
longitude: -747071791

Feature called New York State Reference Route 987E, Southfields, NY 10975, USA at latitude: 412567807
longitude: -741058078

Feature called 103-271 Tempaloni Road, Ellenville, NY 12428, USA at latitude: 416855156
longitude: -744420597

Feature called 1300 Airport Road, North Brunswick Township, NJ 08902, USA at latitude: 404663628
longitude: -744820157

Feature called at latitude: 407113723
longitude: -749746483

Feature called at latitude: 402133926
longitude: -743613249

Feature called at latitude: 400273442
longitude: -741220915

Feature called at latitude: 411236786
longitude: -744070769

Feature called 211-225 Plains Road, Augusta, NJ 07822, USA at latitude: 411633782
longitude: -746784970

Feature called at latitude: 415830701
longitude: -742952812

Feature called 165 Pedersen Ridge Road, Milford, PA 18337, USA at latitude: 413447164
longitude: -748712898

Feature called 100-122 Locktown Road, Frenchtown, NJ 08825, USA at latitude: 405047245
longitude: -749800722

Feature called at latitude: 418858923
longitude: -746156790

Feature called 650-652 Willi Hill Road, Swan Lake, NY 12783, USA at latitude: 417951888
longitude: -748484944

Feature called 26 East 3rd Street, New Providence, NJ 07974, USA at latitude: 407033786
longitude: -743977337

Feature called at latitude: 417548014
longitude: -740075041

Feature called at latitude: 410395868
longitude: -744972325

Feature called at latitude: 404615353
longitude: -745129803

Feature called 611 Lawrence Avenue, Westfield, NJ 07090, USA at latitude: 406589790
longitude: -743560121

Feature called 18 Lannis Avenue, New Windsor, NY 12553, USA at latitude: 414653148
longitude: -740477477

Feature called 82-104 Amherst Avenue, Colonia, NJ 07067, USA at latitude: 405957808
longitude: -743255336

Feature called 170 Seven Lakes Drive, Sloatsburg, NY 10974, USA at latitude: 411733589
longitude: -741648093

Feature called 1270 Lakes Road, Monroe, NY 10950, USA at latitude: 412676291
longitude: -742606606

Feature called 509-535 Alphano Road, Great Meadows, NJ 07838, USA at latitude: 409224445
longitude: -748286738

Feature called 652 Garden Street, Elizabeth, NJ 07202, USA at latitude: 406523420
longitude: -742135517

Feature called 349 Sea Spray Court, Neptune City, NJ 07753, USA at latitude: 401827388
longitude: -740294537

Feature called 13-17 Stanley Street, West Milford, NJ 07480, USA at latitude: 410564152
longitude: -743685054

Feature called 47 Industrial Avenue, Teterboro, NJ 07608, USA at latitude: 408472324
longitude: -740726046

Feature called 5 White Oak Lane, Stony Point, NY 10980, USA at latitude: 412452168
longitude: -740214052

Feature called Berkshire Valley Management Area Trail, Jefferson, NJ, USA at latitude: 409146138
longitude: -746188906

Feature called 1007 Jersey Avenue, New Brunswick, NJ 08901, USA at latitude: 404701380
longitude: -744781745

Feature called 6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA at latitude: 409642566
longitude: -746017679

Feature called 1358-1474 New Jersey 57, Port Murray, NJ 07865, USA at latitude: 408031728
longitude: -748645385

Feature called 367 Prospect Road, Chester, NY 10918, USA at latitude: 413700272
longitude: -742135189

Feature called 10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA at latitude: 404310607
longitude: -740282632

Feature called 11 Ward Street, Mount Arlington, NJ 07856, USA at latitude: 409319800
longitude: -746201391

Feature called 300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA at latitude: 406685311
longitude: -742108603

Feature called 43 Dreher Road, Roscoe, NY 12776, USA at latitude: 419018117
longitude: -749142781

Feature called Swan Street, Pine Island, NY 10969, USA at latitude: 412856162
longitude: -745148837

Feature called 66 Pleasantview Avenue, Monticello, NY 12701, USA at latitude: 416560744
longitude: -746721964

Feature called at latitude: 405314270
longitude: -749836354

Feature called at latitude: 414219548
longitude: -743327440

Feature called 565 Winding Hills Road, Montgomery, NY 12549, USA at latitude: 415534177
longitude: -742900616

Feature called 231 Rocky Run Road, Glen Gardner, NJ 08826, USA at latitude: 406898530
longitude: -749127080

Feature called 100 Mount Pleasant Avenue, Newark, NJ 07104, USA at latitude: 407586880
longitude: -741670168

Feature called 517-521 Huntington Drive, Manchester Township, NJ 08759, USA at latitude: 400106455
longitude: -742870190

Feature called at latitude: 400066188
longitude: -746793294

Feature called 40 Mountain Road, Napanoch, NY 12458, USA at latitude: 418803880
longitude: -744102673

Feature called at latitude: 414204288
longitude: -747895140

Feature called at latitude: 414777405
longitude: -740615601

Feature called 48 North Road, Forestburgh, NY 12777, USA at latitude: 415464475
longitude: -747175374

Feature called at latitude: 404062378
longitude: -746376177

Feature called at latitude: 405688272
longitude: -749285130

Feature called at latitude: 400342070
longitude: -748788996

Feature called at latitude: 401809022
longitude: -744157964

Feature called 9 Thompson Avenue, Leonardo, NJ 07737, USA at latitude: 404226644
longitude: -740517141

Feature called at latitude: 410322033
longitude: -747871659

Feature called at latitude: 407100674
longitude: -747742727

Feature called 213 Bush Road, Stone Ridge, NY 12484, USA at latitude: 418811433
longitude: -741718005

Feature called at latitude: 415034302
longitude: -743850945

Feature called at latitude: 411349992
longitude: -743694161

Feature called 1-17 Bergen Court, New Brunswick, NJ 08901, USA at latitude: 404839914
longitude: -744759616

Feature called 35 Oakland Valley Road, Cuddebackville, NY 12729, USA at latitude: 414638017
longitude: -745957854

Feature called at latitude: 412127800
longitude: -740173578

Feature called at latitude: 401263460
longitude: -747964303

Feature called at latitude: 412843391
longitude: -749086026

Feature called at latitude: 418512773
longitude: -743067823

Feature called 42-102 Main Street, Belford, NJ 07718, USA at latitude: 404318328
longitude: -740835638

Feature called at latitude: 419020746
longitude: -741172328

Feature called at latitude: 404080723
longitude: -746119569

Feature called at latitude: 401012643
longitude: -744035134

Feature called at latitude: 404306372
longitude: -741079661

Feature called at latitude: 403966326
longitude: -748519297

Feature called at latitude: 405002031
longitude: -748407866

Feature called at latitude: 409532885
longitude: -742200683

Feature called at latitude: 416851321
longitude: -742674555

Feature called 3387 Richmond Terrace, Staten Island, NY 10303, USA at latitude: 406411633
longitude: -741722051

Feature called 261 Van Sickle Road, Goshen, NY 10924, USA at latitude: 413069058
longitude: -744597778

Feature called at latitude: 418465462
longitude: -746859398

Feature called at latitude: 411733222
longitude: -744228360

Feature called 3 Hasta Way, Newton, NJ 07860, USA at latitude: 410248224
longitude: -747127767

-------------- RecordRoute --------------
Visiting point latitude: 405002031
longitude: -748407866

Visiting point latitude: 400106455
longitude: -742870190

Visiting point latitude: 409532885
longitude: -742200683

Visiting point latitude: 413628156
longitude: -749015468

Visiting point latitude: 413700272
longitude: -742135189

Visiting point latitude: 406523420
longitude: -742135517

Visiting point latitude: 400273442
longitude: -741220915

Visiting point latitude: 400066188
longitude: -746793294

Visiting point latitude: 415034302
longitude: -743850945

Visiting point latitude: 412567807
longitude: -741058078

Finished trip with 10 points
Passed 10 features
Travelled 551060 meters
It took 0 seconds
-------------- RouteChat --------------
Sending First message at
Sending Second message at longitude: 1

Sending Third message at latitude: 1

Sending Fourth message at
Sending Fifth message at latitude: 1

Received message First message at
Received message Third message at latitude: 1

小结

本文主要介绍了grpc下python的基本运行方式,以及grpc的四种通信方式。

-------------阅读完毕吐槽一番吧~-------------
0%