Taktyczny system dowodzenia i kontroli to aplikacja rozproszona działająca między radiami, pojazdami, operatorami pieszymi i serwerami zaplecza. Szyna wiadomości to kręgosłup. Wybierz złą i system wydaje się szybki w laboratorium, a umiera na spornym łączu. Wybierz właściwą i operator widzi odświeżenie łączonego obrazu w swoim cyklu decyzyjnym.
Artykuł przechodzi przez czterech kandydatów faktycznie pojawiających się w produkcyjnych buildach C2 — NATS, Apache Kafka, MQTT i RabbitMQ — i przedstawia ramę decyzyjną wyboru między nimi. Wersja krótka: nie ma jednej odpowiedzi. Realne systemy używają dwóch lub trzech, połączonych mostkami.
1. Problem wiadomości taktycznych
Sieci taktyczne to nie sieci centrów danych. Pasmo na typowym radiu VHF mierzy się w kilobitach na sekundę, nie megabitach. Czasy obiegu w MANET (mobile ad-hoc network) rutynowo przekraczają 500 ms. Strata pakietów powyżej 20% jest normalna pod zagłuszaniem. Łącza flapują, gdy platformy chowają się za terenem. Backhaul satkom jest kontestowany w porannym push i przy ostatnim świetle.
Operator nie toleruje przestarzałych danych. Łączony track sprzed dwóch minut jest gorszy niż brak tracka — pewnie kłamie o pozycji zagrożenia. Szyna musi więc wymuszać wygasanie wiadomości, priorytetyzować świeży stan nad zaległościami i degradować się płynnie, gdy łącze wraca po partycji, zamiast wysypywać dziesięć minut zakolejkowanej telemetrii naraz.
Kolejność też ma znaczenie, ale nie jednolite. Telemetrię można zlewać (liczy się tylko najnowsza pozycja). Komend nie można — "weapons hold" wydane w T+5 nie może zostać wyprzedzone przez "weapons free" wydane w T+3, które dotarło z opóźnieniem. Szyna potrzebuje różnej semantyki dostarczania per topic, nie jednej globalnej gwarancji.
Wreszcie szyna musi przeżyć partycję. Gdy łącze radiowe wraca po pięciominutowym dropie, trzy zachowania są błędne: wysypanie każdej zakolejkowanej wiadomości naraz (przytłacza konsumenta), ciche odrzucenie wszystkiego (gubi uporządkowane komendy) i przemieszanie podczas nadrabiania (dostarcza przestarzałe weapons-free po świeżym weapons-hold). Prawidłowe zachowanie jest per-topic: zlewanie przy odzyskaniu dla telemetrii, uporządkowane spuszczanie z timestampami dla komend, pełne odtwarzanie dla logów audytowych. Żaden pojedynczy tryb dostarczania nie spełnia wszystkich trzech.
2. NATS i JetStream
NATS to mała, opiniotwórcza szyna pub-sub napisana w Go. Jeden binarny, brak zewnętrznych zależności, domyślne tematy w pamięci i opóźnienie publish-to-deliver poniżej milisekundy w LAN. Footprint w dziesiątkach megabajtów — wystarczająco mały na klocek obliczeniowy w pojeździe lub utwardzony węzeł krawędzi.
Core NATS jest fire-and-forget. JetStream to warstwa persistencji dodana w 2020: trwałe streamy, odtwarzanie po sekwencji lub czasie, kursory konsumentów, wygasanie wiadomości i okna deduplikacji per subject. JetStream używa Raft do replikacji. Trzyniodowy klaster JetStream to standardowe wdrożenie rdzenia taktycznego — kworum przeżywa utratę jednego węzła, a streamy replikują się bez osobnego koordynatora w stylu Zookeepera.
NATS wygrywa, gdy dominującym ruchem są małe, częste, niskolatencyjne wiadomości między usługami — komendy, aktualizacje łączonych tracków, RPC między mikrousługami przez tematy request-reply. To domyślna szyna ruchu usługa-do-usługi wewnątrz silnika fuzji.
Gdzie się załamuje: replikacja JetStream jest doskonała wewnątrz klastra, ale nie jest projektowana, by obejmować zdegradowany WAN. Węzły leaf mogą rozszerzyć topologię NATS na urządzenia krawędzi, ale jeśli leaf jest offline godzinami, okno nadrabiania jest ograniczone retencją streama — nie oczekiwaniami leafa. Traktuj NATS jako szynę rdzenia, nie szynę szerokiego obszaru.
Kompromis tolerancji awarii wart nazwania: kworum Raft JetStream wymaga większości replik do potwierdzenia zapisu. W klastrze trzyniodowym to dwa acki. Jeśli jeden węzeł jest na konserwacji, a drugi straci dysk, zapisy stają — klaster preserwuje spójność kosztem dostępności. Dla rdzenia taktycznego to właściwy wybór; spójność obrazu operacyjnego nie podlega negocjacji. Ale wzorzec operatora ma znaczenie: nie uruchamiaj trzyniodowych klastrów JetStream, gdzie dwa węzły dzielą pojedynczy punkt awarii jak jeden switch lub jedno zasilanie.
3. Apache Kafka
Kafka jest mistrzem trwałości. Log append-only per partycja, współczynnik replikacji konfigurowalny per topic, retencja mierzona w dniach lub tygodniach, model konsumenta pozwalający nowym klientom odtworzyć historię od offsetu zero. Dla after-action review, logowania audytowego i analityki nad historycznymi danymi operacyjnymi Kafka jest prawie zawsze właściwą odpowiedzią.
Jest też kosztowna. Produkcyjny klaster Kafka chce minimum trzech brokerów, szybkich lokalnych dysków, gigabajtów page cache i albo Zookeepera (legacy), albo KRaft (obecne, od Kafki 3.3 GA z końca 2022, domyślne w 3.5+). Rebalansowanie partycji pod partycją sieci to znane zagrożenie operacyjne. Koordynacja grup konsumentów zakłada stabilne połączenie z brokerem-koordynatorem grupy.
Wzorzec "Kafka do wszystkiego", który działa w sklepach cloud-native, zawodzi na krawędzi taktycznej z trzech powodów. Po pierwsze, footprint zasobów jest błędny — broker na JVM na bezwentylatorowej skrzynce krawędziowej przegrywa z binarką NATS za każdym razem. Po drugie, domyślna silna trwałość Kafki karze cię na łączu o wysokiej stracie: producenci stają, czekając na acki. Po trzecie, złożoność operacyjna (konfiguracja brokera, strategia partycjonowania tematów, strojenie retencji, monitorowanie ISR) jest niemożliwa do uzasadnienia, gdy skrzynka jest bezobsługowa w wysuniętej pozycji.
Kafka należy do warstwy strategicznej — klastra na zapleczu, który przyjmuje zagregowane streamy zdarzeń z wysuniętych bramek i serwuje je do analityki, potoków danych treningowych i archiwów długoterminowych.
4. MQTT
MQTT zaprojektowano w 1999 dla telemetrii rurociągów naftowych przez łącza satelitarne — dokładnie ten profil sieci, który dzisiejsza taktyczna sieć czujników prezentuje. Maleńki narzut nagłówka (2-bajtowy nagłówek stały w minimalnym przypadku), trzy poziomy QoS (0 fire-and-forget, 1 at-least-once, 2 exactly-once) i hierarchia tematów naturalnie mapująca się na strukturę czujnik → jednostka → szczebel.
MQTT 5.0, sfinalizowane w 2019, dodało funkcje czyniące go operacyjnie poważnym dla obronności. Współdzielone subskrypcje ($share/group/topic) równoważą topic między grupą konsumentów — przydatne dla fan-outu przetwarzania danych czujników. Interwały wygasania wiadomości automatycznie odrzucają przestarzałe dane taktyczne na brokerze. Właściwości użytkownika niosą etykiety klauzul i znaczniki uwolnienia jako metadane wiadomości. Aliasy tematów kompresują powtarzane długie ciągi tematów do jednego bajtu po pierwszej publikacji — realna wygrana na radiu 9600 bps.
Strona brokera jest dojrzała: Mosquitto dla małych footprintów, EMQX lub HiveMQ dla większych klastrowych wdrożeń ze współdzielonymi subskrypcjami i mostkowaniem. Wszystkie trzy działają na sprzęcie klasy krawędzi. MQTT-SN (Sensor Networks) rozszerza protokół na transporty inne niż TCP dla naprawdę maleńkich — czujników bateryjnych bez stosu IP.
Słabością MQTT jest trwałość. Trwałe sesje i QoS 2 dają niezawodne dostarczenie do znanego klienta, ale MQTT nie jest logiem zdarzeń — nie ma semantyki replay-by-offset. Jeśli konsument rozłączy się po wygaśnięciu sesji, wiadomości znikają. Dla telemetrii czujników to akceptowalne. Dla ścieżki audytowej nie.
5. RabbitMQ i AMQP
RabbitMQ poprzedza falę cloud-native messaging i wciąż zarabia swoje miejsce. Model AMQP 0-9-1 — exchanges, bindings, queues — daje elastyczność routowania, której szyny pub-sub nie dorównują. Topic exchanges z wildcardowymi bindingami, header exchanges dla routowania opartego na zawartości, dead-letter queues dla wiadomości, które zawiodły, TTL per kolejka i potwierdzanie per wiadomość z licznikami redostarczeń.
Dla workflow, gdzie wiadomość musi być przetworzona dokładnie raz przez dokładnie jednego workera, z jawnym ack i semantyką ponawiania, RabbitMQ to wciąż najczystsza odpowiedź. Przykłady w stosie C2: workflow zadaniowania, gdzie każde zadanie idzie do jednego operatora, żądania geokodowania trafiające do zewnętrznego serwisu, joby OCR na pozyskanych obrazach. To problemy kolejek, nie streamów, a semantyka kolejek to to, co RabbitMQ robi.
Obserwowalność to druga cicha siła — interfejs zarządczy, eksporter Prometheus i metryki per kolejka czynią go najłatwiejszym z czterech do obsługi o 03:00, gdy coś jest nie tak. Dla małego zespołu operacyjnego prowadzącego bezobsługową chmurę taktyczną to ma znaczenie.
Granice RabbitMQ pojawiają się przy bardzo wysokiej przepustowości (to nie szyna miliona wiadomości na sekundę) i na chwiejnych sieciach (model AMQP zorientowany na połączenia nie lubi flapowania łączy). Używaj go do warstwy workflow, nie do strumienia telemetrii.
6. Mostkowanie szyn
Produkcyjne systemy C2 uruchamiają dwie lub trzy szyny jednocześnie. Reprezentatywne wdrożenie: MQTT na krawędzi dla ruchu czujników i radiowego, NATS w rdzeniu taktycznym dla komend między usługami i łączonych tracków, Kafka w warstwie strategicznej dla trwałego archiwum zdarzeń. RabbitMQ może pojawić się obok NATS dla warstwy workflow.
Mosty są komponentami pierwszej klasy, nie dodatkiem. Bramka MQTT-do-NATS subskrybuje wybrane tematy MQTT, transformuje payload do kanonicznego schematu wewnętrznego i ponownie publikuje na subject NATS. Most NATS-do-Kafka konsumuje streamy JetStream i produkuje na tematy Kafki z tą samą strategią klucza partycji. Tłumaczenie schematu, obsługa backpressure i idempotentne ponowne publikowanie przy restarcie mostu to trudne części — nie samo połączenie.
Buduj mosty z tą samą dyscypliną inżynierską co każdą inną usługę: health checks, metryki, zdefiniowana procedura odtwarzania przy restarcie i jasna własność. Klasyczny tryb awarii to most, który po cichu odrzuca wiadomości pod obciążeniem, bo jego wewnętrzna kolejka się przelała.
7. Bezpieczeństwo i klauzula
Każda szyna mówi TLS. Każda szyna wspiera mutual TLS z certyfikatami klienta. To konieczne, nie wystarczające.
Izolacja per enklawa to kolejna warstwa: oddzielna instancja brokera z oddzielnym urzędem certyfikacji dla każdego poziomu klauzuli. Szyna wewnątrz enklawy SECRET nigdy nie rozmawia bezpośrednio z szyną wewnątrz enklawy UNCLASSIFIED. Uwolnienie międzydomenowe przechodzi przez zatwierdzony guard lub cross-domain solution, który strippuje, transformuje i ponownie publikuje — nigdy przez most brokerów.
ACL per topic to trzecia warstwa. W NATS — konta i uprawnienia subject. W MQTT — pliki ACL brokera lub plugin. W Kafce — ACL przez AdminClient API. W RabbitMQ — uprawnienia user-vhost-resource. Default-deny to jedyna akceptowalna postawa: usługa może publikować i subskrybować dokładnie te tematy, których wymaga jej rola, i żadne inne.
Metadane wiadomości niosą etykiety klauzuli — właściwości użytkownika MQTT 5, nagłówki NATS, nagłówki Kafki. Broker nie wymusza semantyki klauzuli; robią to usługi konsumujące i cross-domain guard. Broker wymusza, kto może czytać jaki topic.
Kluczowy wniosek: Szyna wiadomości to część granicy bezpieczeństwa, nie odrębna od niej. Traktuj konfigurację brokera — ACL, TLS, izolację kont — z tym samym rygorem co projekt aplikacji offline-first i zgodność symboliki. Źle skonfigurowana ACL to wyciek klauzuli czekający, by się wydarzyć.
8. Rama decyzyjna
Oceń każdą klasę ruchu na czterech osiach:
Budżet opóźnień. Sub-milisekundowy RPC usługa-do-usługi: NATS. Dziesiątki milisekund dla telemetrii czujników: MQTT. Sekundy dla ingestu archiwum: Kafka. Per-wiadomość kroki workflow z semantyką ack: RabbitMQ.
Przepustowość. Do ~10k wiadomości/s per topic na umiarkowanym sprzęcie: dowolna z czterech. 100k+ utrzymywanych per topic: NATS lub Kafka. Miliony przez wiele tematów: Kafka. Fan-in czujników z tysięcy klientów o niskiej częstotliwości: MQTT ze współdzielonymi subskrypcjami.
Trwałość. Brak wymogu odtwarzania: core NATS lub MQTT QoS 0/1. Odtwarzanie w sesji lub krótkim oknie: NATS JetStream, trwałe sesje MQTT. Wielodniowe odtwarzanie klasy audytu: Kafka. Per-wiadomość ack z ponawianiem i dead-letter: RabbitMQ.
Realia sieci krawędzi. Radio 9600 bps z 30% stratą: MQTT z aliasami tematów i QoS 1. Taktyczny LAN wewnątrz pojazdu: NATS. Strategiczny WAN do klastra na zapleczu: Kafka z bramką z przodu. Przerywany satkom: MQTT dla telemetrii, asynchroniczny producent Kafki z lokalnym spool dla archiwum.
Zbuduj macierz dla swojego konkretnego systemu. Każda klasa ruchu mapuje się na jedną szynę. Mosty między nimi są jawne. Wdrożenie uruchamia szyny, których potrzebuje, i nie więcej — dodanie szyny ma koszt operacyjny, a koszt ten płaci się każdą zmianą, nie tylko w czasie integracji.