FastComments.com Blog

Wed Sep 10 2025
...

FastComments вече е глобално разпределен

Какво е новото

Предишната архитектура на FastComments беше много традиционна за уеб приложение. Имахме наши сървъри с приложения, бази данни и някои други услуги. Това беше дублирано в два региона (us-west и eu). Ако сте в Франция и искате да видите тема за коментари за клиент, хостван в нашия глобален датацентър, вашите заявки трябваше да отидат чак до us-west за данните за коментари.

Вече не! Сега данните за коментари и всички медийни ресурси се репликират глобално за клиентите в нашето глобално разпределение, а за клиентите в нашето ЕС разпределение имаме три точки на присъствие в ЕС, където данните се репликират. Вашите заявки отиват до най-близкия възел в ЕС.

Как работеше преди

С изключение на базите данни, които имаха няколко активни реплики в различни региони и облачни доставчици, всички услуги бяха разположени на един екземпляр за тип услуга. Това означаваше, че за всеки регион имахме един сървър с приложение, един сървър за pubsub и един медиен сървър. Планът беше да скалираме вертикално, докато можем, тъй като това опростяваше нещата. Писането на код беше лесно - винаги знаете, че можете да "четете собствените си записи", когато стигнете до базата данни. Инфраструктурата бе лесна, с изключение на актуализации на сигурността, които отнемаха минута престой.

Проблемът

Проблемът започна, очевидно, когато достигнехме капацитета. Така че оптимизирахме и след това в крайна сметка трябваше да увеличим размера на инстанцията за тази услуга.

Това започна да става неприемливо скъпо на Linode, където инстанция от $144 е приблизително еквивалентна, на база тестовете ни, на $20 OVH възел, а дори и да преминехме към други доставчици, щяхме да имаме единични точки на провал навсякъде - и доставчици като OVH обикновено имат по-дълги времена за разрешаване от Linode за проблеми с поддръжката.

RiR :)

През първите няколко години услугите PubSub и Media в FastComments бяха написани на Java. Java беше избрана заради относително високата производителност за вложените усилия, и след години настройване на GC, пробвайки G1GC, Shenandoah и Z1, решихме, че нямаме нужда от Java повече. Прекалено много беше натоварването на паметта и тъй като тези услуги бяха много статични след завършване, ползите от Java изчезнаха. Също така, тези услуги трябваше да се справят с проблема с "громящата тълпа", което означаваше, че JVM се опитваше да обработва пикова трафик, когато JIT все още не беше активирана. Тези услуги бяха идеални кандидати за преминаване на C++ или Rust.

Избрахме Rust, тъй като не сме експерти по C++ и неправилна стъпка в мрежовия код би могла да изложи данните на един клиент на друг. Rust ни помага да предотвратим подобни проблеми.

Искахме да консолидираме тези услуги така или иначе, така че, докато можехме да направим още оптимизация, може би с GraalVM, решихме да преминем на Rust и да приключим с това.

Миграцията не мина без проблеми. Тези услуги трябва да приключат SSL, да поддържат HTTP 1.1, HTTP/2 и т.н. Те правят неща като управление на много потоци от данни едновременно, четене на медии от кеш на диска на ръба, S3, бази данни и комуникация в мрежа. Java екосистемата, Vertx и Netty, бяха много добри за тези неща. Ние натискаме библиотечната екосистема до нейните граници, а недостигът на опит с библиотеките на Rust означаваше, че имаме някои опити и грешки. Това доведе до известен престой и се извиняваме за това.

Също така експериментирахме с различни мениджъри на памет, като се спряхме на mimalloc за нашите персонализирани DNS сървъри и libc за транспортния слой. Не използваме Nginx или Apache, а вместо това използваме собствена комбинация от персонализиран DNS сървър, който маршрутира клиентите глобално на базата на индекс в паметта, който се обновява седмично от Maxmind, и нашия транспортен слой в Rust, който поддържа мрежа с другите транспортни инстанции. Транспортът приключва SSL, обработва работата с pubsub и е нашият CDN. Ползата от това е по-малко натоварване, когато прехвърляме неща между услугите и по-малко инфраструктурно натоварване/абстракция. Недостатъкът е видимостта, така че добрите метрики са важни.

Що се отнася до получената производителност, услугите на Rust използваха около 10-30% от паметта на Java, въпреки всичките ни усилия. Чета книги като Java Concurrency in Practice за забавление, така че въпреки, че не съм експерт, знам нещо или две за писането на бързи JVM услуги и беше много по-лесно да се постигне това с Rust. Освен това, пик на съобщения до голям брой абонати едва ли щеше да се отрази на натоварването на CPU, докато услугите на JVM ще прекарват 40% от времето си в GC, въпреки че пишем нашия код повече като игрови двигател и по-малко като типичен сървър. Не мога да кажа, че съм голям фен на Rust, но за услуги, които вършат много работа и не се променят много след началното развитие, е перфектен. Основната ни логика за бизнеса остава на TypeScript.

Новата архитектура

Новата архитектура вече няма "питомни" възли. Вместо това, всеки сървър е пълен клонинг с всички същите услуги и почти идентична конфигурация. Те всеки изпълняват транспорта, DNS, сървъра с приложения и копие на базата данни. Всички възли поддържат пълно шифроване на диска с автоматично отключване с Dropbear.

Всеки сървър управлява маршрутизиращия транспорт, който приключва заявките и ги обработва или като уебсокет, http поток или CDN заявка. Тези сървъри се свързват помежду си и всеки един предоставя картографиране на глобалната мрежа към своя локален DNS сървър, за да каже на DNS в реално време къде се намира всеки активен възел глобално.

Едно от предимствата на новата архитектура е излишъкът. Ако клиент в един регион ни удари наистина силно, другите региони остават онлайн.

Кодът на приложението сега трябва да бъде много наясно кои заявки могат да ударят най-близкия възел или кои трябва да отидат до основната база данни, която може да е далеч. Неправилна стъпка може да намали значително производителността. Това също означава, че записите от някои региони могат да бъдат бавни, което изисква внимателно настройване и оптимизация. Сега следваме вътрешен модел в кода, при който всички методи, които достъпват базата данни, приемат аргумент readPreference, така че извикващите, чак до най-горе, трябва да се решат изрично как да запитват.

Ползата е много добро хоризонтално мащабиране за четения. FastComments е много натоварен с четения, но не трябва да забравяме за нашите модератори! Модераторите работят ден след ден по целия свят и искаме тяхното изживяване да остава добро. Като част от тази разпространение работим с няколко от тях, за да осигурим, че инструментите за модерация остават бързи.

Можем също да избираме хардуер, което е забавно и удовлетворяващо. Новите сървъри са смесица от боксове i5-13500 и Ryzen 5 5600X, а ЕС използва стари Xeons. В нашите бенчмаркове всичките тези бяха много по-бързи от по-скъпите сървъри, които разглеждахме при други доставчици. Недостатъкът е повече работа при настройка, но автоматизирахме това, заедно с мониторинг на дисковете SMART за неизправности и др.

Правенето на подобни неща означава, че можем да продължим да предлагаме конкурентни цени.

Разпространение

Разпространението през последните няколко месеца, докато преписваме услугите и преминаваме към нови хостинг доставчици, беше неравно, благодарим ви за търпението.

При първоначалното разпространение нашите метрики показаха увеличение на заявките, отнемащи > 100ms. Опитваме се да нямаме много заявки, отнемащи толкова време.

Постепенен напредък
Бавни заявки

Все още правим постепенен напредък в подобряването на производителността за някои региони. Благодаря на всички, които дадоха обратна връзка досега.

Съображения при използване на API

API-то остава силно последователно - можете да четете собствените си записи - за да поддържаме обратно съвместимост и да опазим нещата прости за разработчиците. За да позволим на разработчиците да избират производителност пред последователност, планираме да изложим параметъра readPreference. Ползата е, че може да предложим и отстъпка за кредит за тези API повиквания.

Всички публични крайни точки, като например за обслужване на публичния коментар, винаги четат от най-близката (локална) база данни на този възел.

Защо просто не използвате обикновен CDN

Темите за коментари не са статични, те са живи и прилагането на жив поток върху остарелите статични данни също не работи, тъй като когато прегледате тема като анонимен потребител, получавате "анонисесия". В тази анонимна сесия можете да правите неща като блокирате и етикетирате други потребители, и трябва да покажете дали анонимният потребител е харесал определен коментар и така нататък. Темите за коментари могат също да бъдат заключени зад SSO, удостоверяване или потребителски групи.

Накрая, видът на "прогресивно подобрение", при който динамичните данни се свързват със статичните данни от CDN, ви дава лошо изживяване, при което съдържанието скача или се променя след няколко секунди. Предпочитаме да не правим това.

Кой има моите данни сега

Вашите данни вече не се съхраняват на Linode. Те се репликират на живо между Hetzner и OVH с пълно шифроване на диска, и цялата комуникация между сървърите в бекенда се извършва с TLS. Поддържаме няколко наследствени инстанции на Linode за изходящи проксита на уебхукове, но данни или медии не остават съхранени на Linode.

В заключение

Както при всички основни издания, сме щастливи, че можем да прекараме време в оптимизиране, тестване и правилно пускане на тази промяна. FastComments трябва да мащабира по-добре и да има по-добра наличност в дългосрочен план с тази работа. Кажете ни по-долу, ако откриете някакви проблеми.