Feature Flag med Spring

1. Oversigt

I denne artikel definerer vi kort funktionsflag og foreslår en meningsfuld og pragmatisk tilgang til at implementere dem i Spring Boot-applikationer. Derefter vil vi grave i mere sofistikerede iterationer, der udnytter forskellige Spring Boot-funktioner.

Vi diskuterer forskellige scenarier, der muligvis kræver flagning af funktioner, og vi taler om mulige løsninger. Vi gør dette ved hjælp af et eksempel på en Bitcoin Miner-applikation.

2. Funktionsflag

Feature Flags - undertiden kaldet funktionsskiftere - er en mekanisme, der giver os mulighed for at aktivere eller deaktivere specifik funktionalitet i vores applikation uden at skulle ændre kode eller ideelt set omdeplacere vores app.

Afhængigt af den dynamik, der kræves af et givet funktionsflag, skal vi muligvis konfigurere dem globalt, pr. Appinstans eller mere detaljeret - måske pr. Bruger eller anmodning.

Som med mange situationer inden for softwareudvikling er det vigtigt at prøve at bruge den mest enkle tilgang, der tackler det aktuelle problem uden at tilføje unødvendig kompleksitet.

Funktionsflag er et potent værktøj, der, når det bruges klogt, kan give vores system pålidelighed og stabilitet. Men når de misbruges eller underholdes, kan de hurtigt blive kilder til kompleksitet og hovedpine.

Der er mange scenarier, hvor funktionsflag kan være nyttige:

Trunk-baseret udvikling og ikke-trivielle funktioner

I trunkbaseret udvikling, især når vi vil fortsætte med at integrere ofte, er vi måske ikke klar til at frigive et bestemt stykke funktionalitet. Funktionsflag kan være nyttige, så vi kan fortsætte med at frigive uden at gøre vores ændringer tilgængelige, indtil de er gennemført.

Miljøspecifik konfiguration

Vi kan komme til at kræve en vis funktionalitet for at nulstille vores DB til et E2E-testmiljø.

Alternativt kan det være nødvendigt at vi bruger en anden sikkerhedskonfiguration til ikke-produktionsmiljøer end den, der bruges i produktionsmiljøet.

Derfor kunne vi drage fordel af funktionsflag til at skifte den rigtige opsætning i det rigtige miljø.

A / B-test

At frigive flere løsninger til det samme problem og måle effekten er en overbevisende teknik, som vi kunne implementere ved hjælp af funktionsflag.

Kanariefri frigivelse

Når vi implementerer nye funktioner, kan vi beslutte at gøre det gradvist, begyndende med en lille gruppe brugere og udvide dets anvendelse, når vi validerer rigtigheden af ​​dens adfærd. Funktionsflag giver os mulighed for at opnå dette.

I de følgende sektioner vil vi forsøge at give en praktisk tilgang til at tackle de ovennævnte scenarier.

Lad os nedbryde forskellige strategier for at markere funktioner, startende med det enkleste scenario for derefter at gå ind i en mere detaljeret og mere kompleks opsætning.

3. Funktionsflag på applikationsniveau

Hvis vi har brug for at tackle nogen af ​​de to første brugssager, er flag på funktioner på applikationsniveau en enkel måde at få tingene til at fungere.

Et simpelt funktionsflag vil typisk involvere en ejendom og en eller anden konfiguration baseret på værdien af ​​denne ejendom.

3.1. Funktionsflag ved hjælp af forårsprofiler

Om foråret kan vi drage fordel af profiler. Bekvemt giver profiler os mulighed for at konfigurere bestemte bønner selektivt. Med et par konstruktioner omkring dem kan vi hurtigt skabe en enkel og elegant løsning til funktionsflag på applikationsniveau.

Lad os lade som om vi bygger et BitCoin-minesystem. Vores software er allerede i produktion, og vi har til opgave at oprette en eksperimentel, forbedret minedriftsalgoritme.

I vores JavaConfig vi kunne profilere vores komponenter:

@Configuration public class ProfiledMiningConfig {@Bean @Profile ("! Experimental-miner") offentlig BitcoinMiner defaultMiner () {returner ny DefaultBitcoinMiner (); } @Bean @Profile ("eksperimentel-minearbejder") offentlig BitcoinMiner eksperimentalMiner () {returner ny ExperimentalBitcoinMiner (); }}

Derefter, med den tidligere konfiguration er vi simpelthen nødt til at medtage vores profil for at tilvælge vores nye funktionalitet. Der er mange måder at konfigurere vores app generelt og aktivere profiler i særdeleshed. Ligeledes er der testværktøjer for at gøre vores liv lettere.

Så længe vores system er enkelt nok, vi kunne derefter oprette en miljøbaseret konfiguration for at bestemme, hvilke funktioner der skal anvendes, og hvilke der skal ignoreres.

Lad os forestille os, at vi har et nyt brugergrænseflade baseret på kort i stedet for borde sammen med den tidligere eksperimentelle miner.

Vi vil gerne aktivere begge funktioner i vores acceptmiljø (UAT). Vi kunne oprette en ansøgning-uat.yml fil:

forår: profiler: inkluderer: eksperimentel-minearbejder, ui-kort # Mere konfiguration her

Med den forrige fil på plads, skal vi bare aktivere UAT-profilen i UAT-miljøet for at få det ønskede sæt funktioner.

Det er også vigtigt at forstå, hvordan man drager fordel af spring.profiles.include. Sammenlignet med spring.profiles.active, førstnævnte giver os mulighed for at inkludere profiler på en additiv måde.

I vores tilfælde vil vi have uat profil også for at inkludere eksperimentel minearbejder og ui-kort.

3.2. Funktionsflag ved hjælp af brugerdefinerede egenskaber

Profiler er en fantastisk og enkel måde at få arbejdet gjort på. Vi kan dog kræve profiler til andre formål. Eller måske vil vi måske opbygge en mere struktureret funktionsflagsinfrastruktur.

I disse scenarier kan brugerdefinerede egenskaber være en ønskelig mulighed.

Lad os omskrive vores tidligere eksempel ved at udnytte @ConditionalOnProperty og vores navneområde:

@Configuration public class CustomPropsMiningConfig {@Bean @ConditionalOnProperty (name = "features.miner.experimental", matchIfMissing = true) public BitcoinMiner defaultMiner () {returner nye DefaultBitcoinMiner (); } @Bean @ConditionalOnProperty (name = "features.miner.experimental") offentlig BitcoinMiner experimentalMiner () {returner ny ExperimentalBitcoinMiner (); }}

Det foregående eksempel bygger oven på Spring Boot's betingede konfiguration og konfigurerer en eller anden komponent afhængigt af om ejendommen er indstillet til rigtigt eller falsk (eller udeladt helt).

Resultatet ligner meget det i 3.1, men nu har vi vores navneområde. At have vores navneområde giver os mulighed for at oprette meningsfulde YAML / egenskabsfiler:

# [...] Nogle Spring-konfigurationsfunktioner: minearbejder: eksperimentel: sand ui: kort: sand # [...] Andre funktionsflag

Denne nye opsætning giver os også mulighed for at prefikse vores funktionsflag - i vores tilfælde ved hjælp af funktioner præfiks.

Det kan virke som en lille detalje, men når vores applikation vokser og kompleksiteten øges, vil denne enkle iteration hjælpe os med at holde vores funktionsflag under kontrol.

Lad os tale om andre fordele ved denne tilgang.

3.3. Ved brug af @ConfigurationProperties

Så snart vi får et præfikset sæt egenskaber, kan vi oprette en POJO dekoreret med @ConfigurationProperties for at få et programmatisk håndtag i vores kode.

Efter vores igangværende eksempel:

@Component @ConfigurationProperties (prefix = "features") offentlig klasse ConfigProperties {private MinerProperties minearbejder; private UIProties ui; // standard getters og setters offentlig statisk klasse MinerProperties {privat boolsk eksperimentel; // standard getters og setter} offentlig statisk klasse UIProperties {private boolske kort; // standard getters og setters}}

Ved at placere vores funktionsflags tilstand i en sammenhængende enhed åbner vi nye muligheder, så vi let kan eksponere disse oplysninger for andre dele af vores system, såsom UI eller downstream-systemer.

3.4. Eksponering af funktionskonfiguration

Vores Bitcoin-minesystem fik en UI-opgradering, som ikke er helt klar endnu. Af den grund besluttede vi at markere det med funktionen. Vi har muligvis en enkelt-sideapp ved hjælp af React, Angular eller Vue.

Uanset teknologi, vi har brug for at vide, hvilke funktioner der er aktiveret, så vi kan gengive vores side i overensstemmelse hermed.

Lad os oprette et simpelt slutpunkt til at betjene vores konfiguration, så vores brugergrænseflade kan spørge backend, når det er nødvendigt:

@RestController offentlig klasse FeaturesConfigController {private ConfigProperties egenskaber; // constructor @GetMapping ("/ feature-flags") offentlige ConfigProperties getProperties () {return egenskaber; }}

Der kan være mere sofistikerede måder at betjene disse oplysninger på, såsom at oprette brugerdefinerede aktuatorendepunkter. Men af ​​hensyn til denne guide føles et controller-slutpunkt som en god nok løsning.

3.5. Holder lejren ren

Selvom det måske lyder indlysende, er det lige så vigtigt at forblive disciplineret med at slippe af med dem, når de ikke længere er nødvendige, når vi først har implementeret vores funktionsflag.

Funktionsflag til den første brugssag - trunkbaseret udvikling og ikke-trivielle funktioner - er typisk kortvarig. Dette betyder, at vi bliver nødt til at sikre, at vores ConfigProperties, vores Java-konfiguration, og vores YAML filer forbliver rene og opdaterede.

4. Mere granulære funktionsflag

Nogle gange befinder vi os i mere komplekse scenarier. Til A / B-test eller kanariefrigørelser er vores tidligere tilgang simpelthen ikke nok.

For at få funktionsflag på et mere detaljeret niveau skal vi muligvis oprette vores løsning. Dette kan indebære at tilpasse vores brugerenhed til at omfatte funktionsspecifik information eller måske udvide vores webramme.

Forurening af vores brugere med funktionsflag er måske ikke en tiltalende idé for alle, og der er andre løsninger.

Som et alternativ kunne vi drage fordel af nogle indbyggede værktøjer som Togglz. Dette værktøj tilføjer en vis kompleksitet, men tilbyder en god løsning uden for kassen og giver førsteklasses integration med Spring Boot.

Togglz understøtter forskellige aktiveringsstrategier:

  1. Brugernavn: Flag tilknyttet specifikke brugere
  2. Gradvis udrulning: Flag aktiveret for en procentdel af brugerbasen. Dette er nyttigt til udgivelser fra Canary, for eksempel når vi ønsker at validere vores funktions adfærd
  3. Udgivelses dato: Vi kunne planlægge, at flag skal aktiveres på en bestemt dato og et bestemt tidspunkt. Dette kan være nyttigt til en produktlancering, en koordineret frigivelse eller tilbud og rabatter
  4. Klient-IP: Markerede funktioner baseret på klienters IP'er. Disse kan være nyttige, når de anvender den specifikke konfiguration på bestemte kunder, forudsat at de har statiske IP'er
  5. Server IP: I dette tilfælde bruges serverens IP til at afgøre, om en funktion skal aktiveres eller ej. Dette kan også være nyttigt for kanariske udgivelser med en lidt anden tilgang end den gradvise udrulning - som når vi vil vurdere præstationspåvirkningen i vores tilfælde
  6. ScriptEngine: Vi kunne aktivere funktionsflag baseret på vilkårlige scripts. Dette er uden tvivl den mest fleksible løsning
  7. Systemegenskaber: Vi kunne indstille bestemte systemegenskaber for at bestemme tilstanden for et funktionsflag. Dette ville være meget lig det, vi opnåede med vores mest ligefremme tilgang

5. Resume

I denne artikel havde vi en chance for at tale om funktionsflag. Derudover diskuterede vi, hvordan Spring kunne hjælpe os med at opnå noget af denne funktionalitet uden at tilføje nye biblioteker.

Vi startede med at definere, hvordan dette mønster kan hjælpe os med et par almindelige brugssager.

Dernæst byggede vi et par enkle løsninger ved hjælp af Spring og Spring Boot out-of-the-box værktøjer. Med det kom vi op med en enkel, men alligevel kraftfuld funktionsflaggingskonstruktion.

Nedenunder sammenlignede vi et par alternativer. At gå fra den enklere og mindre fleksible løsning til et mere sofistikeret, men mere komplekst mønster.

Endelig gav vi kort et par retningslinjer for at opbygge mere robuste løsninger. Dette er nyttigt, når vi har brug for en højere grad af granularitet.


$config[zx-auto] not found$config[zx-overlay] not found