server.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. # Copyright 2019 gRPC authors.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """An example of multiprocess concurrency with gRPC."""
  15. from __future__ import absolute_import
  16. from __future__ import division
  17. from __future__ import print_function
  18. from concurrent import futures
  19. import contextlib
  20. import datetime
  21. import logging
  22. import math
  23. import multiprocessing
  24. import socket
  25. import sys
  26. import time
  27. import grpc
  28. import prime_pb2
  29. import prime_pb2_grpc
  30. _LOGGER = logging.getLogger(__name__)
  31. _ONE_DAY = datetime.timedelta(days=1)
  32. _PROCESS_COUNT = multiprocessing.cpu_count()
  33. _THREAD_CONCURRENCY = _PROCESS_COUNT
  34. def is_prime(n):
  35. for i in range(2, int(math.ceil(math.sqrt(n)))):
  36. if n % i == 0:
  37. return False
  38. else:
  39. return True
  40. class PrimeChecker(prime_pb2_grpc.PrimeCheckerServicer):
  41. def check(self, request, context):
  42. _LOGGER.info('Determining primality of %s', request.candidate)
  43. return prime_pb2.Primality(isPrime=is_prime(request.candidate))
  44. def _wait_forever(server):
  45. try:
  46. while True:
  47. time.sleep(_ONE_DAY.total_seconds())
  48. except KeyboardInterrupt:
  49. server.stop(None)
  50. def _run_server(bind_address):
  51. """Start a server in a subprocess."""
  52. _LOGGER.info('Starting new server.')
  53. options = (('grpc.so_reuseport', 1),)
  54. server = grpc.server(futures.ThreadPoolExecutor(
  55. max_workers=_THREAD_CONCURRENCY,),
  56. options=options)
  57. prime_pb2_grpc.add_PrimeCheckerServicer_to_server(PrimeChecker(), server)
  58. server.add_insecure_port(bind_address)
  59. server.start()
  60. _wait_forever(server)
  61. @contextlib.contextmanager
  62. def _reserve_port():
  63. """Find and reserve a port for all subprocesses to use."""
  64. sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
  65. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
  66. if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT) == 0:
  67. raise RuntimeError("Failed to set SO_REUSEPORT.")
  68. sock.bind(('', 0))
  69. try:
  70. yield sock.getsockname()[1]
  71. finally:
  72. sock.close()
  73. def main():
  74. with _reserve_port() as port:
  75. bind_address = 'localhost:{}'.format(port)
  76. _LOGGER.info("Binding to '%s'", bind_address)
  77. sys.stdout.flush()
  78. workers = []
  79. for _ in range(_PROCESS_COUNT):
  80. # NOTE: It is imperative that the worker subprocesses be forked before
  81. # any gRPC servers start up. See
  82. # https://github.com/grpc/grpc/issues/16001 for more details.
  83. worker = multiprocessing.Process(target=_run_server,
  84. args=(bind_address,))
  85. worker.start()
  86. workers.append(worker)
  87. for worker in workers:
  88. worker.join()
  89. if __name__ == '__main__':
  90. handler = logging.StreamHandler(sys.stdout)
  91. formatter = logging.Formatter('[PID %(process)d] %(message)s')
  92. handler.setFormatter(formatter)
  93. _LOGGER.addHandler(handler)
  94. _LOGGER.setLevel(logging.INFO)
  95. main()