Golang micro-services, první ohlédnutí

Je to zhruba půl roku, co jsme začali vyvíjet nový produkt: smečku mikro-servis, běžících v cloudu (nebo v Dockeru). Všechny jsou napsány v Golangu a protože to pro náš 2-pizza team byla nová technologie, bylo dobré si udělat retrospektivu.

Hodnocení a pojetí bylo různorodé — od kritického náhledu na to, co generuje kompilátor, až ready-steady-go feeling (a.k.a. rapid start development). Já jsem si tradičně sepsal mind mapu a aby nepřišla nazmar, tak si ji převyprávím.

Intuitivní pocit

Čím jsem starší, tím jsem intuitivnější. A je to věc, která se špatně kvantifikuje a zejména se špatně někomu vysvětluje. Každopádně…

Když jsem se letos na jaře dostal ke Golangu (jako slepý k houslím), byl jsem chvíli nejistý — první, co moje oko ze syntaxe vypíchlo, byly * před definicí typů. “Šmarjapano, pointry!”, chytal jsem se za hlavu. C-čko nikdy nebylo můj oblíbený jazyk.

Ale pak jsem začal v Go programovat a zjistil jsem, že:

  • se v něm dobře píše,
  • dá se to velmi rychle naučit
  • a celkově působí Golang velice odlehčeně (v dobrém smyslu).

Jenže jak píšu, intuice se dost špatně prodává. Takže když se na to podíváme trochu objektivněji…

Pozitiva

Zdroj: pixabay.com

Zdroj: pixabay.com

Clojure-like 😲

Jedna z časných věcí, co ve mně rychle zarezonovala, byly občasné paralelní podobnosti s… Clojure. Možná teď kroutíte hlavama a možná tam vidím něco, co tam není — jako polyglot např. tvrdím, že Čeština a Španělština mají velmi podobnou gramatiku — ale já bych řekl, že mezi společné body patří:

  • Functional(-like) přístup — Golang sice není funkcionální, ale dá se v něm tak psát (a já v něm taky tak píšu — na věšení funkcí na struct mě moc neužije). Navíc, funkce je vestavěný typ, takže psát high-order funkce je pohoda.
  • HTTP abstrakce — Když jsem se poprvé setkal s gorilla/mux, byl jsem hned doma: gorilla/mux = ring + compojure, tedy starý dobrý handlery a middlewary, plus trošku toho routování.
  • Jazykový puristi by mě asi pěkně sjeli, ale pro mě jsou Golang interfacy ekvivalentem Clojure protokolů.
  • Golang channels jsou jasnou (a přiznanou) inspirací pro Clojure knihovnu core.async.
  • Poslední aspekt míří za hranice obou jazyků — jak Golang, tak Clojure komunita silně preferuje knihovny před frameworky.

Core funkcionality

Líbí se mi, že na úrovni jazyka je k dispozici mraky vestavěných (a šikovných) funkcí. Speciálně, ohledně mikro-servis jde o package net/http, který obsahuje HTTP Server, HTTP Handler a HTTP klienta.

Stejně tak je přímo na úrovni jazyka vyřešený Context, ať už pro potřeby libovolného klienta, nebo třeba jako mechanismus pro kontrolu konkurence.

A nesmím zapomenout na přímou podporu pro současnou linguu francu: JSON. Chcete marshallovat/demarshallovat z/do struct? Chcete enkódovat/dekódovat JSON? Máte to mít — out of the box.

Konkurence

K tomu snad není co dodat — Golang konkurence je parádní: kombo goroutines a channels je zkrátka bez-konkurenční. 😸 👍

Jednoduchost

V sekci Intuitivní pocit jsem zmiňoval, že Golang je “light-weight”. K tomu přispívá zejména, že:

  • nemá objekty — pokud chcete, můžete psát jenom procedurálně/funkcionálně. A pokud bez toho nemůže být, můžete si na structy navěsit funkce a přidat constructor-like factory funkci.
  • nemá výjimky — místo toho může funkce vracet více hodnot a jedna z nich je error. Může to být diskutabilní design, ale než se pustíte do plamenného zatracování, přečtěte si článek Why Go gets exceptions right.
  • preferuje composition over inheritance.
  • Na to, abyste vytvořili “production-ready” mikro-servisu, stačí vám jazyk samotný a jeho core knihovny. Nepotřebujete:
    • žádný kontejner (a.k.a. servlet),
    • žádný aplikační server,
    • žádné runtime prostředí (vyjma operačního systému).

A musím říct, že po 12 letech v Javě je to osvobozující.

Testy

Psaní testů v Golangu je fajn: jednoduché, stručné a čitelné. Hodně se mi líbí (a používám) tabulkové testy. Také se mi líbí (a zatím nepoužívám) benchmarky a examply.

Pro testování mikro-servis je šikovná (core) knihovna httptest, která umožňuje testovat produkční handlery vůči lokálnímu, z testu spuštěnému HTTP(S) Serveru.

Mikro-servisy musí někde běžet — my je do cloudu deployujeme Terraformem. Terraform a (Packer) se dají dobře testovat Terratestem. Je to trochu obsáhlejší záležitost, o kterém možná časem napíšu samostatně. (Mmch. koncept Infrastructure as Code jde ruku v ruce s Continuous Delivery, což je téma, které mám v roadmapě.)

Domain fit & synergie

Ačkoliv je Golang “general-purpose language”, vnímám to tak, že si (prozatím?) vydobyl určitou doménu — jednak systémové programování (takové ty CLI, typicky Unix utilitky) a jednak věci kolem cloud infrastruktury:

V podstatě na co jsem posledního půlroku sáhnul, tak bylo napsaný v Golangu. Je to jenom náhoda? 🤔 Čistě jen pro úplnost ještě přihodím distribuovaný key-value store etcd a GitLab Runner jako komponent distribuovaného Continuous Delivery.

Negativa

Zdroj: pixabay.com

Zdroj: pixabay.com

Chybějící “standardy”

Go má distribuovanou komunitu, která je hodně fragmentovaná — to, že se koncentruje na GitHubu je signifikantní (totéž platí částečně pro Clojure). Pokud k tomu přičteme rozšířený a podporovaný přístup DIY, je výsledkem, že spoustu věcí si každý řeší po svém.

Typickým příkladem je např. logování — chybí jednotný logovací interface, existuje kolem 15 soupeřících (a nekompatibilních) implementací a nějaká konvergence je v nedohlednu.

Zdroj: xkcd.com

Zdroj: xkcd.com

Podobná situace vládne ve formátu konfiguračních souborů – někdo je má v TOML, někdo v YAML, někdo v JSON… 🤦

Samostatnou kapitolou je version a dependency management — to byl můj nejzásadnější problém za uplynulý půlrok. Ani se mi to nechce moc rozebírat. Tady snad ale problémy vyřeší Go Modules (ale že to trvalo!).

Enterprise připravenost

Věc, kterou jsme v práci dost řešili — jak používat Golang v enterprise sféře. Takový ty věci jako:

  • proprietární source code repository,
  • práce za proxy (to je bolest! 🤕),
  • reprodukovatelnost buildů,
  • cachování závislostí,
  • proprietární závislosti (knihovny, co nemůžete mít na GitHubu).

Částečně jsem se tomu věnoval v článku Správa proprietárních závislostí v Golang a ještě se k tomu vrátím v postu Správa proprietárních závislostí v Golang: go modules (TBD).

Tady je to jasné — Golang ještě není “enterprise ready” a člověk musí vymyslet a naimplementovat pár workaroundů, aby to přijatelně fungovalo. Ale snad se blýská na lepší časy s projektem Athens (o kterém jsem referoval z GopherConu).

Pointers

Už jsem to naznačoval v úvodu — Pointry nejsou můj šálek kávy. No ale budiž, na druhou stranu jsem zastáncem, že když už člověk nějaký nástroj používá, měl by ho používat správně a efektivně (ne nadarmo má tenhle blog podtitul Master your tools!).

U pointrů v Go mi ale tak nějak schází motivace/rationale, proč tam vlastně jsou:

  • schází pointer arithmetic,
  • kompilátor se téměř(?) vždycky rozhodne lépe než člověk, jak daný kód zoptimalizovat,
  • (zejména u generovaného kódu) dochází k absurditám jako *bool, či *string.

Kolekce

V Javě jsem začínal na verzi 1.4 a tak jsem zhruba přes 10 let strávil psaním v “moderní” Javě, tedy Javě, která má Collections API. Pokud jsem někdy používal Java Array, tak jenom proto, že jsem upravoval nějaký prastarý legacy kód, nebo musel používat nějaký obstarožní framework (naposled třeba CMP modul od Bouncy Castle 😢).

(Střih do současnosti) Golang má v podstatě jenom pole a mapu. Sice existuje třeba list, ale za posledního půl roku jsem prolezl opravud hodně zdrojáků na GitHubu a stejně nikdo nic jiného, než pole a mapy nepoužívá.

Tak nevím — používat na veškeré data operace ten nejprimitivnější sekvenčně-datový typ mi přijde… primitivní.

(Zatím) netečný

Zdroj: pixabay.com

Zdroj: pixabay.com

Je pár věcí, ke kterým jsem se v Golangu zatím ještě nedostal, hlouběji je neprozkoumal, anebo je vlastně ještě nepotřeboval. Nespadají tak ani do pozitivních, ani negativních věcí. Možná se do jedné z těchto kategorií časem přehoupnou, možná že zůstanou tam kde jsou.

gRPC

Dnešním světem vládne REST. gRPC může být zajímavá alternativa, ale nejsem si moc jistý, jak je to etablovaný a jak se to globálně rozšíří. Nechám tomu nějaký čas a uvidím.

Interfaces

Golang interfacy jsou zajímavě pojaté a je to jedno z témat, do kterých bych se rád víc ponořil. Prozatím, přiznávám se, jsem nenapsal ani jediný. Možná je to mým zlomovým útěkem od Javy, že se jim podvědomě vyhýbám. Radši si je prvně z povzdálí pořádně očíhnu, než si s nima ušpiním ruce.

Security

Nic komplexnějšího, týkajícího se security, jsem v Go zatím nedělal. Samozřejmě, nemám na mysli takové ty drobnosti, jako TLS Server, nebo načtení/vygenerování privátního klíče. Tak snad se časem něco zajímavého vyvrbí.

Shrnutí

Golang retrospektivu jsme v práci dělali tři programátoři a skončilo to výsledkem 2:1 ve prospěch Go. Za mne jednoznačně palec nahoru 👍 a klidně bych další projekt podobného typu dělal znova v Golangu.

Pokud by příští projekt neměl být postavený na mikro-servisách, ale na komplikovanější architektuře, bylo by dobré si to znovu vyhodnotit — přece jenom, Go (a vývoj v něm) má svoje úskalí. Ale zatím, zatím dobrý!

Mind Map