Μια ενοχλητική ιστορία: Γιατί ο διακομιστής μου μπορούσε να χειριστεί μόνο 10 παίκτες

Αυτό που θα μπορούσε να είναι ακόμη πιο ενοχλητικό είναι ότι σε ένα σημείο είχα πειστεί ότι 10 παίκτες ανά διακομιστή ήταν φυσιολογικά.

Όλα ξεκίνησαν με μια ιδέα στις αρχές του καλοκαιριού. Στεκόμουν στο δωμάτιό μου προσπαθώντας να σκεφτώ ένα παιχνίδι io να κάνω (αποφάσισα αν έπρεπε να κάνω ένα παιχνίδι, περιορίστηκα να κάνω ένα παιχνίδι io για μέγιστο ιογενές δυναμικό - αυτό είναι πράγμα, ορκίζομαι).

Έτσι, άρχισα να αναλύω αυτό που έκανε ορισμένα παιχνίδια io (agar.io, slither.io, κ.λπ.) εθιστικά. Βρήκα συγκρίσεις και ομοιότητες μεταξύ τέτοιων παιχνιδιών, όπως φαίνεται στην παρακάτω εικόνα:

Τελικά, μετά από λίγο περισσότερη ανταλλαγή απόψεων, προσγειώθηκα στο knckout.io. Είναι το όνομα του παιχνιδιού. Προσπαθήστε να μείνετε στο χάρτη και να χτυπήσετε άλλους. Το αγάπησα. Απλά χειριστήρια, σαφής στόχος και όμορφος μηχανικός παιχνιδιών

Αφού έδειξα πώς ήθελα το παιχνίδι να φαίνεται και να αισθάνεται, πήρα στη δουλειά. Θα ερχόμουν σπίτι από το καλοκαίρι της πρακτικής μου καθημερινά, θα έκανα δουλειά και έπειτα κωδικοποίησα.

Πρώτα πήρα τον παίκτη να κινηθεί όπως ήθελα. Τότε χειρίστηκα την ενίσχυση. Τότε οι συγκρούσεις. Τελικά το παιχνίδι τελείωσε και ήταν έτοιμο να δοκιμαστεί από το κοινό. Ή έτσι σκέφτηκα…

Το περασμένο Σαββατοκύριακο (πριν από περίπου μια εβδομάδα), ήμουν όλοι εντυπωσιασμένος και έτοιμος να δείξω στον κόσμο τι έκανα. Έτσι πήγα στις διασυνδέσεις και βρήκα ένα μικρό subreddit που ονομάζεται "playmygame". Έγραψα μια σύντομη περίληψη και το δημοσίευσα (ps στα σχόλια της ανάρτησης που μπορείτε να δείτε ξεκάθαρα ότι τονίζω την ικανότητα του διακομιστή μου). Περίμενα υπομονετικά, τότε ΧΟΥΖΖΑ! Ένας παίκτης είχε εγγραφεί.

Πηγαίναμε πίσω στο παιχνίδι στο παιχνίδι. Εν τω μεταξύ, ήμουν άγχος και ανησυχούσα για το τι σκέφτηκε αυτός ο παίκτης. Αφού αυτός ο παίκτης έχασε όλη τη ζωή του και ξεκίνησε από τον αγώνα που βρισκόμασταν, περίμενα να δω αν θα επέστρεφαν. Και το έκαναν! Αλλά ακόμα καλύτερα: ο παίκτης έβαλε το όνομά του σε «ilikethisgame». Τα μάτια μου μεγάλωσαν και είχα αδρεναλίνη! Ήμουν το πιο ευτυχισμένο αγόρι στον κόσμο.

Σύντομα άλλοι παίκτες συμμετείχαν και μερικά άφησαν σχόλια για τη δημοσίευση του Reddit. Περισσότεροι παίκτες είπαν ότι είχαν απολαύσει το παιχνίδι! Ήμουν εκστατικός. Έπειτα έλεγξα πώς κρατούσε ο διακομιστής μου (στις 8/15)…

Ένιωσα σαν κάποιος να μου χτυπήσει τον αέρα. Ήταν αληθινό; Αυτό έπρεπε να είναι ψεύτικο, σκέφτηκα. Μόνο δύο παιχνίδια και ο διακομιστής δυσκολεύεται να τα επεξεργαστεί.

Άρχισα να σκέφτομαι πού πήγα στραβά στον κωδικό μου. Σκέφτηκα ότι η ανίχνευση σύγκρουσης, σίγουρα, έπρεπε να είναι το εμπόδιο. Αλλά χρησιμοποιούσα ήδη τετράγωνα για να περιορίσω τον αριθμό των διαβάσεων ανίχνευσης σύγκρουσης.

Έπρεπε να κάνω κάποια βρώμικη δουλειά, οπότε ξεκίνησα έναν νέο διακομιστή Digital Ocean για χρήση ως διακομιστής ανάπτυξης. Στη συνέχεια απενεργοποίησα προσωρινά εντελώς την ανίχνευση σύγκρουσης και είδα ότι το πρόβλημα ήταν ακόμα εκεί.

Εντάξει - εάν η ανίχνευση σύγκρουσης δεν ήταν το πρόβλημα, τι άλλο θα μπορούσε να είναι;

Σκέφτηκα πόσες πληροφορίες έστελνα από τον διακομιστή σε κάθε πελάτη κάθε δευτερόλεπτο. Είχα αυτή τη λειτουργία εκπομπής που έστειλε την κατάσταση του παιχνιδιού κάθε 22 χιλιοστά του δευτερολέπτου σε κάθε πελάτη. Σε αυτήν τη λειτουργία, φιλτράρω άσκοπα τον τοπικό παίκτη του συγκεκριμένου πελάτη σε μια allPlayersιδιότητα, για να βάλω τον τοπικό παίκτη στη δική του ιδιοκτησία. Έτσι, όχι μόνο έβαλα ένα βρόχο για το βρόχο (το φιλτράρισμα) σε ένα άλλο για το βρόχο (η εκπομπή για κάθε πελάτη), αλλά και προσαρμόζω τα δεδομένα που θα αποστέλλονται από αυτήν τη λειτουργία μετάδοσης για κάθε πελάτη.

Αυτή η προσαρμογή δεν ήταν απαραίτητη. Θα πρέπει απλώς να μπορώ να στείλω την κατάσταση του παιχνιδιού σε όλους χωρίς προσαρμογή. Όλοι πρέπει να λαμβάνουν τα ίδια δεδομένα (και τα δεδομένα δεν πρέπει να είναι προσαρμοσμένα σε συγκεκριμένο πελάτη). Αυτό έπρεπε να είναι εκεί που καταναλώνεται η CPU. Γι 'αυτό βελτιστοποίησα αυτήν τη λειτουργία, την ώθησα στον διακομιστή dev και έλεγξα το γράφημα της CPU. Χωρίς επιδιόρθωση.

Με την άγνοιά μου, άρχισα να πείσω τον εαυτό μου ότι ~ 10-20 παίκτες ανά 1 πυρήνα διακομιστή ήταν καλό. Τώρα, πώς καταλήγω σε ένα τέτοιο συμπέρασμα; Λοιπόν, η ακραία εμπιστοσύνη μου στις τεχνικές μου ικανότητες με σαφώς τυφλώνει από την πραγματικότητα. Έπεσα σε μια ανάρτηση όπου ο δημιουργός του agar.io είπε ότι ο διακομιστής 1 πυρήνα του μπορεί να χειριστεί περίπου 190 παίκτες. Γρήγορα έσπασα από αυτό.

Ο επόμενος ένοχος που παρατάξω ήταν: socket.io. Χρησιμοποιούσα το socket.io για να διαχειριστώ την επικοινωνία σε πραγματικό χρόνο μεταξύ του πελάτη και του διακομιστή. Είχα ακούσει πριν ότι το socket.io δεν ήταν τόσο ελαφρύ όσο άλλες εναλλακτικές.

Πίσω στην ημέρα, εάν θέλετε να στείλετε ένα μήνυμα ασύγχρονα, έπρεπε να εφαρμόσετε κάποιο είδος χακαρίσματος: μακρά ψηφοφορία ή υποδοχές flash. Αυτό οφείλεται στο γεγονός ότι δεν υποστηρίχθηκαν όλα τα προγράμματα περιήγησης ιστού. Ωστόσο, τα περισσότερα προγράμματα περιήγησης προσφέρουν τώρα εγγενή υποστήριξη. Αλλά για να δημιουργήσει μια σύνδεση socket.io, το κάνει πρώτα χρησιμοποιώντας ένα από τα διαθέσιμα hacks που αναφέρονται και, στη συνέχεια, αναβαθμίζει τη σύνδεση εάν ο πελάτης υποστηρίζει έναν καλύτερο τρόπο. Παρόλο που οι υποδοχές ιστού υποστηρίζονται ήδη ευρέως. Αυτή η προσέγγιση έρχεται σε βάρος της CPU και της μνήμης. Όμως όχι όπως είχα σκεφτεί ...

Πήγα στο διαδίκτυο και αφέθηκα να πληκτρολογήσω "πρόβλημα socket io cpu" στο Google. Τα πρώτα αποτελέσματα του ζευγαριού είχαν τον τίτλο "Node.js - Πώς να εντοπίσετε σφάλματα Node + Socket.io CPU Issues - Server Fault" και "Node.js - Socket.io κόμβος διακομιστή χρησιμοποιώντας υψηλή cpu - Stack Overflow." Τα μάτια μου άναψαν. Με διαβεβαίωσαν ότι αυτός ήταν ο ένοχος του προβλήματος μου. Αλλά έκανα κλικ στο πρώτο άρθρο και ο συγγραφέας ανέφερε ότι ασχολήθηκε με ~ 1.500 ταυτόχρονες συνδέσεις υποδοχών. Δεν είμαι μαθηματικός, αλλά 20 παίκτες είναι σημαντικά λιγότεροι από 1.500 παίκτες.

Ακριβώς για το καλό, άλλαξα την εφαρμογή Node από την πλευρά του διακομιστή μου για να χρησιμοποιήσω μικροσκοπικά websockets και μετά άλλαξα την εφαρμογή Node από την πλευρά του πελάτη για να χρησιμοποιήσω εγγενή υποστήριξη websocket, ακριβώς μέσα στο πρόγραμμα περιήγησης. Έχω ωθήσει τις αλλαγές στον διακομιστή dev και έλεγξα το γράφημα της CPU. Χωρίς επιδιόρθωση.

Το ηθικό μου ήταν πάντα χαμηλό. Άρχισα να τσαλακώνω κάθε φορά που έπρεπε να ελέγξω το διάγραμμα CPU. Σκέφτηκα ότι δεν θα έπαιρνα ποτέ αυτή την μπλε γραμμή για να σταματήσω να τρέχω μακριά μου Αυτή ήταν η μόνη φορά που ένιωθα εντελώς ανίκανη να χειριστώ κάποια τεχνική εργασία. Αλλά τότε συνέβη…

Καθόμουν μπροστά από το γράφημα της CPU που βρισκόταν στη δυστυχία μου όταν παρατήρησα κάτι. Δεν είχε σημασία πόσα πλήρη παιχνίδια τρέχουν ή πόσο κοντά ήταν όλα ξεκίνησαν. Η CPU αυξανόταν σταθερά με σταθερό ρυθμό. Ποτέ δεν είχα μείνει αρκετά για να το παρατηρήσω. Ελλειψη μνήμης!

Σάρωσα τον κωδικό μου, γραμμή προς γραμμή, αναζητώντας το σφάλμα (το οποίο θα έπρεπε να είχα κάνει στην αρχή). Εκεί ήταν.

Στο παιχνίδι μου, ένα συμβάν είναι ένα αντικείμενο που συλλαμβάνει πληροφορίες σχετικά με πράγματα όπως θανάτους παικτών, ενισχύσεις και συγκρούσεις. Έτσι δημιουργείται ένα συμβάν κάθε φορά που συμβαίνει ένα από αυτά τα πράγματα.

Έχω αυτόν τον βρόχο που περνά σε κάθε συμβάν και το ενημερώνει. Ονομάζεται κάθε 16 ms. Αφού ένα συμβάν εκπληρώσει το καθήκον του, υποτίθεται ότι πρέπει να διαγραφεί. Λέξεις-κλειδιά: "υποτίθεται."

Λοταρία. Είχα τη μνήμη να συσσωρεύεται καθώς και μια αυξανόμενη ποσότητα περιττών περάσεων για βρόχο. Έχω εισαγάγει μια γραμμή κώδικα και voila!

Τεράστια ανακούφιση ανακούφισης.

Η επόμενη δουλειά μου είναι να δω πόσα παιχνίδια (4 παίκτες ανά παιχνίδι) μπορεί να υποστηρίξει ο ένας ο διακομιστής ομαλά. (Ξέρω ότι είναι τουλάχιστον 12 παιχνίδια, αλλά δεν έχω δοκιμάσει ακόμη περισσότερα). Τώρα που ξέρω ότι ο αριθμός των γεγονότων έχει τεράστιο αντίκτυπο στην CPU… τι θα συμβεί στην παραγωγή όταν όλοι οι παίκτες ξεκινούν τα γεγονότα ώθησης, σύγκρουσης και θανάτου κάθε δευτερόλεπτο; Οι δοκιμές μου δεν έχουν εξηγήσει αυτό.

Επίσης, αφού αυτή η ανάρτηση γίνει viral, και το παιχνίδι μου ακολουθεί το ίδιο, θα χρειαστεί να κλιμακώσω γρήγορα τον αριθμό των διαθέσιμων διακομιστών. Θα κάνω αυτό το θέμα μιας μελλοντικής ανάρτησης μαζί με: «Πώς το knckout.io μεγάλωσε σε εκατομμύρια παίκτες». Ακολουθήστε με εδώ για ενημερώσεις. :)