I have a multiplayer game based on microservices architecture which I am trying to figure how to scale horizontally. It is currently orchestrated in Docker Swarm but I am considering moving to Kubernetes.
Here are the details about the game:
As it works now, I have a single container that is responsible for all tables. When a player joins the table, he sits down and establishes a websocket connection that is routed to that particular container. All players on all tables are connected to the same container. The game logic and the game events can be easily pushed to all clients.
It's currently like that. All clients that sit on the same table have a connection to the same container, so it's easy to push dynamic game data back and forth.
Client 1+
| Container A +Client 3
| +---------------+ |
+---> |---------------| <----+
|| Table 1 || |Client 4
Client 2+----> |---------------| <----+
|---------------|
|| Table 2 ||
|---------------|
|---------------|
|| Table 3 ||
+---------------+
| . |
| . |
| . |
+---------------+
However, when you try to scale this by just increasing the number of containers you run into the problem that clients sitting on the same table are connected to different containers. This means that every game action and all shared dynamic game data have to be updated in a database sitting between these containers. However this becomes increasingly hard to write and maintain:
Container 1 Container 2
Client 1+ +-------------+ +-------------+ +Client 3
+----> |-------------| |-------------| <------+
|| Table 1 || || Table 1 ||
+----> |-------------| |-------------| <------+Client 4
Cleint 2+ |-------------| |-------------|
|| Table 2 || || Table 2 ||
+-------------+ +-------------+
| | | |
| | | |
| | | |
+----+--------+ +-----------+-+
| |
| |
| |
| +------------------------+ |
+> | Redis DB | <+
+------------------------+
Rather than designing the components like that, it would be much simpler to somehow route clients that have to sit on the same table to the same container. This is to avoid writing every player action and every public table update into the DB. It would look like this:
Game Service
+-----------------+
Client 1+ | | + Client 3
| | Container 1 | |
+------> +-----------+ <-------+
| |-----------| |
Client 2 +-----> || Table 1 || <-------+ Client 4
| |-----------| |
| |-----------| |
| || Table 2 || |
| |-----------| |
| +-----------+ |
| |
| Container 2 |
| +-----------+ |
| |-----------| |
| || Table 3 || |
| |-----------| |
| |-----------| |
| || Table 4 || |
| |-----------| |
| +-----------+ |
| |
+-----------------+
Having the above architecture would dramatically decrease the complexity of the app. The problem is that connections coming from different clients have to be identified and routed to the correct container. I haven't found a way to do that. Is routing to specific containers within the service possible and with what tool?
What is the correct approach to use in my scenario? Also, if manually routing requests to the target container is not a viable option, what would be the correct way to architect this service?
This can be achieved with help of 3rd party libraries, like Istio.
https://istio.io/docs/tasks/traffic-management/request-routing/
You will have to define VirtualServices depending on your config. For your game services you should use StatefulSet, by doing this you will be able to identify to which service you need to route your traffic.