Päivitetty 14.5.2019 vastaamaan paremmin Go:n parannuksia viimeisten kahden vuoden aikana (paketinhallinta, parempi suorituskyky, nopeammat käännösajat ja kypsempi ekosysteemi) Uuteen kieleen siirtyminen on aina iso askel, varsinkin kun vain yhdellä tiimin jäsenistä on aiempaa kokemusta kyseisestä kielestä. Tämän vuoden alussa vaihdoimme Streamin ensisijaisen ohjelmointikielen Pythonista Go-kieleen. Tässä postauksessa selitetään joitakin syitä, miksi päätimme jättää Pythonin taaksemme ja siirtyä Go-kieleen. Kiitos Ren Sakamotolle siitä, että hän käänsi japaniksi Why we switched from Python to Go, なぜ私達は Python から Go に移行したのか.
- Syy 1 – Suorituskyky
- Syy 2 – Kielen suorituskyvyllä on väliä
- Syy 3 – Kehittäjien tuottavuus & Ei olla liian luovia
- Syy 4 – Rinnakkaisuus & Kanavat
- Syy 5 – Nopea kääntämisaika
- Syy 6 – Kyky rakentaa tiimi
- Syy 7 – Vahva ekosysteemi
- Syy 8 – Gofmt, pakotettu koodin muotoilu
- Syy 9 – gRPC ja protokollapuskurit
- Haitat 1 – Kehysten puute
- Haitat 2 – Virheenkäsittely
- Haitat 3 – Pakettienhallinta
- Python vs. Go
- Elixir vs Go – The Runner Up
- Johtopäätös
- Lisälukemista Golangiin siirtymisestä
- Learning Go
Syy 1 – Suorituskyky
Go on nopea! Go on äärimmäisen nopea. Suorituskyky on samanlainen kuin Javalla tai C++:lla. Meidän käyttötapauksessamme Go on tyypillisesti 40 kertaa nopeampi kuin Python. Tässä on pieni benchmark-peli, jossa vertaillaan Go:ta ja Pythonia.
Syy 2 – Kielen suorituskyvyllä on väliä
Monille sovelluksille ohjelmointikieli on yksinkertaisesti liima sovelluksen ja tietokannan välillä. Kielen suorituskyvyllä itsessään ei yleensä ole suurta merkitystä. Stream on kuitenkin API-toimittaja, joka pyörittää 700 yritykselle ja yli 500 miljoonalle loppukäyttäjälle suunnattua syöttö- ja keskustelualustaa. Olemme optimoineet Cassandraa, PostgreSQL:ää, Redisiä jne. jo vuosia, mutta lopulta saavutetaan käytettävän kielen rajat. Python on loistava kieli, mutta sen suorituskyky on melko hidas käyttötapauksissa, kuten serialisoinnissa/deserialisoinnissa, luokittelussa ja aggregoinnissa. Törmäsimme usein suorituskykyongelmiin, joissa Cassandralta kesti 1 ms hakea dataa ja Python käytti seuraavat 10 ms sen muuttamiseen objekteiksi.
Syy 3 – Kehittäjien tuottavuus & Ei olla liian luovia
Katsokaa tätä pientä Go-koodinpätkää How I Start Go tutorialista. (Tämä on loistava opetusohjelma ja hyvä lähtökohta Go:n omaksumiseen.)
Jos olet uusi Go:n käyttäjä, ei ole paljon sellaista, mikä yllättää sinua lukiessasi tuota pientä koodinpätkää. Siinä esitellään useita tehtäviä, tietorakenteita, osoittimia, muotoilua ja sisäänrakennettua HTTP-kirjastoa. Kun aloitin ohjelmoinnin, pidin aina Pythonin edistyneempien ominaisuuksien käyttämisestä. Pythonin avulla voit olla melko luova kirjoittamasi koodin kanssa. Voit esimerkiksi:
- Käyttää MetaClasses-luokkia itse rekisteröidäksesi luokkia koodin alustuksen yhteydessä
- Vaihtaa True- ja False-arvoja
- Lisätä funktioita sisäänrakennettujen funktioiden luetteloon
- Ylikuormittaa operaattoreita taikametodien avulla
- Käyttää funktioita ominaisuuksina @property-koristelijan avulla
Näillä ominaisuuksilla on hauska leikkiä, mutta, kuten useimmat ohjelmoijat ovat samaa mieltä, ne vaikeuttavat usein koodin ymmärtämistä, kun lukee jonkun toisen työtä. Go pakottaa sinut pysymään perusasioissa. Tämän ansiosta on erittäin helppoa lukea kenen tahansa koodia ja ymmärtää heti, mistä on kyse. Huomautus: Kuinka ”helppoa” se on, riippuu tietysti käyttötapauksestasi. Jos haluat luoda perus-CRUD-API:n, suosittelen edelleen Django + DRF:ää tai Railsia.
Kielenä Go yrittää pitää asiat yksinkertaisina. Se ei esittele monia uusia käsitteitä. Pääpaino on yksinkertaisen kielen luomisessa, joka on uskomattoman nopea ja helppokäyttöinen. Ainoa osa-alue, jossa se tulee innovatiiviseksi, on goroutiinit ja kanavat. (Ollakseni 100-prosenttisen oikeassa, CSP:n käsite alkoi vuonna 1977, joten tämä innovaatio on pikemminkin uusi lähestymistapa vanhaan ideaan.) Goroutiinit ovat Go:n kevyt lähestymistapa säikeistämiseen, ja kanavat ovat ensisijainen tapa kommunikoida goroutiinien välillä. Goroutiinit ovat hyvin halpoja luoda ja vievät vain muutaman kilotavun lisää muistia. Koska goroutiinit ovat niin kevyitä, niitä voi olla käynnissä satoja tai jopa tuhansia samanaikaisesti. Goroutinien välillä voi kommunikoida kanavien avulla. Go-runtime hoitaa kaiken monimutkaisuuden. Goroutiinien ja kanavapohjaisen lähestymistavan ansiosta on erittäin helppoa käyttää kaikkia käytettävissä olevia suorittimen ytimiä ja käsitellä samanaikaista IO:ta – kaikki tämä monimutkaistamatta kehitystä. Pythoniin/Javaan verrattuna funktion suorittaminen goroutiinilla vaatii vain vähän boilerplate-koodia. Funktiokutsun eteen yksinkertaisesti liitetään avainsana ”go”:
https://tour.golang.org/concurrency/1 Go:n lähestymistapa samanaikaisuuteen on erittäin helppokäyttöinen. Se on mielenkiintoinen lähestymistapa verrattuna Nodeen, jossa kehittäjän on kiinnitettävä tarkkaa huomiota siihen, miten asynkronista koodia käsitellään. Toinen hieno puoli Go:n samanaikaisuudessa on race detector. Sen avulla on helppo selvittää, onko asynkronisessa koodissasi kilpailuolosuhteita.
Knock knock Race condition Who’s there?
– I Am Devloper (@iamdevloper) November 11, 2013
Tässä on muutama hyvä resurssi, joiden avulla pääset alkuun Go:n ja kanavien kanssa:
- https://gobyexample.com/channels
- https://tour.golang.org/concurrency/2
- http://guzalexander.com/2013/12/06/golang-channels-tutorial.html
- https://www.golang-book.com/books/intro/10
- https://www.goinggo.net/2014/02/the-nature-of-channels-in-go.html
- Goroutiinit vs. vihreät säikeet
Syy 5 – Nopea kääntämisaika
Suurimman Go:lla kirjoitetun mikropalvelumme kääntäminen kestää tällä hetkellä 4 sekuntia. Go:n nopeat kääntämisajat ovat merkittävä tuottavuusvoitto verrattuna kieliin kuten Java ja C++, jotka ovat kuuluisia hitaasta kääntämisnopeudesta. Pidän miekkailusta, mutta vielä mukavampaa on saada asiat valmiiksi, kun vielä muistan, mitä koodin pitäisi tehdä:
Syy 6 – Kyky rakentaa tiimi
Aluksi aloitetaan itsestäänselvyydestä: Go-kehittäjiä ei ole niin paljon verrattuna vanhempiin kieliin, kuten C++:aan ja Javaan. StackOverflow’n mukaan 38 % kehittäjistä osaa Javaa, 19,3 % osaa C++:ta ja vain 4,6 % osaa Go:ta. GitHubin tiedot osoittavat samanlaista suuntausta: Go on yleisempi kuin Erlangin, Scalan ja Elixirin kaltaiset kielet, mutta vähemmän suosittu kuin Java ja C++. Onneksi Go on hyvin yksinkertainen ja helposti opittava kieli. Se tarjoaa tarvitsemasi perusominaisuudet eikä mitään muuta. Uudet käsitteet, jotka se esittelee, ovat ”defer”-lauseke ja sisäänrakennettu samanaikaisuuden hallinta ”go-rutiineilla” ja kanavilla. (Puristeille: Go ei ole ensimmäinen kieli, joka toteuttaa nämä käsitteet, se on vain ensimmäinen, joka tekee niistä suosittuja). Kuka tahansa Python-, Elixir-, C++-, Scala- tai Java-kielen kehittäjä, joka liittyy tiimiin, voi olla tehokas Go-kielessä kuukaudessa sen yksinkertaisuuden vuoksi. Olemme huomanneet, että Go-kehittäjien tiimin rakentaminen on helpompaa verrattuna moniin muihin kieliin. Jos palkkaat ihmisiä Boulderin ja Amsterdamin kaltaisissa kilpailluissa ekosysteemeissä, tämä on tärkeä etu.
Syy 7 – Vahva ekosysteemi
Meidän kokoiselle tiimille (~20 henkilöä) ekosysteemillä on merkitystä. Et yksinkertaisesti voi luoda arvoa asiakkaillesi, jos joudut keksimään jokaisen pienen toiminnon osan uudelleen. Go:lla on loistava tuki käyttämillemme työkaluille. Redisille, RabbitMQ:lle, PostgreSQL:lle, Template parsingille, Task schedulingille, Expression parsingille ja RocksDB:lle oli jo saatavilla vankat kirjastot. Gon ekosysteemi on suuri voitto verrattuna muihin uudempiin kieliin, kuten Rustiin tai Elixiriin. Se ei tietenkään ole yhtä hyvä kuin Javan, Pythonin tai Noden kaltaiset kielet, mutta se on vankka ja moniin perustarpeisiin löytyy jo laadukkaita paketteja.
Syy 8 – Gofmt, pakotettu koodin muotoilu
Aloitetaan siitä, mikä on Gofmt? Ja ei, se ei ole kirosana. Gofmt on mahtava komentorivin apuohjelma, joka on rakennettu Go-kääntäjään koodin muotoilua varten. Toiminnaltaan se on hyvin samanlainen kuin Pythonin autopep8. Vaikka Silicon Valley -ohjelmassa kuvataan toisin, useimmat meistä eivät oikeasti halua kiistellä tabulaattoreista ja välilyönneistä. On tärkeää, että muotoilu on johdonmukaista, mutta varsinaisella muotoilustandardilla ei ole niinkään väliä. Gofmt välttää kaiken tämän keskustelun, koska sillä on yksi virallinen tapa muotoilla koodisi.
Syy 9 – gRPC ja protokollapuskurit
Gossa on ensiluokkainen tuki protokollapuskureille ja gRPC:lle. Nämä kaksi työkalua toimivat erittäin hyvin yhdessä rakennettaessa mikropalveluja, joiden on kommunikoitava RPC:n kautta. Sinun tarvitsee vain kirjoittaa manifesti, jossa määrittelet, mitä RPC-kutsuja voidaan tehdä ja mitä argumentteja ne ottavat vastaan. Sekä palvelin- että asiakaskoodi luodaan sitten automaattisesti tästä manifestista. Tuloksena syntyvä koodi on nopeaa, sen verkkojalanjälki on hyvin pieni ja sitä on helppo käyttää. Samasta manifestista voi luoda asiakaskoodia monille eri kielille, kuten C++:lle, Javalle, Pythonille ja Rubylle. Ei siis enää epäselviä REST-päätepisteitä sisäistä liikennettä varten, joita varten sinun on kirjoitettava joka kerta lähes sama asiakas- ja palvelinkoodi. .
Haitat 1 – Kehysten puute
Go:lla ei ole yhtä hallitsevaa kehystä, kuten Rails Rubylle, Django Pythonille tai Laravel PHP:lle. Tästä käydään kiivasta keskustelua Go-yhteisössä, sillä monet puhuvat sen puolesta, että kehyksiä ei kannata käyttää alun alkaenkaan. Olen täysin samaa mieltä siitä, että tämä on totta joissakin käyttötapauksissa. Jos joku kuitenkin haluaa rakentaa yksinkertaisen CRUD-API:n, hänellä on paljon helpompaa Djangon/DJRF:n, Railsin, Laravelin tai Phoenixin kanssa. Päivitys: kuten kommenteissa todettiin, on olemassa useita hankkeita, jotka tarjoavat kehyksen Go:lle. Revel, Iris, Echo, Macaron ja Buffalo näyttävät olevan kärjessä. Streamin käyttötapauksessa emme halua käyttää kehystä. Kuitenkin monille uusille projekteille, jotka haluavat tarjota yksinkertaisen CRUD API:n, hallitsevan kehyksen puuttuminen on vakava haitta.
Haitat 2 – Virheenkäsittely
Go käsittelee virheet yksinkertaisesti palauttamalla virheen funktiosta ja odottamalla kutsuvan koodin käsittelevän virheen (tai palauttavan sen ylöspäin kutsuvaan pinoon). Vaikka tämä lähestymistapa toimii, on helppo menettää laajuus siitä, mikä meni pieleen, jotta voit tarjota käyttäjille mielekkään virheilmoituksen. errors-paketti ratkaisee tämän ongelman, sillä sen avulla voit lisätä virheisiin kontekstin ja pinojäljen. Toinen ongelma on, että virheiden käsittely unohtuu helposti vahingossa. Staattisen analyysin työkalut, kuten errcheck ja megacheck, ovat käteviä näiden virheiden välttämiseksi. Vaikka nämä kiertotiet toimivat hyvin, se ei tunnu aivan oikealta. Odottaisi, että kieli tukisi kunnollista virheenkäsittelyä.
Haitat 3 – Pakettienhallinta
Päivitys: Gon pakettienhallinta on kehittynyt paljon tämän postauksen kirjoittamisen jälkeen. Go moduulit ovat tehokas ratkaisu, ainoa ongelma jonka olen nähnyt niiden kanssa on se, että ne rikkovat joitakin staattisen analyysin työkaluja kuten errcheck. Tässä on opetusohjelma Go:n käytön opetteluun Go-moduulien avulla. Go:n paketinhallinta ei ole missään nimessä täydellinen. Oletusarvoisesti siinä ei ole tapaa määritellä tiettyä versiota riippuvuudesta, eikä siinä ole tapaa luoda toistettavia rakennelmia. Pythonilla, Nodella ja Rubylla on paremmat järjestelmät pakettien hallintaa varten. Oikeilla työkaluilla Go:n paketinhallinta toimii kuitenkin varsin hyvin. Voit käyttää Depiä riippuvuuksien hallintaan, jotta voit määrittää ja kiinnittää versiot. Tämän lisäksi olemme osallistuneet avoimen lähdekoodin VirtualGo-työkaluun, joka helpottaa työskentelyä useiden Go-kielellä kirjoitettujen projektien parissa.
Python vs. Go
Päivitys: Suorituskykyero Pythonin ja Go:n välillä on kasvanut tämän postauksen kirjoittamisen jälkeen. (Go muuttui nopeammaksi ja Python ei) Yksi mielenkiintoinen kokeilu, jonka teimme, oli ottaa ranked feed -toiminnallisuutemme Pythonissa ja kirjoittaa se uudelleen Go:lla. Katso tätä esimerkkiä ranking-menetelmästä:
Kummankin Python- ja Go-koodin on tehtävä seuraavat asiat tukeakseen tätä ranking-menetelmää:
- Parsi lauseke pistemäärää varten. Tässä tapauksessa haluamme muuttaa tämän merkkijonon ”simple_gauss(time)*popularity” funktioksi, joka ottaa syötteenä aktiviteetin ja palauttaa tulosteena pistemäärän.
- Luo osittaisfunktioita JSON-konfiguraation perusteella. Haluamme esimerkiksi, että ”simple_gauss” kutsuu ”decay_gauss”-funktiota, jonka skaala on 5 päivää, offset 1 päivä ja decay-kerroin 0.3.
- Kertaa ”defaults”-konfiguraatio, jotta sinulla on varasuunnitelma, jos tiettyä kenttää ei ole määritetty aktiviteetissa.
- Käytä vaiheen 1 funktiota pisteyttääksesi kaikki aktiviteetit syötteessä.
Luokittelukoodin Python-version kehittäminen kesti noin kolme päivää. Siihen sisältyy koodin, yksikkötestien ja dokumentaation kirjoittaminen. Seuraavaksi käytimme noin 2 viikkoa koodin optimointiin. Yksi optimoinneista oli pisteytyslausekkeen (simple_gauss(time)*popularity) kääntäminen abstraktiksi syntaksipuuksi. Toteutimme myös välimuistilogiikan, joka laski pisteet etukäteen tietyille tuleville ajankohdille. Sen sijaan tämän koodin Go-koodin kehittäminen kesti noin 4 päivää. Suorituskyky ei vaatinut lisäoptimointia. Vaikka siis alkuvaiheen kehitys oli nopeampaa Pythonissa, Go-pohjainen versio vaati tiimiltämme lopulta huomattavasti vähemmän työtä. Lisähyötynä Go-koodi toimi noin 40 kertaa nopeammin kuin erittäin optimoitu Python-koodimme. Tämä on vain yksi esimerkki suorituskyvyn paranemisesta, jonka olemme kokeneet siirtymällä Go-koodiin. Siinä tietenkin verrataan omenoita appelsiineihin:
- Ranking-koodi oli ensimmäinen Go-kielinen projektini
- Go-koodi rakennettiin Python-koodin jälkeen, joten käyttötapausta ymmärrettiin paremmin
- Lausekkeiden jäsennykseen tarkoitettu Go-kirjasto oli poikkeuksellisen hyvälaatuinen
Mittarilukemasi vaihtelee. Joidenkin muiden järjestelmämme komponenttien rakentaminen Go:lla vei huomattavasti enemmän aikaa kuin Pythonilla. Yleisenä suuntauksena voidaan todeta, että Go-koodin kehittäminen vaatii hieman enemmän työtä. Käytämme kuitenkin paljon vähemmän aikaa koodin optimointiin suorituskyvyn kannalta.
Elixir vs Go – The Runner Up
Toinen arvioimamme kieli on Elixir. Elixir on rakennettu Erlangin virtuaalikoneen päälle. Se on kiehtova kieli, ja harkitsimme sitä, koska yhdellä tiimimme jäsenistä on paljon kokemusta Erlangista. Käyttötapauksissamme huomasimme, että Go:n raaka suorituskyky on paljon parempi. Sekä Go että Elixir tekevät hyvää työtä palvellessaan tuhansia samanaikaisia pyyntöjä. Jos kuitenkin tarkastellaan yksittäisten pyyntöjen suorituskykyä, Go on käyttötapauksissamme huomattavasti nopeampi. Toinen syy, miksi valitsimme Go:n Elixirin sijaan, oli ekosysteemi. Tarvitsemiemme komponenttien osalta Go:lla oli kypsemmät kirjastot, kun taas monissa tapauksissa Elixir-kirjastot eivät olleet valmiita tuotantokäyttöön. Kehittäjiä on myös vaikeampi kouluttaa/löytää työskentelemään Elixirin kanssa. Nämä syyt kallistivat vaakakupin Go:n eduksi. Phoenix-kehys Elixirille näyttää kuitenkin mahtavalta ja on ehdottomasti katsomisen arvoinen.
Johtopäätös
Go on erittäin suorituskykyinen kieli, jossa on hyvä tuki samanaikaisuudelle. Se on lähes yhtä nopea kuin C++:n ja Javan kaltaiset kielet. Vaikka Go:lla rakentaminen vie hieman enemmän aikaa verrattuna Pythoniin tai Rubyyn, säästät paljon aikaa, joka kuluu koodin optimointiin. Meillä Streamissa on pieni kehitystiimi, joka tuottaa syötteitä ja chattia yli 500 miljoonalle loppukäyttäjälle. Go on loistava valinta, koska siinä yhdistyvät suuri ekosysteemi, helppo aloitus uusille kehittäjille, nopea suorituskyky, vankka tuki samanaikaisuudelle ja tuottava ohjelmointiympäristö. Stream käyttää edelleen Pythonia kojelautaan, sivustoon ja koneoppimiseen personoituja syötteitä varten. Emme aio hyvästellä Pythonia lähiaikoina, mutta jatkossa kaikki suorituskykyintensiivinen koodi kirjoitetaan Go-kielellä. Myös uusi Chat API on kirjoitettu kokonaan Go-kielellä. Jos haluat oppia lisää Go:sta, tutustu alla oleviin blogikirjoituksiin. Jos haluat oppia lisää Streamista, tämä interaktiivinen opetusohjelma on hyvä lähtökohta.
Lisälukemista Golangiin siirtymisestä
- https://movio.co/en/blog/migrate-Scala-to-Go/
- https://hackernoon.com/why-i-love-golang-90085898b4f7
- https://sendgrid.com/blog/convince-company-go-golang/
- https://dave.cheney.net/2017/03/20/why-go
Learning Go
- https://learnxinyminutes.com/docs/go/
- https://tour.golang.org/
- http://howistart.org/posts/go/1/
- https://getstream.io/blog/building-a-performant-api-using-go-and-cassandra/
- https://www.amazon.com/gp/product/0134190440
- Go Rocket Tutorial