socialgekon.com
  • Tärkein
  • Muu
  • Web-Käyttöliittymä
  • Työn Tulevaisuus
  • Suunnittelu
Taustaa

Ohjelmistojen uudelleensuunnittelu: Spagettista puhtaaseen suunnitteluun

Voitteko vilkaista järjestelmäämme? Ohjelman kirjoittanutta kaveria ei ole enää lähellä, ja meillä on ollut useita ongelmia. Tarvitsemme jonkun tarkastelemaan sitä ja puhdistamaan sen meille.

Jokainen, joka on ollut sisällä ohjelmistotuotanto kohtuullisen ajan tietää, että tämä näennäisesti viaton pyyntö on usein alku hankkeelle, jonka 'on kirjoittanut katastrofi kaikkialle'. Joku toisen koodin periminen voi olla painajainen, varsinkin kun koodi on huonosti suunniteltu ja puuttuu dokumentaatio.

Joten kun sain äskettäin yhden asiakkaamme pyynnön tarkastella hänen olemassa olevaa asiakasta socket.io chat - palvelinsovellus (kirjoitettu Node.js ) ja parantamaan sitä, olin erittäin varovainen. Mutta ennen juoksemista kukkuloille päätin ainakin suostua katsomaan koodia.



Valitettavasti koodin tarkastelu vain vahvisti huoleni. Tämä chat-palvelin oli toteutettu yhtenä suurena JavaScript-tiedostona. Tämän yhden monoliittisen tiedoston uudelleen suunnittelu puhtaasti suunnitelluksi ja helposti ylläpidettäväksi ohjelmistoksi olisi todellakin haaste. Mutta nautin haasteesta, joten suostuin.

ohjelmistojen uudelleensuunnittelu

Lähtökohta - Valmistaudu uudelleensuunnitteluun

Nykyinen ohjelmisto koostui yhdestä tiedostosta, joka sisälsi 1200 riviä dokumentoimattomia koodeja. Yikes. Lisäksi sen tiedettiin sisältävän joitain vikoja ja suorituskykyongelmia.

Lisäksi lokitiedostojen (aina hyvä paikka aloittaa, kun perit jonkun toisen koodin) tutkiminen paljasti mahdollisia muistivuotoja. Jossakin vaiheessa prosessin ilmoitettiin käyttävän yli 1 Gt RAM-muistia.

Kun otetaan huomioon nämä ongelmat, kävi heti selväksi, että koodi olisi järjestettävä uudelleen ja moduloitava ennen kuin yrität edes virheenkorjausta tai parantaa liiketoimintalogiikkaa. Tätä tarkoitusta varten joihinkin alkuperäisiin ongelmiin, jotka oli ratkaistava, kuului:

  • Koodirakenne. Koodilla ei ollut lainkaan todellista rakennetta, minkä vuoksi kokoonpanon ja infrastruktuurin erottaminen liiketoimintalogiikasta oli vaikeaa. Pohjimmiltaan ei tapahtunut modulaatiota tai huolenaiheita.
  • Tarpeeton koodi. Jotkin koodin osat (kuten jokaisen tapahtumankäsittelijän virhekäsittelykoodi, verkkopyyntöjen tekemisen koodi jne.) Kopioitiin useita kertoja. Replikoitu koodi ei ole koskaan hyvä asia, mikä tekee koodista huomattavasti vaikeampaa ylläpitää ja alttiimpaa virheille (kun turha koodi korjataan tai päivitetään yhdessä paikassa, mutta ei toisessa paikassa).
  • Kovakoodatut arvot. Koodi sisälsi useita kovakoodattuja arvoja (harvoin hyvä asia). Mahdollisuus muokata näitä arvoja konfigurointiparametrien avulla (pikemminkin kuin vaatia muutoksia koodattuihin kovakoodattuihin arvoihin) lisäisi joustavuutta ja voisi myös helpottaa testausta ja virheenkorjausta.
  • Kirjaaminen. Kirjausjärjestelmä oli hyvin yksinkertainen. Se luo yhden jättimäisen lokitiedoston, jota oli vaikea ja kömpelö analysoida tai jäsentää.

Keskeiset arkkitehtoniset tavoitteet

Aloittaessani koodin uudelleenjärjestelyä, halusin käsitellä yllä yksilöityjä erityiskysymyksiä, lisäksi käsitellä joitain keskeisiä arkkitehtonisia tavoitteita, jotka ovat (tai ainakin pitäisi olla) yhteisiä minkä tahansa ohjelmistojärjestelmän suunnittelulle . Nämä sisältävät:

  • Ylläpidettävyys. Älä koskaan kirjoita ohjelmistoa, joka odottaa olevan ainoa henkilö, joka tarvitsee ylläpitää sitä. Mieti aina, kuinka ymmärrettävä koodi on jollekin muulle, ja kuinka helppoa heillä on muokata tai virheenkorjausta.
  • Laajennettavuus. Älä koskaan oleta, että tänään käyttämäsi toiminnot ovat kaikki mitä tarvitset. Suunnittele ohjelmistosi tavoilla, joita on helppo laajentaa.
  • Modulaarisuus. Erota toiminnot loogisiin ja erillisiin moduuleihin, joilla jokaisella on oma selkeä tarkoitus ja toiminta.
  • Skaalautuvuus. Nykypäivän käyttäjät ovat yhä kärsimättömiä ja odottavat välitöntä (tai ainakin lähellä välitöntä) vasteaikaa. Huono suorituskyky ja korkea viive voivat aiheuttaa kaikkein hyödyllisimmän sovelluksen epäonnistumisen markkinoilla. Kuinka ohjelmistosi toimii, kun samanaikaisten käyttäjien määrä ja kaistanleveysvaatimukset kasvavat? Tekniikat, kuten rinnakkaistaminen, tietokannan optimointi ja asynkroninen käsittely, voivat auttaa parantamaan järjestelmän kykyä pysyä reagoivana kasvavista kuormitus- ja resurssitarpeista huolimatta.

Säännöstön uudelleenjärjestely

Tavoitteenamme on siirtyä yhdestä monoliittisesta mongolaisesta lähdekooditiedostosta modularisoituun sarjaan puhtaasti suunniteltuja komponentteja. Tuloksena olevan koodin on oltava huomattavasti helpompi ylläpitää, parantaa ja virheenkorjausta.

Tätä sovellusta varten olen päättänyt järjestää koodin seuraaviin erillisiin arkkitehtonisiin komponentteihin:

  • app.js - tämä on pääsykohta, koodimme suoritetaan täältä
  • kokoonpano - tässä kokoonpanoasetuksemme asuvat
  • ioW - 'IO-kääre', joka sisältää kaiken IO (ja liike) logiikan
  • puunkorjuu - kaikki lokiin liittyvä koodi (huomaa, että hakemistorakenne sisältää myös uuden logs kansion, joka sisältää kaikki lokitiedostot)
  • package.json - luettelo Node.js: n pakettiriippuvuuksista
  • solmu_moduulit - kaikki Node.js: n edellyttämät moduulit

Tässä erityisessä lähestymistavassa ei ole mitään taikaa; koodin uudelleenjärjestelyyn voisi olla monia eri tapoja. Minusta tuntui vain henkilökohtaisesti, että tämä organisaatio oli riittävän puhdas ja hyvin järjestetty olematta liian monimutkainen.

Tuloksena oleva hakemisto ja tiedostojärjestely on esitetty alla.

uudelleenjärjestetty koodi

Kirjaaminen

Kirjauspaketit on kehitetty suurimmalle osalle nykypäivän kehitysympäristöjä ja kieliä, joten nykyään on harvinaista, että joudut käyttämään 'omaa' kirjauskapasiteettiasi.

Koska työskentelemme Node.js: n kanssa, valitsin log4js-solmu , joka on pohjimmiltaan versio log4js kirjasto käytettäväksi Node.js: n kanssa. Tässä kirjastossa on joitain hienoja ominaisuuksia, kuten kyky kirjata useita tasoja viestejä (VAROITUS, VIRHE jne.), Ja meillä voi olla liikkuva tiedosto, joka voidaan jakaa esimerkiksi päivittäin, joten meidän ei tarvitse käsitellä valtavia tiedostoja, joiden avaaminen vie paljon aikaa ja joita on vaikea analysoida ja jäsentää.

Tarkoituksia varten olen luonut pienen kääreen log4js-solmun ympärille lisätäksesi tiettyjä haluttuja lisäominaisuuksia. Huomaa, että olen päättänyt luoda kääreen log4js-solmun ympärille, jota käytän sitten koko koodissani. Tämä lokalisoi näiden laajennettujen kirjausominaisuuksien toteuttamisen yhdessä paikassa välttäen turhuutta ja tarpeetonta monimutkaisuutta koko koodissani, kun käytän kirjaamista.

Koska työskentelemme I / O: n kanssa ja meillä olisi useita asiakkaita (käyttäjiä), jotka synnyttävät useita yhteyksiä (pistorasioita), haluan pystyä jäljittämään tietyn käyttäjän toiminnan lokitiedostoista ja haluan myös tietää kunkin lokimerkinnän lähde. Siksi odotan, että sovelluksen tilaan liittyy joitain lokimerkintöjä, ja joitain, jotka liittyvät nimenomaan käyttäjien toimintaan.

Kirjaamalla käärekoodissani pystyn kartoittamaan käyttäjätunnuksen ja pistorasiat, joiden avulla voin seurata toimintoja, jotka tehtiin ennen ERROR-tapahtumaa ja sen jälkeen. Kirjauskääre antaa minulle myös mahdollisuuden luoda erilaisia ​​kirjaajia, joilla on erilaiset asiayhteyteen liittyvät tiedot, jotka voin välittää tapahtumankäsittelijöille, jotta tiedän lokimerkinnän lähteen.

Kirjauskääreen koodi on käytettävissä tässä .

Kokoonpano

Usein on tarpeen tukea järjestelmän eri kokoonpanoja. Nämä erot voivat olla joko eroja kehitys- ja tuotantoympäristöjen välillä tai jopa perustua tarpeeseen näyttää erilaisia ​​asiakasympäristöjä ja käyttöskenaarioita.

Sen sijaan, että vaaditaan koodin muutoksia tämän tueksi, yleinen käytäntö on hallita näitä käyttäytymiseroja konfigurointiparametrien avulla. Minun tapauksessani tarvitsin mahdollisuuden käyttää erilaisia ​​toteutusympäristöjä (lavastus ja tuotanto), joilla voi olla erilaiset asetukset. Halusin myös varmistaa, että testattu koodi toimi hyvin sekä lavastuksessa että tuotannossa, ja jos minun olisi pitänyt vaihtaa koodi tätä tarkoitusta varten, se olisi mitätöinyt testausprosessin.

Käyttämällä Node.js-ympäristömuuttujaa voin määrittää, mitä kokoonpanotiedostoa haluan käyttää tiettyyn suoritukseen. Siksi siirsin kaikki aiemmin kovakoodatut määritysparametrit kokoonpanotiedostoihin ja loin yksinkertaisen määritysmoduulin, joka lataa oikean määritystiedoston haluttuilla asetuksilla. Ryhmittelin myös kaikki asetukset pakottaakseni jonkinasteisen organisaation kokoonpanotiedostoon ja helpottaakseni navigointia.

Tässä on esimerkki tuloksena olevasta määritystiedostosta:

{ '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' } }

Koodivirta

Toistaiseksi olemme luoneet kansiorakenteen eri moduulien isännöimiseksi, olemme luoneet tavan ladata ympäristökohtaisia ​​tietoja ja luoneet kirjausjärjestelmän, joten katsotaanpa, kuinka voimme sitoa kaikki kappaleet muuttamatta yrityskohtaista koodia.

Uuden modulaarisen koodirakenteen ansiosta pääsykohta app.js on tarpeeksi yksinkertainen ja sisältää vain alustuskoodin:

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

Kun määritimme koodirakenteen, sanoimme, että ioW kansioon mahtuu liiketoimintaan ja socket.io liittyvä koodi. Erityisesti se sisältää seuraavat tiedostot (huomaa, että voit napsauttaa mitä tahansa luetellun tiedostonimeä nähdäksesi vastaavan lähdekoodin):

  • index.js - hoitaa socket.io -alustan ja -yhteydet sekä tapahtumien tilauksen sekä tapahtumien keskitetyn virhekäsittelijän
  • eventManager.js - isännöi kaiken liiketoimintaan liittyvän logiikan (tapahtumankäsittelijät)
  • webHelper.js - auttajamenetelmät verkkopyyntöjen tekemiseen.
  • linkedList.js - linkitetty luettelo hyödyllisyysluokka

Teimme verkkopyynnön tekevän koodin uudelleen ja siirrimme sen erilliseen tiedostoon, ja onnistuimme pitämään liiketoimintalogiikkamme samassa paikassa muuttumattomana.

Yksi tärkeä huomautus: Tässä vaiheessa eventManager.js sisältää edelleen joitain auttajatoimintoja, jotka todella pitäisi purkaa erilliseen moduuliin. Koska tavoitteenamme oli kuitenkin tällä ensimmäisellä kierroksella järjestää koodi uudelleen samalla, kun minimoidaan vaikutukset liiketoimintalogiikkaan, ja nämä aputoiminnot ovat liian monimutkaisesti sidottuja liiketoimintalogiikkaan, päätimme lykätä tämän myöhempään siirtoon organisaation parantamiseksi. koodi.

Koska Node.js on määritelmänsä mukaan asynkroninen, kohtaamme usein vähän rotan pesän 'soittopyynnön', mikä tekee koodista erityisen vaikean navigoida ja virheenkorjaus. Tämän kuopan välttämiseksi olen käyttänyt uudessa toteutuksessani lupaa mallia ja ne hyödyntävät erityisesti sinikka joka on erittäin mukava ja nopea lupaus kirjasto. Lupaukset antavat meille mahdollisuuden seurata koodia ikään kuin se olisi synkronoitu, ja ne tarjoavat myös virheiden hallinnan ja puhtaan tavan standardoida vastaukset puheluiden välillä. Koodissamme on implisiittinen sopimus, jonka mukaan jokaisen tapahtumankäsittelijän on palautettava lupaus, jotta voimme hallita keskitettyä virheiden käsittelyä ja kirjaamista.

Kaikki tapahtumankäsittelijät palauttavat lupauksen (soittavatko he asynkronisia puheluja vai eivät). Kun tämä on paikallaan, voimme keskittää virheenkäsittelyn ja lokien kirjaamisen ja varmistamme, että jos tapahtumakäsittelijässä on käsittelemätön virhe, virhe havaitaan.

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); }); };

Lokitietokeskustelussamme mainitsimme, että jokaisella yhteydellä olisi oma logger, jossa on asiayhteyteen liittyviä tietoja. Erityisesti sidomme socket id: n ja tapahtuman nimen loggeriin, kun luomme sen, joten kun välitämme loggerin tapahtumankäsittelijälle, jokaisella lokirivillä on kyseiset tiedot:

var sLogger = logging.createLogger(socket.id + ' - ' + eventName);

Yksi toinen mainitsemisen arvoinen seikka tapahtumien käsittelystä: Alkuperäisessä tiedostossa meillä oli setInterval toimintokutsu, joka oli socket.io-yhteyden tapahtuman tapahtumakäsittelijän sisällä, ja olemme tunnistaneet tämän toiminnon ongelmaksi.

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

Tämä koodi luo ajastinta tietyllä aikavälillä (meidän tapauksessamme se oli 1 minuutti) jokainen yhteyspyyntö että saamme. Joten jos meillä esimerkiksi on milloin tahansa 300 online-pistorasiaa, meillä olisi 300 ajastinta suorittamaan joka minuutti. Kuten yllä olevasta koodista voi nähdä, ongelmana on, että pistoketta ei käytetä eikä mitään muuttujaa, joka on määritelty tapahtumakäsittelijän piirissä. Ainoa muuttuja, jota käytetään, on messageHub muuttuja, joka ilmoitetaan moduulitasolla, mikä tarkoittaa, että se on sama kaikille yhteyksille. Siksi ei ole mitään erillistä ajastinta liitäntää kohti. Joten olemme poistaneet tämän yhteystapahtumakäsittelijästä ja sisällyttäneet sen yleiseen alustuskoodiin, joka tässä tapauksessa on initialize toiminto.

Lopuksi, käsittelyssä vastauksistamme webHelper.js -sovelluksessa lisättiin käsittely kaikille tunnistamattomille vastauksille, jotka kirjaavat tietoja, jotka ovat hyödyllisiä virheenkorjausprosessissa:

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; }

Viimeinen vaihe on luoda lokitiedosto Node.js-standardivirheelle. Tämä tiedosto sisältää käsittelemättömiä virheitä, jotka olemme saattaneet unohtaa. Asetamme solmuprosessin Windowsissa (ei ihanteellinen, mutta tiedät…) palveluna, käytämme työkalua nimeltä nssm jolla on visuaalinen käyttöliittymä, jonka avulla voit määrittää vakiotulostustiedoston, tavallisen virhetiedoston ja ympäristömuuttujat.

Tietoja Node.js Performance -sovelluksesta

Node.js on yksisäikeinen ohjelmointikieli. Skaalautuvuuden parantamiseksi voimme käyttää useita vaihtoehtoja. Siellä on solmuklusterimoduuli tai yksinkertaisesti vain lisää solmuprosesseja ja laita nginx niiden päälle suorittamaan edelleenlähetys ja kuormituksen tasapainotus.

Meidän tapauksessamme, vaikka jokaisella solmuryhmän aliprosessilla tai solmuprosessilla on oma muistitila, emme voi jakaa tietoja näiden prosessien välillä helposti. Joten tässä tapauksessa meidän on käytettävä ulkoista tietovarastoa (kuten toistaa ) pitääkseen online-pistorasiat käytettävissä eri prosesseille.

Johtopäätös

Kun kaikki tämä on paikallaan, olemme saavuttaneet merkittävän puhdistuksen alun perin meille luovutetusta koodista. Kyse ei ole koodin täydellisestä tekemisestä, vaan pikemminkin sen uudelleensuunnittelusta, jotta luodaan puhdas arkkitehtoninen perusta, jota on helpompi tukea ja ylläpitää ja joka helpottaa ja yksinkertaistaa virheenkorjausta.

Noudattamalla aiemmin lueteltuja keskeisiä ohjelmistosuunnitteluperiaatteita - ylläpidettävyys, laajennettavuus, modulaarisuus ja skaalautuvuus - loimme moduulit ja koodirakenteen, jotka yksilöivät selkeästi ja siististi moduulien erilaiset vastuut. Olemme myös tunnistaneet joitain ongelmia alkuperäisessä toteutuksessa, jotka johtavat korkeaan muistin kulutukseen, joka heikensi suorituskykyä.

Toivottavasti pidit artikkelista. Kerro minulle, jos sinulla on vielä kommentteja tai kysymyksiä.

Mielen silmä - katsaus datan visualisointipsykologiaan

Ux-Suunnittelu

Mielen silmä - katsaus datan visualisointipsykologiaan
Tekoälyn nykyisyys ja tulevaisuus suunnittelussa (infografiikan kanssa)

Tekoälyn nykyisyys ja tulevaisuus suunnittelussa (infografiikan kanssa)

Ux-Suunnittelu

Suosittu Viestiä
Elixir-ohjelmointikielen käytön aloittaminen
Elixir-ohjelmointikielen käytön aloittaminen
Heuristinen analyysi UX: lle - Kuinka suorittaa käytettävyyden arviointi
Heuristinen analyysi UX: lle - Kuinka suorittaa käytettävyyden arviointi
Tietorakenteen periaatteet mobiililaitteille (infografiikan kanssa)
Tietorakenteen periaatteet mobiililaitteille (infografiikan kanssa)
Reagoiva suunnittelu ei riitä, tarvitsemme reagoivaa suorituskykyä
Reagoiva suunnittelu ei riitä, tarvitsemme reagoivaa suorituskykyä
Google-kuvat ja tietosuoja: Näin pidät valokuvasi turvassa
Google-kuvat ja tietosuoja: Näin pidät valokuvasi turvassa
 
Kuinka käyttää Google Kuvia iPhonessa kuvien varmuuskopioimiseen, tallentamiseen ja jakamiseen
Kuinka käyttää Google Kuvia iPhonessa kuvien varmuuskopioimiseen, tallentamiseen ja jakamiseen
Ansiosopimukset: Rakenteet neuvottelujen kuolleiden pisteiden rikkomiseksi
Ansiosopimukset: Rakenteet neuvottelujen kuolleiden pisteiden rikkomiseksi
Koneoppimisen numerotunnistus - nollasta sovellukseen
Koneoppimisen numerotunnistus - nollasta sovellukseen
Hinta on oikea - yleiskatsaus hinnoittelustrategiaan kuluttajayrityksille
Hinta on oikea - yleiskatsaus hinnoittelustrategiaan kuluttajayrityksille
Kuinka lisätä musiikkia Instagram-tarinaan tarroilla tai ilman
Kuinka lisätä musiikkia Instagram-tarinaan tarroilla tai ilman
Luokat
Tietojenkäsittely Ja TietokannatiOS-vinkkejäWeb-KäyttöliittymäTrenditProjektinhallintaKetteräAmmuntaOngelmien karttoittaminenElämäntapaTaustaa

© 2023 | Kaikki Oikeudet Pidätetään

socialgekon.com