1
- import sys
1
+ from concurrent import futures
2
+ import contextlib
3
+ import datetime
2
4
import logging
5
+ import multiprocessing
6
+ import time
7
+ import socket
8
+ import sys
3
9
10
+ import argparse
11
+ import os .path
4
12
import grpc
5
- import concurrent .futures as futures
6
13
7
- import service . common
14
+ from service import registry
8
15
9
16
# Importing the generated codes from buildproto.sh
10
17
import service .service_spec .example_service_pb2_grpc as grpc_bt_grpc
11
18
from service .service_spec .example_service_pb2 import Result
12
19
13
- logging .basicConfig (level = 10 , format = "%(asctime)s - [%(levelname)8s] - %(name)s - %(message)s" )
14
- log = logging .getLogger ("example_service" )
20
+ logging .basicConfig (level = 10 , format = "%(asctime)s - [%(levelname)8s]"
21
+ " - %(name)s - %(message)s" )
22
+ _LOGGER = logging .getLogger ("example_service" )
15
23
24
+ _ONE_DAY = datetime .timedelta (days = 1 )
25
+ _PROCESS_COUNT = multiprocessing .cpu_count ()
26
+ _THREAD_CONCURRENCY = _PROCESS_COUNT
16
27
17
28
"""
18
29
Simple arithmetic service to test the Snet Daemon (gRPC), dApp and/or Snet-CLI.
41
52
value: 924.0
42
53
"""
43
54
44
-
45
55
# Create a class to be added to the gRPC server
46
56
# derived from the protobuf codes.
47
57
class CalculatorServicer (grpc_bt_grpc .CalculatorServicer ):
@@ -50,7 +60,7 @@ def __init__(self):
50
60
self .b = 0
51
61
self .result = 0
52
62
# Just for debugging purpose.
53
- log .debug ("CalculatorServicer created" )
63
+ _LOGGER .debug ("CalculatorServicer created" )
54
64
55
65
# The method that will be exposed to the snet-cli call command.
56
66
# request: incoming data
@@ -64,7 +74,7 @@ def add(self, request, context):
64
74
self .result = Result ()
65
75
66
76
self .result .value = self .a + self .b
67
- log .debug ("add({},{})={}" .format (self .a , self .b , self .result .value ))
77
+ _LOGGER .debug ("add({},{})={}" .format (self .a , self .b , self .result .value ))
68
78
return self .result
69
79
70
80
def sub (self , request , context ):
@@ -73,7 +83,7 @@ def sub(self, request, context):
73
83
74
84
self .result = Result ()
75
85
self .result .value = self .a - self .b
76
- log .debug ("sub({},{})={}" .format (self .a , self .b , self .result .value ))
86
+ _LOGGER .debug ("sub({},{})={}" .format (self .a , self .b , self .result .value ))
77
87
return self .result
78
88
79
89
def mul (self , request , context ):
@@ -82,7 +92,7 @@ def mul(self, request, context):
82
92
83
93
self .result = Result ()
84
94
self .result .value = self .a * self .b
85
- log .debug ("mul({},{})={}" .format (self .a , self .b , self .result .value ))
95
+ _LOGGER .debug ("mul({},{})={}" .format (self .a , self .b , self .result .value ))
86
96
return self .result
87
97
88
98
def div (self , request , context ):
@@ -91,10 +101,18 @@ def div(self, request, context):
91
101
92
102
self .result = Result ()
93
103
self .result .value = self .a / self .b
94
- log .debug ("div({},{})={}" .format (self .a , self .b , self .result .value ))
104
+ _LOGGER .debug ("div({},{})={}" .format (self .a , self .b , self .result .value ))
95
105
return self .result
96
106
97
107
108
+ def wait_forever (server ):
109
+ try :
110
+ while True :
111
+ time .sleep (_ONE_DAY .total_seconds ())
112
+ except KeyboardInterrupt :
113
+ server .stop (None )
114
+
115
+
98
116
# The gRPC serve function.
99
117
#
100
118
# Params:
@@ -103,17 +121,54 @@ def div(self, request, context):
103
121
#
104
122
# Add all your classes to the server here.
105
123
# (from generated .py files by protobuf compiler)
106
- def serve (max_workers = 10 , port = 7777 ):
107
- server = grpc .server (futures .ThreadPoolExecutor (max_workers = max_workers ))
124
+ def run_server (grpc_port = 7777 ):
125
+ options = (('grpc.so_reuseport' , 1 ),)
126
+ server = grpc .server (
127
+ futures .ThreadPoolExecutor (max_workers = _THREAD_CONCURRENCY ,),
128
+ options = options )
108
129
grpc_bt_grpc .add_CalculatorServicer_to_server (CalculatorServicer (), server )
109
- server .add_insecure_port ("[::]:{}" .format (port ))
110
- return server
130
+ server .add_insecure_port ("[::]:{}" .format (grpc_port ))
131
+ server .start ()
132
+ wait_forever (server )
133
+
134
+
135
+ @contextlib .contextmanager
136
+ def reserve_port (grpc_port = 7777 ):
137
+ """Find and reserve a port for all subprocesses to use."""
138
+ sock = socket .socket (socket .AF_INET6 , socket .SOCK_STREAM )
139
+ sock .setsockopt (socket .SOL_SOCKET , socket .SO_REUSEPORT , 1 )
140
+ if sock .getsockopt (socket .SOL_SOCKET , socket .SO_REUSEPORT ) == 0 :
141
+ raise RuntimeError ("Failed to set SO_REUSEPORT." )
142
+ sock .bind (('' , grpc_port ))
143
+ try :
144
+ yield sock .getsockname ()[1 ]
145
+ finally :
146
+ sock .close ()
147
+
148
+ def main ():
149
+ """ Runs the gRPC server to communicate with the SNET Daemon. """
150
+ parser = argparse .ArgumentParser (prog = __file__ )
151
+ service_name = os .path .splitext (os .path .basename (__file__ ))[0 ]
152
+ parser .add_argument ("--grpc-port" ,
153
+ help = "port to bind gRPC service to" ,
154
+ default = registry [service_name ]['grpc' ],
155
+ type = int ,
156
+ required = False )
157
+ args = parser .parse_args (sys .argv [1 :])
158
+ with reserve_port (args .grpc_port ) as port :
159
+ _LOGGER .debug ("Binding to port '%s'" , port )
160
+ sys .stdout .flush ()
161
+ workers = []
162
+ for _ in range (_PROCESS_COUNT ):
163
+ # NOTE: It is imperative that the worker subprocesses be forked before
164
+ # any gRPC servers start up. See
165
+ # https://github.com/grpc/grpc/issues/16001 for more details.
166
+ worker = multiprocessing .Process (target = run_server , args = (port ,))
167
+ worker .start ()
168
+ workers .append (worker )
169
+ for worker in workers :
170
+ worker .join ()
111
171
112
172
113
173
if __name__ == "__main__" :
114
- """
115
- Runs the gRPC server to communicate with the Snet Daemon.
116
- """
117
- parser = service .common .common_parser (__file__ )
118
- args = parser .parse_args (sys .argv [1 :])
119
- service .common .main_loop (serve , args )
174
+ main ()
0 commit comments