README.md
1## Multiprocessing with gRPC Python
2
3Multiprocessing allows application developers to sidestep the Python global
4interpreter lock and achieve true parallelism on multicore systems.
5Unfortunately, using multiprocessing and gRPC Python is not yet as simple as
6instantiating your server with a `futures.ProcessPoolExecutor`.
7
8The library is implemented as a C extension, maintaining much of the state that
9drives the system in native code. As such, upon calling
10[`fork`](http://man7.org/linux/man-pages/man2/fork.2.html), any threads in a
11critical section may leave the state of the gRPC library invalid in the child
12process. See this [excellent research
13paper](https://www.microsoft.com/en-us/research/uploads/prod/2019/04/fork-hotos19.pdf)
14for a thorough discussion of the topic.
15
16Calling `fork` without `exec` in your process *is* supported
17before any gRPC servers have been instantiated. Application developers can
18take advantage of this to parallelize their CPU-intensive operations.
19
20## Calculating Prime Numbers with Multiple Processes
21
22This example calculates the first 10,000 prime numbers as an RPC. We instantiate
23one server per subprocess, balancing requests between the servers using the
24[`SO_REUSEPORT`](https://lwn.net/Articles/542629/) socket option.
25
26```python
27_PROCESS_COUNT = multiprocessing.cpu_count()
28```
29
30On the server side, we detect the number of CPUs available on the system and
31spawn exactly that many child processes. If we spin up fewer, we won't be taking
32full advantage of the hardware resources available.
33
34## Running the Example
35
36To run the server,
37[ensure `bazel` is installed](https://docs.bazel.build/versions/master/install.html)
38and run:
39
40```
41bazel run //examples/python/multiprocessing:server &
42```
43
44Note the address at which the server is running. For example,
45
46```
47...
48[PID 107153] Binding to '[::]:33915'
49[PID 107507] Starting new server.
50[PID 107508] Starting new server.
51...
52```
53
54Note that several servers have been started, each with its own PID.
55
56Now, start the client by running
57
58```
59bazel run //examples/python/multiprocessing:client -- [SERVER_ADDRESS]
60```
61
62For example,
63
64```
65bazel run //examples/python/multiprocessing:client -- [::]:33915
66```
67
68Alternatively, generate code using the following and then run the client and server
69directly:
70
71```python
72cd examples/python/helloworld
73python -m grpc_tools.protoc -I . prime.proto --python_out=. --grpc_python_out=.
74```
75