Frissítve 2019. május 14-én, hogy jobban tükrözze a Go elmúlt két évben történt fejlesztéseit (csomagkezelés, jobb teljesítmény, gyorsabb fordítási idők és egy érettebb ökoszisztéma) Új nyelvre váltani mindig nagy lépés, különösen, ha csak a csapat egyik tagjának van korábbi tapasztalata az adott nyelvvel. Az év elején a Stream elsődleges programozási nyelvét Pythonról Go nyelvre váltottuk. Ez a bejegyzés elmagyaráz néhány okot, amiért úgy döntöttünk, hogy magunk mögött hagyjuk a Pythont, és áttérünk a Go nyelvre. Köszönjük Ren Sakamotónak, hogy lefordította japánra a Miért váltottunk Pythonról Go-ra なぜ私達は Python から Go に移行したのか.
- 1. ok – Teljesítmény
- 2. ok – A nyelv teljesítménye számít
- 3. ok – Fejlesztői termelékenység & Nem túl kreatív
- 4. ok – Párhuzamosság & Csatornák
- 5. ok – gyors fordítási idő
- 6. ok – A csapatépítés képessége
- 7. ok – Erős ökoszisztéma
- 8. ok – Gofmt, Enforced Code Formatting
- 9. ok – gRPC és protokollpufferek
- Hátrány 1 – Keretrendszerek hiánya
- Hátrány 2 – Hibakezelés
- Hátrány 3 – Csomagkezelés
- Python vs Go
- Elixir vs Go – A befutó
- Következtetés
- Tovább olvasnivaló a Golangra való átállásról
- Go tanulása
1. ok – Teljesítmény
A Go gyors! A Go rendkívül gyors. A teljesítménye hasonló a Java vagy a C++ teljesítményéhez. A mi felhasználási esetünkben a Go jellemzően 40-szer gyorsabb, mint a Python. Íme egy kis benchmark-játék a Go és a Python összehasonlítása.
2. ok – A nyelv teljesítménye számít
Sok alkalmazás esetében a programozási nyelv egyszerűen a ragasztó az alkalmazás és az adatbázis között. Magának a nyelvnek a teljesítménye általában nem sokat számít. A Stream azonban egy API-szolgáltató, amely 700 vállalat és több mint 500 millió végfelhasználó számára üzemeltet feed- és chatplatformot. Évek óta optimalizáljuk a Cassandra, PostgreSQL, Redis stb. rendszereket, de végül elérjük a használt nyelv határait. A Python egy nagyszerű nyelv, de a teljesítménye meglehetősen lassú az olyan felhasználási esetekben, mint a szerializáció/deserializáció, rangsorolás és aggregálás. Gyakran futottunk bele olyan teljesítményproblémákba, amikor a Cassandra 1 ms-ig tartott az adatok lekérdezése, és a Python a következő 10 ms-ot azzal töltötte, hogy objektumokká alakítsa őket.
3. ok – Fejlesztői termelékenység & Nem túl kreatív
Nézze meg ezt a kis Go kódrészletet a How I Start Go tutorialból. (Ez egy nagyszerű bemutató, és jó kiindulópont ahhoz, hogy elsajátítsunk egy kis Go-t.)
Ha új vagy a Go-ban, nem sok minden fog meglepni, ha elolvasod ezt a kis kódrészletet. Bemutatja a többszörös hozzárendelést, az adatstruktúrákat, a mutatókat, a formázást és a beépített HTTP könyvtárat. Amikor elkezdtem programozni, mindig is szerettem a Python fejlettebb funkcióit használni. A Python lehetővé teszi, hogy elég kreatív legyen a kód, amit írsz. Például:
- A MetaClasses használata az osztályok önregisztrációjához a kód inicializálásakor
- A True és False felcserélése
- Függvények hozzáadása a beépített függvények listájához
- Operátorok túlterhelése mágikus módszerekkel
- Függvények használata tulajdonságként a @property dekorátoron keresztül
Ezekkel a funkciókkal szórakoztató játszani, de, a legtöbb programozó egyetért azzal, hogy gyakran megnehezítik a kód megértését, amikor valaki más munkáját olvassuk. A Go arra kényszerít, hogy ragaszkodj az alapokhoz. Ez nagyon megkönnyíti, hogy bárki kódját elolvassuk, és azonnal megértsük, mi történik. Megjegyzés: Az, hogy ez mennyire “könnyű”, természetesen a felhasználási esettől függ. Ha egy alapvető CRUD API-t akarsz létrehozni, akkor még mindig a Django + DRF-et, vagy a Rails-t ajánlanám.
4. ok – Párhuzamosság & Csatornák
A Go nyelvként igyekszik a dolgokat egyszerűen tartani. Nem vezet be sok új fogalmat. A hangsúly egy egyszerű nyelv létrehozásán van, amellyel hihetetlenül gyors és könnyű dolgozni. Az egyetlen terület, ahol mégis újítást hoz, a goroutine-ok és a csatornák. (Hogy 100%-ig pontos legyek, a CSP koncepciója 1977-ben kezdődött, így ez az újítás inkább egy régi ötlet új megközelítése). A goroutine-ok a Go könnyített megközelítése a szálkezelésnek, a csatornák pedig a goroutine-ok közötti kommunikáció preferált módja. A goroutinokat nagyon olcsón lehet létrehozni, és csak néhány KB többletmemóriát igényelnek. Mivel a goroutine-ok ilyen könnyűek, lehetséges, hogy több száz vagy akár több ezer goroutine fut egyszerre. A goroutine-ok között csatornák segítségével lehet kommunikálni. A Go futásidő kezeli az összes komplexitást. A goroutine-ok és az egyidejűség csatorna-alapú megközelítése nagyon egyszerűvé teszi az összes rendelkezésre álló CPU-mag kihasználását és az egyidejű IO kezelését – mindezt a fejlesztés bonyolítása nélkül. A Python/Java-hoz képest egy függvény futtatása egy goroutine-on minimális boilerplate kódot igényel. A függvényhívást egyszerűen a “go” kulcsszóval kell megelőlegezni:
https://tour.golang.org/concurrency/1 A Go párhuzamossági megközelítése nagyon könnyen kezelhető. Ez egy érdekes megközelítés a Node-hoz képest, ahol a fejlesztőnek nagyon oda kell figyelnie arra, hogyan kezeli az aszinkron kódot. Az egyidejűség másik nagyszerű aspektusa a Go-ban a versenyérzékelő. Ez megkönnyíti annak kiderítését, hogy vannak-e versenyfeltételek az aszinkron kódodban.
Kopp-kopp versenyfeltétel Ki van ott?
– I Am Devloper (@iamdevloper) November 11, 2013
Itt van néhány jó forrás a Go és a csatornák használatához:
- 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
- Goroutines vs Green threads
5. ok – gyors fordítási idő
A legnagyobb Go nyelven írt mikroszolgáltatásunk jelenleg 4 másodperc alatt fordítható. A Go gyors fordítási ideje jelentős termelékenységi nyereség az olyan nyelvekhez képest, mint a Java és a C++, amelyek a lassú fordítási sebességükről híresek. Szeretem a kardcsörtetést, de még szebb, ha a dolgokat úgy tudom elvégezni, hogy még emlékszem, mit kell tennie a kódnak:
6. ok – A csapatépítés képessége
Először is kezdjük a nyilvánvalóval: nincs annyi Go fejlesztő a régebbi nyelvekhez, például a C++-hoz és a Javához képest. A StackOverflow szerint a fejlesztők 38%-a ismeri a Javát, 19,3%-a a C++-t és csak 4,6%-a a Gót. A GitHub adatai hasonló tendenciát mutatnak: A Go elterjedtebb, mint az olyan nyelvek, mint az Erlang, a Scala és az Elixir, de kevésbé népszerű, mint a Java és a C++. Szerencsére a Go egy nagyon egyszerű és könnyen megtanulható nyelv. Az alapvető funkciókat biztosítja, amire szükséged van, és semmi mást. Az új fogalmak, amelyeket bevezet, a “defer” utasítás és az egyidejűség beépített kezelése a “go rutinokkal” és csatornákkal. (A puristáknak: A Go nem az első nyelv, amely megvalósítja ezeket a fogalmakat, csak az első, amely népszerűvé teszi őket). Bármely Python, Elixir, C++, Scala vagy Java fejlesztő, aki csatlakozik egy csapathoz, az egyszerűsége miatt egy hónapon belül hatékony lehet a Go-ban. Úgy találtuk, hogy sok más nyelvhez képest könnyebb Go fejlesztőkből álló csapatot építeni. Ha olyan versenyképes ökoszisztémákban veszünk fel embereket, mint Boulder és Amszterdam, ez fontos előny.”
7. ok – Erős ökoszisztéma
Egy ilyen méretű csapatnál (~20 fő) az ökoszisztéma számít. Egyszerűen nem tudsz értéket teremteni az ügyfeleid számára, ha minden apró funkcionalitást újra fel kell találnod. A Go nagyszerű támogatást nyújt az általunk használt eszközökhöz. A Redis, RabbitMQ, PostgreSQL, Template parsing, Task scheduling, Expression parsing és RocksDB számára már szilárd könyvtárak álltak rendelkezésre. A Go ökoszisztémája nagy nyereség más újabb nyelvekhez, például a Rusthoz vagy az Elixirhez képest. Természetesen nem olyan jó, mint az olyan nyelvek, mint a Java, a Python vagy a Node, de szilárd, és sok alapvető igényre már most is találunk jó minőségű csomagokat.
8. ok – Gofmt, Enforced Code Formatting
Kezdjük azzal, hogy mi az a Gofmt? És nem, ez nem egy káromkodás. A Gofmt egy fantasztikus parancssori segédprogram, ami a Go fordítóba van beépítve a kód formázására. Funkcionalitását tekintve nagyon hasonlít a Python autopep8-hoz. Bár a Silicon Valley című sorozat másképp mutatja be, a legtöbben nem igazán szeretünk a tabulátorok és a szóközök között vitatkozni. Fontos, hogy a formázás következetes legyen, de a tényleges formázási szabvány nem igazán számít. A Gofmt elkerüli ezt a vitát azzal, hogy egyetlen hivatalos módja van a kód formázásának.
9. ok – gRPC és protokollpufferek
A Go első osztályú támogatást nyújt a protokollpufferekhez és a gRPC-hez. Ez a két eszköz nagyon jól működik együtt olyan mikroszolgáltatások építésénél, amelyeknek RPC-n keresztül kell kommunikálniuk. Csak egy manifesztet kell írni, amelyben meghatározzuk, hogy milyen RPC-hívásokat lehet végrehajtani, és milyen argumentumokat fogadnak el. Ezután mind a szerver, mind a kliens kód automatikusan generálódik ebből a manifesztből. Az így kapott kód egyszerre gyors, nagyon kis hálózati lábnyomú és könnyen használható. Ugyanabból a manifesztből klienskódot generálhat számos különböző nyelven, például C++, Java, Python és Ruby nyelven is. Tehát nincs többé félreérthető REST végpont a belső forgalomhoz, amelyhez minden alkalommal szinte ugyanazt a kliens- és szerverkódot kell megírnia. .
Hátrány 1 – Keretrendszerek hiánya
A Go nem rendelkezik egyetlen domináns keretrendszerrel, mint a Rails a Ruby-hoz, a Django a Pythonhoz vagy a Laravel a PHP-hoz. Ez heves viták témája a Go közösségen belül, mivel sokan azt támogatják, hogy eleve ne használjunk keretrendszert. Teljesen egyetértek azzal, hogy ez bizonyos felhasználási esetekre igaz. Azonban ha valaki egy egyszerű CRUD API-t akar építeni, sokkal könnyebb dolga lesz a Django/DJRF, Rails Laravel vagy Phoenix segítségével. Frissítés: ahogy a hozzászólások rámutattak, több olyan projekt is van, amely keretrendszert biztosít a Go számára. A Revel, az Iris, az Echo, a Macaron és a Buffalo tűnnek a vezető versenyzőknek. A Stream felhasználási esetére inkább nem használunk keretrendszert. Azonban sok új projekt számára, amelyek egy egyszerű CRUD API-t szeretnének biztosítani, a domináns keretrendszer hiánya komoly hátrányt fog jelenteni.
Hátrány 2 – Hibakezelés
A Go úgy kezeli a hibákat, hogy egyszerűen visszaad egy hibát egy függvényből, és elvárja, hogy a hívó kód kezelje a hibát (vagy adja vissza a hívó veremre). Bár ez a megközelítés működik, könnyen elveszítheti annak hatókörét, hogy mi ment rosszul, hogy biztosítsa, hogy értelmes hibát adjon a felhasználóknak. Az errors csomag megoldja ezt a problémát azzal, hogy lehetővé teszi, hogy a hibákhoz kontextust és veremkövetést adjon. Egy másik probléma, hogy könnyen előfordulhat, hogy véletlenül elfelejtünk kezelni egy hibát. Az olyan statikus elemző eszközök, mint az errcheck és a megacheck praktikusak az ilyen hibák elkerülésére. Bár ezek a megoldások jól működnek, nem érzem teljesen helyesnek. Az ember elvárná, hogy a megfelelő hibakezelést a nyelv támogassa.
Hátrány 3 – Csomagkezelés
Frissítés: A Go csomagkezelése sokat fejlődött, mióta ez a bejegyzés megíródott. A Go modulok hatékony megoldást jelentenek, az egyetlen probléma, amit láttam velük kapcsolatban, hogy megtörnek néhány statikus elemző eszközt, mint például az errcheck. Itt van egy bemutató a Go modulok használatával történő Go használatának megtanulásához. A Go csomagkezelése korántsem tökéletes. Alapértelmezés szerint nincs módja egy függőség konkrét verziójának megadására, és nincs mód reprodukálható buildek létrehozására. A Python, a Node és a Ruby mind jobb csomagkezelő rendszerekkel rendelkezik. A megfelelő eszközökkel azonban a Go csomagkezelése elég jól működik. A Dep segítségével kezelheted a függőségeket, hogy lehetővé váljon a verziók megadása és kitűzése. Ezen kívül hozzájárultunk egy VirtualGo nevű nyílt forráskódú eszközzel, amely megkönnyíti a munkát több Go nyelven írt projekten.
Python vs Go
Frissítés: A Python és a Go közötti teljesítménykülönbség nőtt a bejegyzés megírása óta. (A Go gyorsabb lett, a Python nem) Egy érdekes kísérletet végeztünk, amikor fogtuk a Pythonban lévő rangsorolt feed funkcionalitásunkat és átírtuk Go-ban. Nézze meg ezt a példát egy rangsorolási módszerre:
Mind a Python, mind a Go kódnak a következőket kell tennie a rangsorolási módszer támogatásához:
- Parse the expression for the score. Ebben az esetben ezt a “simple_gauss(time)*popularity” karakterláncot egy olyan függvénnyé akarjuk alakítani, amely bemenetként egy tevékenységet fogad, és kimenetként egy pontszámot ad vissza.
- A JSON konfiguráció alapján részfüggvényeket hozunk létre. Például azt akarjuk, hogy a “simple_gauss” hívja meg a “decay_gauss”-t 5 napos skálával, 1 napos eltolással és 0,3-as lecsengési faktorral.
- Az “defaults” konfigurációt elemezzük, hogy legyen egy tartalék, ha egy bizonyos mező nincs definiálva egy tevékenységen.
- Az 1. lépésből származó függvényt használjuk a feedben lévő összes tevékenység pontozására.
A rangsoroló kód Python verziójának fejlesztése nagyjából 3 napot vett igénybe. Ez magában foglalja a kód megírását, az egységteszteket és a dokumentációt. Ezután körülbelül 2 hetet töltöttünk a kód optimalizálásával. Az egyik optimalizálás a score kifejezés (simple_gauss(time)*popularity) absztrakt szintaxisfává történő lefordítása volt. Olyan gyorsítótárazási logikát is implementáltunk, amely a jövőben bizonyos időpontokra előre kiszámította a pontszámot. Ezzel szemben e kód Go változatának fejlesztése nagyjából 4 napot vett igénybe. A teljesítmény nem igényelt további optimalizálást. Tehát bár a fejlesztés kezdeti része Pythonban gyorsabb volt, a Go alapú verzió végül lényegesen kevesebb munkát igényelt a csapatunktól. További előnyként a Go kód nagyjából 40-szer gyorsabban teljesített, mint az erősen optimalizált Python kódunk. Nos, ez csak egyetlen példa arra a teljesítménynövekedésre, amit a Go kódra való átállással tapasztaltunk. Természetesen almát és narancsot hasonlítunk össze:
- A rangsoroló kód volt az első Go-s projektem
- A Go kód a Python kód után készült, így a felhasználási esetet jobban megértettük
- A kifejezéselemzésre szolgáló Go könyvtár kivételes minőségű volt
A te mérföldköved eltérő lesz. A rendszerünk néhány más komponensét lényegesen több időbe telt Go-ban megépíteni, mint Pythonban. Általános tendenciaként azt látjuk, hogy a Go kód fejlesztése valamivel több erőfeszítést igényel. Ugyanakkor sokkal kevesebb időt töltünk a kód teljesítményoptimalizálásával.
Elixir vs Go – A befutó
Egy másik általunk vizsgált nyelv az Elixir. Az Elixir az Erlang virtuális gépre épül. Ez egy lenyűgöző nyelv, és megfontoltuk, mivel az egyik csapattagunknak rengeteg tapasztalata van az Erlanggal. A mi felhasználási eseteink esetében azt vettük észre, hogy a Go nyers teljesítménye sokkal jobb. Mind a Go, mind az Elixir nagyszerű munkát végez több ezer egyidejű kérés kiszolgálásával. Ha azonban az egyes kérések teljesítményét nézzük, a Go lényegesen gyorsabb a mi felhasználási esetünkben. A másik ok, amiért a Go-t választottuk az Elixir helyett, az ökoszisztéma volt. Az általunk igényelt komponensekhez a Go kiforrottabb könyvtárakkal rendelkezett, míg az Elixir könyvtárak sok esetben még nem álltak készen a termelési használatra. Emellett nehezebb képezni/keresni fejlesztőket az Elixirrel való munkára. Ezek az okok a Go javára billentették a mérleget. A Phoenix keretrendszer az Elixirhez azonban nagyszerűnek tűnik, és mindenképpen érdemes megnézni.
Következtetés
A Go egy nagyon hatékony nyelv, nagyszerű párhuzamossági támogatással. Majdnem olyan gyors, mint az olyan nyelvek, mint a C++ és a Java. Bár a Go használatával kicsit több időt vesz igénybe a dolgok elkészítése a Pythonhoz vagy a Rubyhoz képest, rengeteg időt spórolhatsz meg a kód optimalizálására fordított időből. Egy kis fejlesztőcsapatunk van a Streamnél, amely több mint 500 millió végfelhasználó számára biztosítja a feedeket és a chatet. A Go a nagyszerű ökoszisztéma, az új fejlesztők számára könnyű bevezetés, a gyors teljesítmény, az egyidejűség szilárd támogatása és a produktív programozási környezet kombinációja miatt nagyszerű választás. A Stream még mindig a Pythont használja a műszerfalon, a webhelyen és a gépi tanulásban a személyre szabott feedekhez. Nem fogunk egyhamar búcsút inteni a Pythonnak, de a jövőben minden teljesítményigényes kódot Go nyelven fogunk írni. Az új csevegő API-nk is teljes egészében Go nyelven íródott. Ha többet szeretne megtudni a Góról, olvassa el az alábbi blogbejegyzéseket. Ha többet szeretne megtudni a Streamről, ez az interaktív bemutató remek kiindulópont.
Tovább olvasnivaló a Golangra való átállásról
- 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
Go tanulása
- 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