Miért váltottunk Pythonról Go-ra

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

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:

  1. 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.
  2. 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.
  3. Az “defaults” konfigurációt elemezzük, hogy legyen egy tartalék, ha egy bizonyos mező nincs definiálva egy tevékenységen.
  4. 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

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.