This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Varför du troligen inte borde använda mikrotjänster
Monday, 29 December 2025
Mikrotjänsterna har blivit standardinställningen.
Om du vill låta mognad, så pratar du om servnät, ,, händelserna bussar, ,, fördelade spårning, M SK3 och ", oberoende användningsmöjligheter. "., om du vill se på ett företag,-, redo, MSC7, så ritar du lådor tills diagrammet ser ut som en skål med spaghetti som någon kastade mot en vitbräda.
Men här är det de korrigerandeste ungdomarna som inte får
Mikrotjänster är inte en applikationsarkitekturens uppgradering . De är en organisationell skaleringsstrategi
Om du inte redan har organisationell smärta som de löser, så är det inte bra att anamma dem tidigt. Det gör dig inte till framtiden.
Det här är inte en teorisk argumentation, det är en ingenjörskonst, och som alla affärer, så är det logiskt bara när du förstår vilka skatter du betalar och vad du får i gengäld
Jag har varit lika skyldig som vem som helst i att göra mikrotjänster utan att helt internalisera de inbyggda organisationella överkostnaderna. Det är lätt att bli fast i ordspråket och glömma att den verkliga utmaningen är att hantera komplexitet mellan lag och system.
Till slut kommer det till den äldsta mönstret inom mjukvaruingenjörskonst KISS - håll det enkelt
Mikrotjänsternas myt
Mikrotjänsterna säljs som en standardarchitektur eftersom berättelsen är lockande.
- Små tjänster är: "cleaner "
- Distribuerade system är "moderna"
- Självstyre är " fri"
- Du kan " skala senare" eftersom du började "rättM SK3
Detta ram är baklänges.
Mikrotjänsterna handlar inte i huvudsak om kodstrukturen, de handlar om vem kan ändra vad, utan att prata med vem, och hur ofta.
Om du inte har
- Flera lag som levererar självständigt
- Konfliktiga prioriteringar
- Koordineringsstockningar
- Tillvägagångsargument
- Verkliga ägandegränser
… då köper du ' du löser inte ett organisationellt problem
Arkitektur handlar i slutändan om att låta människor
Diagrammet som borde vara en varningsetikett
Ni har sett diagrammet på LinkedIn där man annonserar en Microservices kurs med alla dessa.
Beteckning: Jag hade den faktiska bilden från det men det var otäckt för mig att använda den så det ' togs bort
Det här är inte en "example architecture att kopiera
Kurser undervisar ofta mikrotjänster som diagram eftersom diagram är lättare än att undervisa operationell ansvar
Den viktiga omframställningen är den här: ":", att diagrammet inte är ett mål tillstånd. kostnadsnivå.
Varje låda är ett engagemang
- En starttid att operera
- En tjänstgörelse för att hantera
- Ett kontrakt till version
- En misslyckande mode som du nu äger
- Ett samtalsreportage som du inte kan ignorera
Pfeilerna ser imponerande ut.
Eftersom varje arrow är:
- Tillfällighet
- Partiellt misslyckande
- Förtjänningar
- Tydnout
- spåra kontextförökning
- "Works in staging
Om en arkitektur kräver detta på dag ett, så bygger du inte en produkt. Du bygger en plattformM SK2
Den dolda kostnadsmodellen för mikrotjänster
Som alla arkitektoniska beslut kommer mikrotjänsterna med komplexitetsskatt (se Att leka med kod: komplexitetsskatt. Skillnaden är att denna beskattning fördubblas över varje gräns för tjänstemänniskor , varje utplacering, , och varje misslyckande tillstånd
Deras kostnader är sällan uppenbara.
Operationskostnader
Varje tjänst vill ha:
- Dess egen byggningskanal
- Dess egen utplaceringskonfiguration
- Dess egen övervakning och varning
- Den har sin egen logning, brädstolar , springboker
- Dess eget säkerhetssätt (secrets, authM SK2 ingress
Om ett lag kan göra detta på ett bekvämt sätt, så har det inga mikrotjänster. en fördelad monolith med extra steg.
Latens och misslyckande multiplikation
I en monolith är en sändning en funktionssändning
I mikrotjänster är en ",", ett """, "" call "" och """ en förhandlad fred mellan
- nätverk
- Lade balansörer
- TLS
- Auth middleware
- Serialisering
- Tydnout
- Förtjänningar
- Backpressure
Förlustsmodi multipliceras
- Ett nedström är långsamt → uppströmstrådar staplar upp
- Tillåter förstärka last → du gör det ordentligt
- En enskild beroende fläckar → tre tjänster degraderas " slumpmässigt
Du löser inte bugs längre' t systemtillstånd.
Serialiseringsskatten som ingen talar om
Här är en kostnad som nästan aldrig diskuteras i mikrotjänsternas lobbying. serialisering och deserialisering (SerDeM SK1 överhead.
Varje gräns för tjänst betyder:
// Monolith: direct object reference
var user = _userService.GetUser(userId); // < 1μs
var tier = user.Tier; // Memory access
// Microservices: serialize → network → deserialize
var json = JsonSerializer.Serialize(user); // ~50μs for a complex object
var bytes = Encoding.UTF8.GetBytes(json); // ~10μs
// ... send over network ...
var responseJson = await response.Content.ReadAsStringAsync(); // ~20μs
var user = JsonSerializer.Deserialize<User>(responseJson); // ~70μs
var tier = user.Tier; // Finally
På varje sändning, ,, att ', ~150, μs av ren CPU överhuvudtaget innan nätverket ens är inblandad.
Multiplicera det genom en kopplingskedjan
- Orderservice deseraliserar begäran (150μsM SK1
- Orderservice serialiserar User service request (150μs)
- Användarservice deseraliserar begäran (150μs)
- Användarservice serialiserar respons (150μs)
- Orderservice deseraliserar användarens respons (150μs)
- Orderservice serialiserar Inventoryanställning (150μsM SK1
- … och så vidare
I en 5-tjänsterskedja , spenderar du ' bara på SerDe - och dess optimistiska JSON serialisering . Om du använder XMLM SK4 Protokollbuffer med reflektionen, eller ineffektiva serialiseringarMska6 multiplicera det med
Ja, kan man minska det med tekniker som Generering av JSON-källor i .NET:
[JsonSerializable(typeof(User))]
internal partial class UserJsonContext : JsonSerializerContext { }
// ~30-40% faster than reflection-based serialization
var json = JsonSerializer.Serialize(user, UserJsonContext.Default.User);
Men du, SK0, håller fortfarande på med serialiseringen, SK1. Du, SK2, har just gjort skatten lite billigare, .. I en monolith, SK4, är skatten noll
Ett verkligt exempel
Jag profilerade en gång ett adressökssystem med den här arkitekturen (all i Kubernetes behållare
ASP.NET API → Go Service (fan-out) → ASP.NET Search Service → Elasticsearch
Go-tjänsterna tog hand om data från flera datakällor och sammanlagda resultat.
Breakdown för en typisk adresssökning:
- ASP.NET API
- nätverk: ASPM SK1NET → Gå ( varierar med klusterlast
- Go Service: Deserialisera begäran (40μsM SK2
- Go Service: Lyssna ut | - | Serialisera begäran till flera backends
- Network: Gå M SK1 ASP.nätverkssökning (variesMSC4
- söktjänster: Deserialisera begäran (90μsM SK2
- söktjänster: Build Elasticsearch query
- nätverk: sök → elastisk sök | ( | varianter
- Elasticsearch: Deserialisera sökningen, utföra M SK2 serialisera resultat ( faktiska sökningar
- nätverk: Elasticsearch → sök (variesM SK3
- söktjänster: Serialisera den ES-responsen | (300 | μs | ), | karta till domainmodellen | МSK3 | serialisera |
- nätverk: sök → gå ( varierarM SK3
- Go Service: Deserialisera reaktioner från alla baksidor (150μs × NM SK3 agregatMska4 serialisera mska5μsM Ska6
- nätverk: gå → ASPM SK2 NET ( varierarMska4
- ASP.NET API
För en 3-backend-fan
- GesamtSerDe överhead : ~2 ms innan Elasticsearch ens började
- nätverk överhuvudtaget mellan behållare: ~2-3ms
- Den faktiska sökningen + affärslogik
Vi spenderade nästan lika mycket tid på SerDe som vi gjorde på den faktiska sökningen.
kostnaden var "'", inte själva Go-tjänsterna. "-", ventilatorn, - . Det var logiskt för användandet. servicegränser. Alla behållaregränser betydde serialisering.
I en monolith som gör samma ventil
- Samma Elasticsearch-sökningar
- Samma aggregeringslogik
- Zero inter-service SerDe (just method callsM SK2
- ~40% mindre total latens
Det kostar
- Skalor med objektkomplexitet (
- Komponerade med sändningsdjupet (tjänsternas kedjor)
- Brännar CPU på varje begäran (kan'kan inte kaska bort det
- Förhöjer GC-trycket (allokations churn från trådenM SK1bitskiffer
- Det läggs ihop snabbt när du gör hundratals begärningar per sekund.
I en monolit är denna kostnad noll.
Performance-myten : Durchputt vs . Skalbarhet
Här är "'" en vanlig försäljningsprång.
Detta är baklänges.
Mikrotjänsterna är långsammare för samma beräkningsresurser
Utputt (frågan per sekund per CPU: s kärna ):
- Monolith: Högare - noll SerDe, nolla nätverkskopplarM SK3 direkta metoder
- Mikrotjänster: Nedre - SerDe överhuvudtaget
Skalbarhet (förmåga att hantera fler totala begäran genom att lägga till resurser):
- Monolith: Begränsad med vertikala skalningar ( motorer
- Mikrotjänster: Horisontaal - lägga till fler fall av service som hamnar i trängsel
Mikrotjänster låter dig skala oberoende av varandra. Om dina lagertjänster behöver 10x resurser men inte dina användartjänster när du behöver det.
Men det är inte en effektiv optimering. skalningsstrategi. Och det kommer med en skatte
Moderna monoliter kan också skalas
Argumentet "microservices skalar bättre " gjorde mer vettigt i | 2010 | när servern hade |4-8 | kärnor
Moderna server har 32-128 kärnor . En enda maskin kan utföra tusentals samtida operationer effektivt
Om din monolit är byggd med moderna samtida implementeringsmönster efemerala uträttelsekoordinatörer, kan du
// Process thousands of concurrent operations in a monolith
services.AddEphemeralWorkCoordinator<TranslationRequest>(
async (request, ct) => await TranslateAsync(request, ct),
new EphemeralOptions { MaxConcurrency = Environment.ProcessorCount * 4 });
// Bounded, observable, efficient concurrent processing
// No network overhead, no SerDe, no container orchestration
// Can handle 10k+ req/sec on a single machine
Det ger dig
- Parallelism: arbetet körs samtidigt över alla kärnor
- Observerbarhet: spår i förväg /aktivt/operationen misslyckades
- Backpressure: Begränsade köer förhindrar minnes utbyte
- Noll överhead: Ingen serialisering
När du göra behöver skala bortom en maskin
- Runna flera instanser bakom en lastbalanser (statlöst horisontellt skalande)
- Använd ett utbox-mönster för async distribution
- Partition genom nyckeln (Kunde ID, regionM SK2 etc .) över instanserna
Du använder fortfarande en monolith, du använder den bara flera gånger
Poängen: Don oberoende skalering av specifika komponenter rättfärdigar den operationella överheaden.
Kognitiv belastning
Mikrotjänsterna reducerar inte komplexitet, de omfördelar det.
" enkel tjänst " behöver fortfarande kontext
- Vem kallar det?
- Vad betyder "korrigt"
- Vad händer när den
- Vad är rollbacksplanet?
- Vad är beroende grafen?
Folk underskattar detta eftersom diagrammet gömmer det.
Versionering och drift
Varje gräns blir ett kontrakt
Varje kontrakt blir ":".
- Versioneringsregler
- Kompatibilitetsgarantier
- Schema-evolutionsbegränsningar
- Koordination överhuvudtaget du låtsas att du inte hade
Du kan undvika detta ett tag med " bara skicka allting tillsammans
Det är det som är poängen. Om du använder allt tillsammans, så har du byggt en monolith. Bara en värre.
Att använda verktyg före värdet
Den tidiga utgiften är alltid densamma
- Serviceutforskning
- Hemlighetshantering
- Distribuerad spårning
- Centraliserad skogsmarkering
- Metrik och varning
- CI/CD-ပုံစံ
- Lokalt dev-berättande som inte är smärtsamt
- Abhängighetshantering
- Ett sätt att köra allt utan att gråta
Ingen av de här skeppens produktvärde.
Och de flesta lag gör det innan de bevisar att produkten förtjänar komplexiteten.
Detta är den centrala felet du betalar ceremoniskatten innan du "'" har tjänat värdet. likt dagliga standups som slösar tid utan att leverera alignment, mikrotjänster kan bli en ritualistisk arkitektur : imponerande att se på
Sammataget, så är dessa kostnader inte försvunna. De fördubblas. Och de fördublas oavsett om organisationen i första hand behövde mikrotjänster.
Vad folk glömmer
Arkitektur existerar för att hjälpa grupper av människor att ändra mjukvara på ett säkert sätt.
Att inte imponera andra ingenjörer.
Att inte möta en diagramm.
Att inte rättfärdiga ett plattformteam du inte har
Kommandets storlek och utpostningskaden är viktigare än mallkatalogen.
- Om ett lag äger hela kartan, så är en monolit vanligtvis snabbare och säkrare.
- Om du fortfarande förstår systemets slutpunkt, så kommer mikrotjänsterna att minska klarheten.
- Om du inte har stabila ägandegränser, så kommer mikrotjänsterna att tvinga koordinering via API.
Conways lag är optionell. Den gäller fysik.
Din arkitektur kommer att spegla din kommunikationsstruktur oavsett om du gillar den eller inte.
Mikrotjänsterna skapar inte självständighet. De kräver det.
I försvaret för den modulära monoliten
De flesta system borde börja som en monolit
A modulär monolith is:
- En utpostningsenhet
- Ett loppstid
- Ett ställe att debugera
- En konsekvent syn på staten
- Men med riktiga inre gränser
Det betyder:
- Domainmodul med explicita beroenden
- Klara ägande i kod
- Nej "allt referenserar allting
- Fördrag som följs inuti kodbasen
Poängen är att tjänar gränser innan du gör dem avlägsna.
En modulär monolith tvingar dig att lära dig de färdigheter som mikrotjänsterna låtsas att lösa
- Domainmodellering ( riktiga gränser , inte kartongsgränser
- Separation av bekymrningar ( inte " vi gjorde en tjänst
- Testbarhet
- byta disciplin
- Att veta vad ditt system faktiskt gör
Om du kan bygga en ren modulär monolith, så kommer mikrotjänsterna att rädda dig. De kommer att distribuera röran.
Bra monolithdesign : Pattern som skalar senare
Den rätta monoliten designen låter dig skjuta upp beslutet om mikrotjänster utan att låsa sig i
Outbox-fenstern : Async utan fördelning
Ett av de bästa exemplen är utbox-mönster - ett sätt att uppnå konsekvent och asynkt bearbetande inuti en monolith, med en ren väg till fördelningen senare
I stället för:
// Tightly coupled synchronous code
public async Task PlaceOrder(Order order)
{
await _orderRepo.Save(order);
await _emailService.SendConfirmation(order); // Blocks on external service
await _inventoryService.Reserve(order.Items); // Blocks on another service
}
Du skriver:
// Outbox pattern: write events to a table
public async Task PlaceOrder(Order order)
{
await using var transaction = await _db.Database.BeginTransactionAsync();
// Save order
await _orderRepo.Save(order);
// Write events to outbox table (same transaction)
_db.OutboxMessages.Add(new OutboxMessage
{
EventType = "OrderPlaced",
Payload = JsonSerializer.Serialize(order),
CreatedAt = DateTime.UtcNow
});
await _db.SaveChangesAsync();
await transaction.CommitAsync();
// Background worker picks up outbox events and publishes them
}
Vad detta ger dig
- Transaktionskonsistens: Erfarenheter och data sätter samman eller inte alls
- Avkoppling: E-post /inventorylogiken kör async, inte blockerar inställnings skapande
- Tillförlitlighet: Erfarenheter uppstår även om system i nedströmmen är nedåtgående
- Rent extraktionspfad: senare , byta utbox-arbetaren till Kafka/RabbitMQ utan att ändra ordningslogiken
Den SegmentCommerce-provets projekt visar detta mönster i produktionen:
// Mostlylucid.SegmentCommerce/Services/Queue/PostgresOutbox.cs
public class PostgresOutbox
{
public async Task PublishAsync<T>(string eventType, T payload)
{
var message = new OutboxMessage
{
EventType = eventType,
Payload = JsonSerializer.Serialize(payload),
CreatedAt = DateTime.UtcNow
};
_db.OutboxMessages.Add(message);
// Caller commits transaction
}
}
// Background service
public class OutboxProcessor : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var pending = await _db.OutboxMessages
.Where(m => !m.Processed)
.OrderBy(m => m.CreatedAt)
.Take(100)
.ToListAsync();
foreach (var message in pending)
{
await ProcessMessage(message);
message.Processed = true;
}
await _db.SaveChangesAsync();
await Task.Delay(1000, stoppingToken);
}
}
}
Det funkar i en enda app idag. När du behöver skala
- Ersätta den bakgrundsarbetaren med en meddelandebusförbrukare
- publicera utboxevent till Kafka/RabbitMQ istället för att processa dem lokalt
- Beställningskod inte ändras
Du har byggt mikrotjänster, färdig infrastruktur utan att betala fördelningsskatten.
Andra former som är värda att göra tidigt
- CQRS (: Separat läsa / skriva modeller även om de delar en DB
- Erfarenhetsresurser (selektiv): För kontroller - endast kritiska enheter
- Featureflagger: Decouple-deploy från utsläpp
- Hintergrundarbeten: Don ' t block requests, process async
Ingen av dessa kräver fördelning.
När mikrotjänster faktiskt är begripliga
Mikrotjänsterna är logiska när begränsningarna är riktiga
Den kritiska frågan är: "" Är vi tvungna att använda mikrotjänster? "" är vinsterna större än driftsskatten
Som att välja mellan små lokala modeller och gränslig LLM, det här handlar om att förstå var komplexiteten hör hemma och vilka misslyckande former du har råd med
Bra triggar
- Teamkontention är mätbar: Teams blockerar varandra varje vecka
- Självständiga utpostningar är nödvändiga: Release cadence differs per domain
- Skalan är ojämlik: Ett subsystem behöver 10x resurser och isolering
- Misslyckande isolering: En del måste inte ta ner resten
- Regulerande data isolering är nödvändigt: Begränsningar är inte möjliga
- Orgstrukturen är redan multi-team: Sammanvaro existerar och är stabil
Bad Triggers
- " Vi skulle kunna skala "
- "Netflix gör det
- "Best practice
- " det ' ser bra ut
- "Our monolit är stökig, så vi ska fixa det genom att dela upp den."
Om din anledning innehåller orden kraft, slutligen, eller framtida-säkra, det ' är förmodligen inte en anledning
Den tekniska verkligheten: Vad mikrotjänster faktiskt kostar
Låt oss få konkret. Här är vad som ändras när man delar en monolith på tjänster.
Rörningskedjor och kort tid
Monolith:
public async Task<Order> PlaceOrder(OrderRequest request)
{
var user = await _userService.GetUser(request.UserId);
var inventory = await _inventoryService.CheckStock(request.Items);
var price = _pricingService.Calculate(request.Items, user.Tier);
var order = new Order { /* ... */ };
await _orderRepository.Save(order);
await _emailService.SendConfirmation(order);
return order;
}
Total latens: M SK1ms (in-processuppdrag | + | 2 | DB queries
Mikrotjänster:
public async Task<Order> PlaceOrder(OrderRequest request)
{
// HTTP call to User Service (network + TLS + serialization)
var user = await _httpClient.GetAsync<User>($"http://user-service/users/{request.UserId}");
// HTTP call to Inventory Service
var inventory = await _httpClient.PostAsync<StockResult>(
"http://inventory-service/check", request.Items);
// HTTP call to Pricing Service
var price = await _httpClient.PostAsync<PriceResult>(
"http://pricing-service/calculate",
new { items = request.Items, tier = user.Tier });
// HTTP call to Order Service
var order = await _httpClient.PostAsync<Order>(
"http://order-service/orders", request);
// Event published to message bus
await _messageBus.Publish(new OrderPlaced { OrderId = order.Id });
return order;
}
Total latens: ~400ms (ockvänt optimistisk | 50–80ms per HTTP sändning i verkliga miljöer |+ | meddelandebussen publicera |
Vad du fått:
- Varje tjänst kan användas oberoendet
- Inventar och pris kan skalas separat från order
- Misslyckanden i e-mail blockerar inte orderskapningen.
Vad du betalade
- ~8x ökad latens
- 4 nya avbrytningspunkter
- Network, DNS, TLS överhead på varje sändning
- Serialisering/derialisering överhuvudtaget (se tidigare sektionen
- Bracket för återtestningslogik, kretskortsbrytare , avbrott
- Distribuerad spårning till avbugsproblem
Fehler i hantering av explosioner
handling av en monolithfel:
try
{
var order = await PlaceOrder(request);
return Ok(order);
}
catch (InsufficientStockException)
{
return BadRequest(new { error = "Out of stock" });
}
catch (Exception ex)
{
_logger.LogError(ex, "Order placement failed");
return StatusCode(500);
}
Mikrotjänsterna handling av fel
try
{
// Call User Service
var user = await _httpClient.GetAsync<User>($"http://user-service/users/{request.UserId}");
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
return NotFound(new { error = "User not found" });
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.ServiceUnavailable)
{
// Retry with exponential backoff?
// Circuit breaker opened?
// Fail fast or degrade gracefully?
_logger.LogWarning("User service unavailable, retrying...");
await Task.Delay(TimeSpan.FromMilliseconds(100));
// ... retry logic ...
}
catch (TaskCanceledException ex)
{
// Timeout - was the request processed? Do we retry?
_logger.LogError("User service timeout");
return StatusCode(503, new { error = "Service temporarily unavailable" });
}
try
{
var inventory = await _httpClient.PostAsync<StockResult>(
"http://inventory-service/check", request.Items);
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.BadRequest)
{
// Business error from remote service
var errorDetails = await ex.Content.ReadAsAsync<ErrorResponse>();
return BadRequest(new { error = errorDetails.Message });
}
catch (HttpRequestException ex)
{
// Which service failed? Network issue? Service down?
// Do we have a fallback? Cached data? Fail fast?
_logger.LogError(ex, "Inventory service failed");
// Maybe try a backup instance?
// ... more retry logic ...
}
// And repeat for every service call...
Varje nätverkssändning introducerar
- Tyddumpscenarior
- nätverksoperationer
- brist på tillgång till tjänst
- Partiella fel (ansökt skickad, svaret inte fåttM SK2
- Att försöka igen förstärker ( ditt försök skulle kunna utlösa deras försök igen )
- Distribuerade transaktionsnärmar
Deployment Koordinering
Einsatz av monolit
# Build
dotnet publish -c Release
# Run migrations
dotnet ef database update
# Deploy
docker push myapp:v1.2.3
kubectl set image deployment/myapp myapp=myapp:v1.2.3
# Rollback if needed
kubectl rollout undo deployment/myapp
Implementering av mikrotjänster:
Du' förändrar ordningsstrukturen . Nu Order inkluderar UserTier direkt (denormaliserad för prestationen).
# 1. Deploy Pricing Service v2 (understands both old and new Order format)
kubectl set image deployment/pricing-service pricing-service=pricing:v2.0.0
# 2. Wait for rollout and monitor
kubectl rollout status deployment/pricing-service
# Check metrics - is v2 working with old order format?
# 3. Deploy Order Service v2 (starts sending new format)
kubectl set image deployment/order-service order-service=order:v2.0.0
# 4. Monitor for errors
# If Pricing v2 has a bug, you can't just rollback Order service
# You have to rollback both in reverse order
# 5. Deploy User Service v2 (stops including tier in response)
# But wait - old Order Service instances might still be running!
# Need zero-downtime rollout or maintain backward compat
# 6. Eventually clean up old code paths in Pricing Service v3
# (6 months later because you're scared to remove backward compat)
Med mikrotjänster , varje förändring korsar flera tjänster. Beställningsutveckling kräver
- Kompatibilitetsfenster bakåt
- Ytterliga flaggor över tjänster
- Koordinerade utdrag
- Extended testing matrices (Service A v1 + Service B v2, Service A v~2 | |
Inget av detta är omöjligt, men det kräver disciplin, verktyg och erfarenhet som de flesta lag bara tjänar efter flera år av smärta.
Debuggingupplevelsen
Monolith bug report: "Order placement fails for premium users
// Set breakpoint in PlaceOrder
// Step through each call
// User tier is null - ah, there's the bug
// Fix, test, deploy
Mikrotjänster "Order placement fails for premium users
# 1. Check logs - which service failed?
kubectl logs -l app=order-service --tail=100
# 2. Oh, it's calling Pricing service. Check those logs
kubectl logs -l app=pricing-service --tail=100
# 3. Grep for correlation ID across all services
stern -l app=order-service,app=pricing-service,app=user-service \
| grep "correlation-id-xyz"
# 4. Reconstruct the call chain from distributed traces
# Open Jaeger/Zipkin, find the trace
# User service returned 200 OK
# Pricing service returned 400 Bad Request
# Error: "user.tier is required"
# 5. Check User service - did it send tier?
# Look at schema version - ah, User v1.2 stopped sending tier
# When did that deploy? Was Pricing service updated?
# 6. Check API contracts
# Pricing expects tier, User stopped sending it
# Who approved this change?
# 7. Fix requires coordinating two teams
# Can't just deploy a fix - need contract negotiation
Detta är verkligheten. bugs som var 5-minuter lösningar blir multiM SK1teamutredningar.
Om det låter extremt
Hur man utvecklar sig på ett säkert sätt: Monolith → Mikrotjänster
Mikrotjänster är en en- -vägsdörr. hantera dem som en-
Den säkra vägen ser tråkigt ut
1. bevisa gränser först
Om du inte kan dra gränsen inuti monoliten, kan du inte extrahera den på ett säkert sätt.
få sämarna rätt lokalt först.
2. Extraherar läser före skrivandet
Läsningen är lättare:
- Fewer invariants
- Mindre konsekventa krav
- Fewer rollback nightmares
Flytta ut läsade modeller först om du behöver separation.
3. föredrar async före synkronisering
Synkroniserade sändningar skapar beroendekedjor.
Async- meddelanden skapar:
- Buffering
- Robusthet
- Att avkoppela kan du överleva
Börja med att separera händelser och fakta
4. Strangle
Ingen stor chock.
Nej " vi ' jag skriver om det ordentligt
Släpp av en gräns
Om du kan förklara varför en tjänst existerar oberoende av andra, så borde den inte existera alls.
The Takeaway: Microservices only pay if you Understand the Trade
Mikrotjänster är inte en utgångspunkt
Kompleksitet borde vinnas. Distribuerade system är inte en badge, de är ett skuldinstrument.
Merpartningsequationen är enkel
Microservices Value = (Team Autonomy + Independent Scaling + Failure Isolation)
- (Latency Tax + SerDe Tax + Operational Overhead + Coordination Cost)
För de flesta lag - särskilt tidigt - steg- eller ettlagsprodukterM SK2teamprodukter - den högra sidan dominerar M SK4 Du betalar enorma kostnader för fördelarna du inte behöver
Men när du har
- 5+ lag som kliver på varandra
- Deployment köer mätta i dagar
- Subsystem med helt olika skalningsbehov
- Reglementära krav för dataisolationer
… Då börjar vänstra sidan vinna . Steuern blir rättfärdigd
Ett beslutsframework
Fråga dessa frågor i ordning:
Kan ett lag fortfarande äga den här änden?
→ Ja : stanna i monoliten . Nej M SK3 överväg splittringen MSC4Är lag blockerade av varandra?
→ Ja : Mikrotjänster kan hjälpaHar vi operativa muskler för att köra distribuerade system?
→ NejKan vi spåra debug, ,, , och implementera flera tjänster utan heroik?
→ Nej : duHar vi bevisat gränserna i koden först?
→ Nej : fixa din monolithstruktur
Om du inte kan "'", kan du inte svara på den tidigare frågan. Boring architecture is a competitive advantage.
De mest framgångsrika produkterna byggdes först som monoliths.
ditt jobb är att bygga den mest imponerande arkitekturen.
Och ingen kund har någonsin betalat extra eftersom ditt system använde Kafka.
Ibland betyder det mikrotjänster.