Google Go

Google Go

Google Go, også kjent som golang, ble utviklet av Google tilbake i 2007. Det er et statisk typet språk som fokuserer på stabilitet, ytelse og enkelhet. Syntaksen er løst derivert fra C og legger til garbage collection, typesikkerhet, dynamisk typing, key-value maps, i tillegg til et rikt standardbibliotek.

Denne bloggposten vil vise hva Go er, hva det brukes til og hvordan du kan komme i gang med Go. Begynnelsen vil omhandle Go generelt, fordelene med Go og en kort introduksjon til syntaks. Videre går vi gjennom eksempler på hvordan samtidighet og synkronisering kan gjøres i Go, før vi avslutter med et eksempel på en REST-service i Go.

Etter du har lest denne bloggposten vil du forhåpentligvis ha fått et innblikk i hva Go kan gjøre for deg, og bli inspirert til å prøve det ut selv! 

Intro

Go ble utviklet hos Google av Robert Griesemer, Rob Pike og Ken Thompson, språket ble offisielt sluppet i 2009 som et open-source prosjekt.

Go er et relativt nytt språk, designet for å løse moderne problemer som koordinasjon mellom mange utviklere og flerkjernesystemer i stor skala.
Go er utviklet som et moderne alternativ til språk som C og Python. Disse ble utviklet på en tid hvor flerkjernesystemer ikke var utbredt. De var heller ikke i utgangspunktet utviklet for å skalere godt mot flere kjerner.
Go føles som et dynamisk språk slik som Python eller Ruby, men har ytelsen og sikkerheten til språk som Java og C. Formålet med Go er å binde sammen fordelene hos statiske og dynamiske språk og er spesielt fokusert mot skyen. Go kommer med et rikt standardbibliotek og har en modell for parallellitet som tillater utvikling av skalerbare applikasjoner med høy ytelse.

Hvem bruker Go?

Sitat CloudFlare:
We chose to use Go for Railgun because Railgun is inherently highly concurrent. A single instance of the Railgun client should be able to handle large numbers of requests from the CloudFlare data center for content and then multiplex them across an Internet connection to be handled. Go’s concurrency makes writing software that must scale up and down very easy.

Etter hvert er det blitt flere og flere aktører som har tatt i bruk Go. Google har blant annet benyttet Go for sin back-end infrastruktur bak dl.google.com – kilden for Chrome, Earth, Android SDK og andre store Google nedlastinger. I tillegg brukes det av flere store aktører:

BBC

Is Google Go ready for production use

Bitly

Go-Go Gadget NSQ:
Realtime Distributed message processing

CloudFlare

Go at CloudFlare

CloudFoundry

Cloud Foundry logging system

Docker

Why Docker was written in Go

Dropbox

Dropbox - Go Libraries

Github

Github: Key-value logs in Go

Google

dl.google.com - Backend infrastruktur
Vitess: Large-scale SQL installations

Heroku

Go at Heroku

New York Times

Elastic MapReduce in Go(EMR)

SoundCloud

Go at SoundCloud

Tumblr

GoCircuit - Dynamic cloud orchestration

Twitch

Twitch GroupChat

Fordeler med Go

Go kan ved første øyekast virke noe enkelt og tilby få muligheter sammenlignet med andre språk, men dette er fordi språket er designet slik:
Go skal ha få innebygde egenskaper, Go bygger ikke inn alt av muligheter bare fordi alle andre språk har disse – funksjonalitet må tjene et formål!

Et eksempel på dette er generics i Go. Go har et stort standardbibliotek, men har valgt å ikke implementere generics da utviklerne ikke har funnet en løsning som gir god nok verdi i forhold til kompleksiteten det innebærer å bygge det inn.
Det finnes riktignok mange eksempler på 3.-parts mellomvare som prøver å simulere dette og også gjenskaper mye av funksjonaliteten til generics. Et annet eksempel er at Go bare har èn type løkker, en slags kombinasjon av for og while.

De viktigste egenskapene kan oppsummeres slik:

- Samtidighet
Go har innebygd samtidighet(concurrency) gjennom Goroutines, som tillater parallellitet på en enklere måte enn andre språk. I tillegg til å ha samtidighet gjennom Goroutines har Go såkalte channels til å tillate både kommunikasjon og synkronisering.

- Open Source
Open source, men fortsatt backet av Google – forsvinner ikke med det første.
Kryssplattform Utviklet mot kryssplattform og kryssprosessor.

- Minnehåndtering
Go er et kompilert språk, men har også en runtime. Go håndterer minneallokering for deg, noe som er nærmere Java enn C++. Minne som brukes av variabler varer så lenge som variablene er referert, som normalt sett er scopet til en funksjon – dette muliggjøres fordi Go er garbage collected.

- Hurtighet
Go er raskt både i forhold til andre språk og når man snakker om kompileringstid. Man kan til og med skrive og kjøre goprogrammer direkte på weben på play.golang.org.

- Statisk typet
Go er statisk typet, noe som øker robustheten til koden siden feil kan oppdages under kompilering.

- Syntaks
Syntaksen og miljøet føles mer som et dynamisk språk - det er statisk typet, men støtter automatisk typeinferens. x := 0 vs int x = 0;

- Stort standardbibliotek
Go har et stort standardbibliotek – blant annet APIer for http-Server/Client, JSON, Unicode, Kryptografi og HTML templating motor. Go kan også skryte på seg å inkludere en fullstendig web-server som en del av sitt standardbibliotek.

- Utrulling
Go kompilerer til en statisk binærfil og krever ingen dynamiske avhengigheter, noe som letter utrulling av kode. Man kopierer rett og slett binærfilen over og behøver ikke noe virtuelt miljø eller lignende. Alt dette blir håndtert under kjøring.

- Dokumentasjon
Go har dokumentasjon som en standard feature, noe som gjør at utviklere kan dokumentere koden sin lettere og generere leselig data fra kommentarer i kildekoden.

- Pakkehåndtering
Siden Go kompilerer alt statisk trenger du ikke tenke på pakkehåndtering ved runtime. Dersom du ønsker å inkludere et bibliotek inn i Go koden din, kan du importere de via en url slik: import «github.com/google/go-github/github».

Go vet hvordan den skal slå opp og klone biblioteket. Dette fungerer også med Subversion og Mercurial!

Bruksområder

Go:

  • Kan brukes til utvkling av systemverktøy.
  • Utvikling av spillservere.
  • Kan prosessere mange ulike events samtidig og gjøre komplekse analyser av disse.
  • Som et generelt programmeringsspråk for å løse tekstprosessering, scripting, men også for å lage frontends.

Go kan derimot ikke brukes i «ekte» sanntidssystemer på grunn av at det er garbage collected og har automatisk minneallokasjon. Det vil riktignok fungere fint ved nær-sanntidssystemer, der noe forsinkelse kan tillattes, slik det ofte er på for eksempel live betting sider. CQRS og Event Sourcing er eksempler på slike systemer, og det finnes flere toolkits for dette med Go.

Go er også et utmerket språk til skytjenester nettopp grunnet språkets fokus på samtidighet og skalering. Eksempelvis der man I PHP og også Node.js er fanget i en enkelt tråd, har man i Go samtidighet med en hurtighet som kan måle seg med C og C++.
Go brukes av flere kjente aktører som Docker, Heroku, CloudFlare og CloudFoundry. Vitess er et annet prosjekt som er utviklet i Go, og er en fundamental del av YouTube sin MySQL infrastruktur – myntet på skalering av MySQL i skyen.

Setup

Go har støtte for de fleste plattformer som Windows, Mac og Linux, og kan lastes ned fra sine nettsider.

Når Go er installert får man tilgang til en rekke ulike kommandoer, som kjøres med følgende syntaks:

            go command [arguments]

build       compile packages and dependencies

clean       remove object files

env         print Go environment information

fix         run go tool fix on packages

fmt         run gofmt on package sources

generate    generate Go files by processing source

get         download and install packages and dependencies

install     compile and install packages and dependencies

list        list packages

run         compile and run Go program

test        test packages

tool        run specified go tool

version     print Go version

vet         run go tool vet on packages

Eksempelvis:
go get github.com/revel/cmd/revel - Installerer “Revel” pakken fra Github. Et web-rammeverk for Go.

Go kompilerer til en kjørbar binærfil som kan kjøres på plattformen programmet er kompilert til. Alle refererte biblioteker er statisk linket, slik at de blir pakket sammen med binærfilen. Dette gjør at Go ikke får noen dynamiske avhengigheter på serveren programmet skal kjøres på. Alt man trenger er inkludert!

Syntaks

Go syntaks er delvis inspirert av C og ser slik ut:

Dette programmet legger sammen to tall og skriver ut resultatet. fmt følger med Go og brukes til å skrive til konsollen. Det første man legger merke til er at typen til en variabel kommer etter navnet til variabelen. Krangler du mye om hvilken kodestil som skal følges? I Go har man ikke dette problemet, fordi go fmt skriver om koden du har skrevet til standardformatet til Go - Ingen flere krangler!

Go er også annerledes i måten variabler gjøres tilgjengelig innenfor samme pakke og variabler tilgjengelig utenfor. Der man i C# og Java har private og public har man i Go stor og liten forbokstav på variabler.

Stor forbokstav: Tilgjengelig utenfor pakken den er deklarert i.
Liten forbokstav: Tilgjengelig kun inne i pakken den er deklarert i.

Dermed er funksjonen calc over kun tilgjengelig i main pakken. Programmet over kan kjøres ved å lagre det som «calc.go» og kjøre kommandoen:
go calc.go

Structs & Interface

I Go opererer man ikke med klasser på samme måte som i C# eller Java. Go bruker structs som typedeklarasjon. Eksempelet under viser en type Animal i Go og en funksjon PrintName som kan kalles på Animal. Til slutt oppretter vi en instans av Animal med new funksjonen. Dette allokerer minne og returnerer en minneadresse til objektet.

Go har også interfaces, som er et sett med metoder en type må ha for å implementere et gitt interface. I språk som Java vil man ofte eksplisitt implementere et interface gjennom nøkkelord som implements . I Go er det slik at så lenge en type har de samme funksjonene som et interface har deklarert, så implementeres dette interfacet automatisk.

I eksempelet under ser vi et interface Predator som implementeres av både typen Cheetah og Lion(Felines).

Output: 

Las oss prøve et nytt eksempel: 

Golang ser her at Dog ikke har Growl() metoden implementert, og tilfredstiller derfor ikke signaturen til HuntAndTriumph().

Channels & go-routines

I Go angripes problemstillingen rundt parallellitet med såkalte go-routiner og kanaler. En go-routine er en lettvekts eksekveringstråd som kommuniserer med andre slike prosesser ved å sende delte data over en go-channel.

Do not communicate by sharing memory; instead, share memory by communicating.

På denne måten unngår man at en delt variabel blir aksessert av to prosesser samtidig, fordi det er kun èn go-routine som kan aksessere data til enhver tid. Dette minsker sannsynligheten for at deadlocks eller race conditions kan oppstå.

Hvordan opprette en go-routine:

Go-routines:

Vi kan starte så mange go-routiner som vi behøver, og Go vil multiplekse disse til det konfigurerte antall CPUer eller tråder når det trengs. Man starter da med en liten stack som kan øke og minke via dynamisk allokkering. Man starter en go-routine så enkelt som go func(x) hvor func(x) er en funksjon. Fjerner man nøkkelordet go foran vil funksjonen kjøre synkront som et vanlig funksjonskall.

Go-Channels:

Go-kanaler brukes for å sende og motta verdier til og fra Goroutines for prosessering. De er som standard ubufret, som vil si at de er synkrone og blokkerende. Kanalene kan også være bufret, noe som tillater å legge verdier inn i en kø for prosessering. Flere goroutines kan skrive og lese fra kanalene samtidig uten å behøve å bruke låser. Benytter man Go sin select statement kan man også lese fra flere kanaler samtidig.

Sitat CloudFlare’s Railgun:
Probably the nicest thing about goroutines and channels is that they make it easy to create 'fire and forget until needed' systems. You create a channel, create a goroutine that communicates on that channel and then read from the channel when needed (perhaps using a select statement).

Under er et eksempel på hvordan to rutiner skifter på å skrive annenhver gang:

Flyten er som følger:

  1. Vi starter med å definere en struct Paper.
  2. Vi oppretter en kanal workDesk som tar imot et objekt av typen Paper. Kanaler i Go er typet, som gjør at vi kan sende inn objekter.
  3. Vi starter to go-routines: go work med henholdsvis «bip» og «bop» som melding.

Funksjonen work tar imot en operasjonsmelding og kanalen workDesk. for-løkken kjører evig så lenge programmet ikke avsluttes. Vi venter først på at vi skal få en operasjon inn på kanalen workDesk som vi så lagrer i variabelen paper. Vi øker så antall linjer på papiret og skriver ut henholdsvis meldingen og antall linjer, før vi sender en melding tilbake på workDesk.

Begge disse go-rutinene vil starte med å vente på en melding på kanalen workDesk. Selve arbeidet starter vi i main ved å sende en melding inn på workDesk. Bip vil skrive sin melding på papiret først, øke antall linjer på papiret, for så å legge tilbake papiret på workDesk. Bop vil da få tilgang til å skrive sin melding på papiret. De vil så kjøre annenhver gang til det har gått 3 sekunder. Programmet vil da avsluttes.

Utskriften vil se slik ut etter endt gjennomkjøring:

Med Go trenger man ikke forholde seg til unødvendig kompleksitet rundt parallelle prosesser. Flere go-routines kan kjøres på samme tråd, og dersom en rutine blokkerer, vil den andre automatisk flyttes til en annen tråd. Resultatet er at utvikleren oppretter go-routines og Go tar seg av trådhåndteringen.

Go-REST

Avslutningsvis skal vi se på hvordan vi på kort tid kan lage REST-services med Go.
Først og fremst krever det at man har Go installert. Etter dette kjører vi kommandoen

run go get code.google.com/p/gorest – fra cmd-vinduet

Dette vil installere gorest-rammeverket.
Vi lager så vår helloWorld.go-fil med følgende innhold:

Vi starter med å importere gorest og net/http pakkene. gorest brukes for å lage vår REST-service og http inneholder implementasjoner for http-klient og server. Videre registrerer vi en ny service kalt HelloService. http.Handle tar seg av handlers til serveren. ListenAndServe lytter på tcp-nettverket på adressen vi sender inn, som er :9090 med en handler nil.

Neste steg er å definere servicen vår. Som vist tidligere har vi her definert en struct type.
Videre har vi definert flere fields:
gorest.RestService deklarerer en ny service og setter rot-urlen til å være intro. helloWorld og sayHey er gorest endpoints av typen GET. Begge gir en output av typen string, og kan nås via path url’en. Brukerne kan få tilgang til applikasjonen via disse endpointene.

Man kan selvsagt også benytte andre metoder som POST, PUT, DELETE osv.

Her har vi definert hva endpointene våre skal returnere, henholdsvis strengen «Hello World» og «Hey there» samt navnet vi har lagt til i URL’en og en velkomstmelding.

Vi kan nå kjøre vår helloWorld.go fil fra cmd-vinduet med go run helloWorld.go
Hvis vi så åpner et browservindu får vi ved /intro/hello-world:

Og ved /hey/{navn}

Og til slutt ved en URL som vi ikke har definert i koden vår /intro/thisdoesnotexist

Når man så har en tjeneste kan denne enkelt hostes på for eksempel Heroku eller Google App Engine.

Oppsummering

Vi har i denne bloggposten vist hvordan man installerer Go, kjører Go-programmer og eksempler på Go-kode samt en REST-service skrevet I Go.

Go er et morsomt språk å lære seg, og jeg anbefaler alle å ta en titt på hva det kan tilby! Mer detaljert informasjon finner du her:

Interaktiv “getting started” guide til go, rett I nettleseren.
Intro til Go skrevet av Caleb Doxsey
Offisiell hjemmeside med dokumentasjon og andre ressurser.

 

Om bloggeren:
Utvikler hos Acando i Trondheim. Problemløser med engasjement innen interaksjonsdesign, web og skyløsninger.

comments powered by Disqus