portaldacalheta.pt
  • Hlavná
  • Back-End
  • Investori A Financovanie
  • Rast Tržieb
  • Ziskovosť A Efektívnosť
Back-End

Reengineering softvéru: Od špagiet po čistý dizajn



Môžete sa pozrieť na náš systém? Ten, kto napísal softvér, už nie je nablízku a mali sme množstvo problémov. Potrebujeme, aby si to niekto prezrel a vyčistil za nás.

Každý, kto bol v softvérové ​​inžinierstvo po primeranú dobu vie, že táto zdanlivo nevinná požiadavka je často začiatkom projektu, ktorý „má katastrofu napísanú“. Dedenie kódu niekoho iného môže byť nočnou morou, najmä ak je kód nesprávne navrhnutý a chýba mu dokumentácia.



Takže keď som nedávno dostal požiadavku od jedného z našich zákazníkov, aby som si prezrel jeho existujúcu socket.io aplikácia chatovacieho servera (napísaná v Node.js ) a vylepšiť to, bol som mimoriadne opatrný. Pred behom do kopcov som sa ale rozhodol aspoň súhlasiť s tým, že si pozriem kód.



Pohľad na tento kód, bohužiaľ, iba potvrdil moje obavy. Tento chatovací server bol implementovaný ako jeden veľký súbor JavaScriptu. Opätovné inžinierovanie tohto jediného monolitického súboru do čisto navrhnutého a ľahko udržiavateľného softvéru by bolo skutočne výzvou. Ale výzva ma baví, tak som súhlasil.



reinžiniering softvéru

Východiskový bod - príprava na reinžiniering

Existujúci softvér pozostával z jedného súboru, ktorý obsahoval 1 200 riadkov nezdokumentovaného kódu. Fuj. Navyše bolo známe, že obsahuje určité chyby a má problémy s výkonom.



Okrem toho preskúmanie protokolových súborov (vždy vhodné začať pri dedení kódu niekoho iného) odhalilo potenciálne problémy s únikom pamäte. V určitom okamihu sa uvádzalo, že tento proces využíval viac ako 1 GB RAM.

Vzhľadom na tieto problémy sa okamžite ukázalo, že bude potrebné uskutočniť reorganizáciu a modularizáciu kódu, a to ešte pred pokusom o ladenie alebo vylepšenie obchodnej logiky. Za týmto účelom niektoré z počiatočných problémov, ktoré bolo potrebné vyriešiť, zahŕňali:



  • Štruktúra kódu. Tento kód nemal vôbec žiadnu skutočnú štruktúru, takže bolo ťažké odlíšiť konfiguráciu od infraštruktúry od obchodnej logiky. V zásade nedošlo k nijakej modulácii alebo oddeleniu obáv.
  • Prebytočný kód. Niektoré časti kódu (napríklad kód na spracovanie chýb pre každú obslužnú rutinu udalosti, kód na vytváranie webových požiadaviek atď.) Boli duplikované viackrát. Replikovaný kód nie je nikdy dobrá vec, vďaka čomu je jeho údržba výrazne náročnejšia a náchylnejšia na chyby (keď sa nadbytočný kód na jednom mieste opraví alebo aktualizuje, na druhom nie).
  • Napevno napísané hodnoty. Kód obsahoval množstvo pevne zakódovaných hodnôt (zriedka dobrá vec). Schopnosť upravovať tieto hodnoty prostredníctvom konfiguračných parametrov (namiesto vyžadovania zmien pevne zakódovaných hodnôt v kóde) by zvýšila flexibilitu a mohla by tiež pomôcť uľahčiť testovanie a ladenie.
  • Protokolovanie. Systém ťažby dreva bol veľmi základný. Generoval by jediný obrovský logovací súbor, ktorý bolo ťažké a nemotorné analyzovať alebo analyzovať.

Kľúčové architektonické ciele

V procese začatia reštrukturalizácie kódu som sa okrem riešenia vyššie uvedených konkrétnych problémov chcel venovať aj niektorým z kľúčových architektonických cieľov, ktoré sú (alebo by aspoň mali byť) spoločné pre návrh ľubovoľného softvérového systému. . Tie obsahujú:

  • Udržateľnosť. Nikdy nepíšte softvér, ktorý očakáva, že bude jedinou osobou, ktorá ho bude musieť udržiavať. Vždy zvážte, aký zrozumiteľný bude váš kód pre niekoho iného a aké ľahké bude pre neho úpravy alebo ladenia.
  • Rozšíriteľnosť Nikdy nepredpokladajte, že funkčnosť, ktorú dnes implementujete, bude všetko, čo bude niekedy potrebné. Vytvorte si softvér tak, aby sa dal ľahko rozšíriť.
  • Modularita. Oddeľte funkčnosť do logických a samostatných modulov, z ktorých každý má svoj jasný účel a funkciu.
  • Škálovateľnosť. Dnešní používatelia sú čoraz nedočkavejší a očakávajú okamžité (alebo aspoň krátke) reakčné doby. Zlý výkon a vysoká latencia môžu spôsobiť zlyhanie aj tej najužitočnejšej aplikácie na trhu. Ako bude váš softvér fungovať, keď sa zvýši počet súbežných používateľov a požiadavky na rýchlosť spojenia? Techniky, ako je paralelizácia, optimalizácia databázy a asynchrónne spracovanie, môžu pomôcť zlepšiť schopnosť vášho systému zostať responzívnym napriek zvýšeniu zaťaženia a nárokov na zdroje.

Reštrukturalizácia kódexu

Naším cieľom je prejsť od jediného súboru zdrojového kódu monolitického monga k modularizovanej množine čisto navrhnutých komponentov. Výsledný kód by sa mal výrazne ľahšie udržiavať, vylepšovať a ladiť.



Pre túto aplikáciu som sa rozhodol usporiadať kód do nasledujúcich samostatných architektonických komponentov:

  • app.js - toto je náš vstupný bod, náš kód bude bežať odtiaľto
  • konfigur - toto bude miesto našich konfiguračných nastavení
  • ioW - „IO wrapper“, ktorý bude obsahovať celú IO (a obchodnú) logiku
  • ťažba dreva - všetok kód súvisiaci s protokolovaním (nezabudnite, že adresárová štruktúra bude obsahovať aj nový priečinok logs, ktorý bude obsahovať všetky súbory denníka)
  • balíček.json - zoznam závislostí balíkov pre Node.js
  • node_modules - všetky moduly požadované Node.js

Na tomto špecifickom prístupe nie je nič kúzla; môže existovať mnoho rôznych spôsobov, ako reštrukturalizovať kód. Len som osobne cítil, že táto organizácia bola dostatočne čistá a dobre organizovaná bez toho, aby bola príliš zložitá.



Výsledná organizácia adresárov a súborov je uvedená nižšie.

reštrukturalizovaný kód



Protokolovanie

Balíky protokolovania boli vyvinuté pre väčšinu dnešných vývojových prostredí a jazykov, takže v dnešnej dobe je zriedkavé, že budete musieť „zaviesť svoje vlastné“ možnosti protokolovania.

Pretože pracujeme s Node.js, vybral som si uzol log4js , ktorá je v podstate verziou log4js knižnica na použitie s Node.js. Táto knižnica má niekoľko skvelých funkcií, ako je schopnosť protokolovať niekoľko úrovní správ (VÝSTRAHA, CHYBA atď.) A môžeme mať k dispozícii priebežný súbor, ktorý je možné rozdeliť napríklad na dennej báze, takže nemusíme narábajte s obrovskými súbormi, ktorých otvorenie bude trvať veľa času a bude ťažké ich analyzovať a analyzovať.

Pre naše účely som vytvoril malý obal okolo uzla log4js, aby som pridal ďalšie konkrétne požadované funkcie. Všimnite si, že som sa rozhodol vytvoriť obal okolo uzla log4js, ktorý potom použijem v celom svojom kóde. Toto lokalizuje implementáciu týchto rozšírených funkcií protokolovania na jednom mieste, čím sa zabráni nadbytočnosti a nepotrebnej zložitosti celého môjho kódu, keď vyvolám protokolovanie.

Pretože pracujeme s I / O a mali by sme niekoľko klientov (používateľov), ktorí vytvoria niekoľko pripojení (zásuviek), chcem mať možnosť sledovať aktivitu konkrétneho používateľa v súboroch denníka a tiež chcem vedieť zdroj každej položky protokolu. Očakávam preto, že niektoré záznamy v denníku budú zodpovedať stavu aplikácie a niektoré, ktoré budú špecifické pre aktivitu používateľov.

V kóde obálky protokolovania môžem zmapovať ID používateľa a zásuvky, čo mi umožní sledovať akcie, ktoré boli vykonané pred a po udalosti CHYBY. Obálka protokolovania mi tiež umožní vytvoriť rôzne protokolovacie nástroje s rôznymi kontextovými informáciami, ktoré môžem odovzdať obsluhovačom udalostí, aby som poznal zdroj záznamu protokolu.

K dispozícii je kód obálky protokolovania tu .

Konfigurácia

Pre systém je často potrebné podporovať rôzne konfigurácie. Tieto rozdiely môžu byť buď rozdiely medzi vývojovým a produkčným prostredím, alebo dokonca založené na potrebe zobraziť rôzne zákaznícke prostredia a scenáre použitia.

Namiesto požadovania zmien v kóde, ktoré to podporujú, je bežnou praxou kontrolovať tieto rozdiely v správaní pomocou konfiguračných parametrov. V mojom prípade som potreboval schopnosť mať rôzne prostredia na vykonávanie (inscenačné a produkčné), ktoré môžu mať odlišné nastavenia. Tiež som sa chcel ubezpečiť, že testovaný kód funguje dobre pri inscenácii aj produkcii, a keby som na tento účel potreboval zmeniť kód, zneplatnilo by to proces testovania.

Pomocou premennej prostredia Node.js môžem určiť, ktorý konfiguračný súbor chcem použiť na konkrétne spustenie. Preto som presunul všetky predtým pevne nakonfigurované konfiguračné parametre do konfiguračných súborov a vytvoril som jednoduchý konfiguračný modul, ktorý načíta správny konfiguračný súbor s požadovaným nastavením. Tiež som kategorizoval všetky nastavenia, aby som vynútil určitý stupeň organizácie v konfiguračnom súbore a uľahčil navigáciu.

Tu je príklad výsledného konfiguračného súboru:

{ 'app': { 'port': 8889, 'invRepeatInterval':1000, 'invTimeOut':300000, 'chatLogInterval':60000, 'updateUsersInterval':600000, 'dbgCurrentStatusInterval':3600000, 'roomDelimiter':'_', 'roomPrefix':'/' }, 'webSite':{ 'host': 'mysite.com', 'port': 80, 'friendListHandler':'/MyMethods.aspx/FriendsList', 'userCanChatHandler':'/MyMethods.aspx/UserCanChat', 'chatLogsHandler':'/MyMethods.aspx/SaveLogs' }, 'logging': { 'appenders': [ { 'type': 'dateFile', 'filename': 'logs/chat-server', 'pattern': '-yyyy-MM-dd', 'alwaysIncludePattern': false } ], 'level': 'DEBUG' } }

Tok kódu

Doteraz sme vytvorili štruktúru priečinkov na hosťovanie rôznych modulov, nastavili sme spôsob načítania konkrétnych informácií o prostredí a vytvorili sme systém protokolovania. Pozrime sa teda, ako dokážeme spojiť všetky časti bez zmeny podnikového kódu.

Vďaka našej novej modulárnej štruktúre kódu je náš vstupný bod app.js je dostatočne jednoduchý a obsahuje iba inicializačný kód:

var config = require('./config'); var logging = require('./logging'); var ioW = require('./ioW'); var obj = config.getCurrent(); logging.initialize(obj.logging); ioW.initialize(config);

Keď sme definovali našu štruktúru kódu, povedali sme, že ioW priečinok by obsahoval kód súvisiaci s podnikaním a socket.io. Konkrétne bude obsahovať nasledujúce súbory (všimnite si, že kliknutím na ktorýkoľvek z uvedených názvov súborov môžete zobraziť zodpovedajúci zdrojový kód):

  • index.js - spracováva inicializáciu a pripojenia socket.io, rovnako ako predplatné udalostí, plus centralizovaný obslužný program udalostí
  • eventManager.js - hostí všetku obchodnú logiku (obsluhy udalostí)
  • webHelper.js Pomocné metódy na vykonávanie webových požiadaviek -.
  • linkedList.js Prepojená listová trieda nástrojov -

Opätovne sme upravili kód, ktorý generuje webovú požiadavku, a presunuli sme ho do samostatného súboru. Podarilo sa nám udržať našu obchodnú logiku na rovnakom mieste a bez úprav.

Jedna dôležitá poznámka: V tejto fáze eventManager.js stále obsahuje niektoré pomocné funkcie, ktoré by sa skutočne mali extrahovať do samostatného modulu. Pretože však našim cieľom v tomto prvom prechode bolo reorganizovať kód pri minimálnom dopade na obchodnú logiku a tieto pomocné funkcie sú príliš zložito spojené s obchodnou logikou, rozhodli sme sa odložiť to na ďalší prechod pri zlepšovaní organizácie kód.

Keďže súbor Node.js je podľa definície asynchrónny, často sa stretávame s trochou krysieho hniezda „spätného volania v pekle“, čo sťažuje navigáciu a ladenie kódu. Aby som sa tomuto úskaliu vyhnul, vo svojej novej implementácii som zamestnal sľubuje vzor a konkrétne využívajú Modrý vták čo je veľmi pekná a rýchla knižnica sľubov. Sľuby nám umožnia sledovať kód, akoby bol synchrónny, a tiež poskytnúť správu chýb a čistý spôsob štandardizácie odpovedí medzi hovormi. V našom kóde je implicitná zmluva, že každý spracovateľ udalostí musí vrátiť prísľub, aby sme mohli spravovať centralizované spracovanie chýb a protokolovanie.

Všetci spracovatelia udalostí vrátia prísľub (bez ohľadu na to, či asynchrónne volajú alebo nie). Keď je toto na mieste, môžeme centralizovať spracovanie chýb a protokolovanie a zaisťujeme, že ak sa vyskytne neošetrená chyba vo vnútri obsluhy udalosti, táto chyba sa zachytí.

function execEventHandler(socket, eventName, eventHandler, data){ var sLogger = logging.createLogger(socket.id + ' - ' + eventName); sLogger.info(''); eventHandler(socket, data, sLogger).then(null, function(err){ sLogger.error(err.stack); }); };

V našej diskusii o protokolovaní sme spomenuli, že každé pripojenie bude mať svoj vlastný protokolovací nástroj s kontextovými informáciami. Konkrétne pri vytváraní viazame ID zásuvky a názov udalosti na záznamník, takže keď tento záznamník odovzdáme obslužnej rutine udalosti, každý riadok protokolu bude obsahovať tieto informácie:

uhlový.modul nie je funkcia
var sLogger = logging.createLogger(socket.id + ' - ' + eventName);

Jeden ďalší bod, ktorý stojí za zmienku, pokiaľ ide o spracovanie udalostí: V pôvodnom súbore sme mali setInterval volanie funkcie, ktoré sa nachádzalo vo vnútri obslužnej rutiny udalosti pripojenia socket.io, a túto funkciu sme identifikovali ako problém.

io.on('connection', function (socket) { ... Several event handlers .... setInterval(function() { try { var date = Date.now(); var tmp = []; while (0

Tento kód vytvára časovač so zadaným intervalom (v našom prípade to bola 1 minúta) pre každú jednu žiadosť o pripojenie že dostaneme. Napríklad, ak máme v danom okamihu 300 online zásuviek, potom by sme mali každú minútu spustených 300 časovačov. Ako vidíte v kóde vyššie, problém spočíva v tom, že neexistuje použitie soketu ani žiadna premenná, ktorá bola definovaná v rozsahu obsluhy udalosti. Jediná použitá premenná je messageHub premenná, ktorá je deklarovaná na úrovni modulu, čo znamená, že je rovnaká pre všetky pripojenia. Preto nie je absolútne potrebné samostatný časovač pre každé pripojenie. Takže sme to odstránili z obslužnej rutiny udalosti pripojenia a zahrnuli sme to do nášho všeobecného inicializačného kódu, ktorým je v tomto prípade initialize funkcie.

Nakoniec sme v našom spracovaní odpovedí V webHelper.js pridali spracovanie pre každú nerozpoznanú odpoveď, ktorá bude zaznamenávať informácie, ktoré potom budú užitočné pri procese ladenia:

if (!res || !res.d || !res.d.IsValid){ logger.debug(sendData); logger.debug(data); reject(new Error('Request failed. Path ' + params.path + ' . Invalid return data.')); return; }

Posledným krokom je nastavenie súboru protokolovania pre štandardnú chybu súboru Node.js. Tento súbor bude obsahovať nespracované chyby, ktoré sme možno prehliadli. Na nastavenie procesu uzla v systéme Windows (nie je to ideálne, ale viete ...) ako služby používame nástroj s názvom nssm ktorý má vizuálne používateľské rozhranie, ktoré umožňuje definovať štandardný výstupný súbor, štandardný chybový súbor a premenné prostredia.

O výkone Node.js

Node.js je jednovláknový programovací jazyk. Na zlepšenie škálovateľnosti môžeme použiť niekoľko alternatív. K dispozícii je modul uzlov klastra alebo jednoducho len pridať viac procesov uzlov a na vrchole nich umiestniť nginx, ktorý vykoná presmerovanie a vyrovnanie záťaže.

V našom prípade však za predpokladu, že každý podproces klastra uzlov alebo proces uzla bude mať svoj vlastný pamäťový priestor, nebudeme môcť ľahko zdieľať informácie medzi týmito procesmi. Pre tento konkrétny prípad teda budeme musieť použiť externé úložisko dát (ako napr opakovať ), aby boli online zásuvky dostupné pre rôzne procesy.

Záver

Týmto všetkým sme dosiahli významné vyčistenie kódu, ktorý nám bol pôvodne odovzdaný. Nejde o to, aby bol kód dokonalý, ale skôr o jeho reinžiniering, aby sa vytvoril čistý architektonický základ, ktorý sa bude ľahšie podporovať a udržiavať a ktorý uľahčí a zjednoduší ladenie.

V súlade s vyššie vymenovanými kľúčovými princípmi návrhu softvéru - udržiavateľnosťou, rozšíriteľnosťou, modularitou a škálovateľnosťou - sme vytvorili moduly a štruktúru kódu, ktoré jasne a zreteľne identifikovali rôzne zodpovednosti modulu. Zistili sme tiež niektoré problémy v pôvodnej implementácii, ktoré vedú k vysokej spotrebe pamäte, ktorá znižovala výkon.

Dúfam, že sa vám článok páčil, dajte mi vedieť, ak máte ďalšie pripomienky alebo otázky.

Ako vytvárať vlastné písma: 7 krokov a 3 prípadové štúdie

Nástroje A Výukové Programy

Ako vytvárať vlastné písma: 7 krokov a 3 prípadové štúdie
Výukový program Power Pivot pre Excel: Najčastejšie prípady a príklady použitia

Výukový program Power Pivot pre Excel: Najčastejšie prípady a príklady použitia

Finančné Procesy

Populárne Príspevky
Sofistikované prototypy - prečo používať Axure
Sofistikované prototypy - prečo používať Axure
ARM servery: architektúra mobilných CPU pre datacentrá?
ARM servery: architektúra mobilných CPU pre datacentrá?
Zoznam najlepších bezplatných kníh o programovaní v ApeeScape
Zoznam najlepších bezplatných kníh o programovaní v ApeeScape
V reálnom čase s Redis Pub / Sub
V reálnom čase s Redis Pub / Sub
Recenzie Haxe: Vlastnosti a silné stránky Haxe 4
Recenzie Haxe: Vlastnosti a silné stránky Haxe 4
 
Desať najlepších pravidiel front-endového dizajnu pre vývojárov
Desať najlepších pravidiel front-endového dizajnu pre vývojárov
Vytváranie rozprávania z čísel
Vytváranie rozprávania z čísel
Smerom k aktualizovateľným grafom D3.js
Smerom k aktualizovateľným grafom D3.js
Čo je prognóza predaja?
Čo je prognóza predaja?
Prípadová štúdia: Prečo pre svoje produkty používam cloudovú infraštruktúru AWS
Prípadová štúdia: Prečo pre svoje produkty používam cloudovú infraštruktúru AWS
Populárne Príspevky
  • ako nájsť klientov ako poradca
  • unikli kreditné karty, ktoré fungujú
  • ako písať unit testy
  • a* c++
  • rozdiel medzi s corp a corp
Kategórie
  • Back-End
  • Investori A Financovanie
  • Rast Tržieb
  • Ziskovosť A Efektívnosť
  • © 2022 | Všetky Práva Vyhradené

    portaldacalheta.pt