Code Days 2019, München: Vortrag von Johannes Weigend (@JohannesWeigend, Technischer Geschäftsführer bei QAware)
=== Dokument bitte herunterladen, falls unscharf! Please download slides if blurred! ===
Abstract:
Programmiersprachen für die Cloud - Java und Go im Vergleich
Java ist nach dem Tiobe Index 2018 unangefochten Platz 1 bei den weltweit eingesetzten Programmiersprachen. Java ist ausgereift, stabil und verfügt über ein immenses Open Source Ökosystem. Was will man mehr? Obwohl Java gerade für die Backend Entwicklung attraktiv ist, hat Google 2008 eine eigene Programmiersprache Open Source gestellt: Golang oder kurz Go. Der Vortrag beleuchtet die Stärken und Schwächen von Go gegenüber Java, gibt Hinweise für welche Projekte Go eine gute Alternative ist, und wie ein Best of Breed Ansatz aussehen kann. Interessant an Go ist, dass die Grundbausteine von Cloud Plattformen wie OpenShift oder die Google Container Plattform mit Go erstellt wurden. Docker, Kubernetes, Helm, Grafana oder Prometheus ‒ alles ist mit Go programmiert. Die Fragen aus der Sicht von Java-Experten sind: Was macht Go für die Cloud so interessant? Gibt es Funktionen, die Java Programmierer kennen sollten, und wenn ja, welche?
Aus blau wird grün! Ansätze und Technologien für nachhaltige Kubernetes-Cluster
Java und Go im Vergleich
1. JAVA UND GO IM VERGLEICH
PROGRAMMIERSPRACHEN FÜR DIE CLOUD
JOHANNES WEIGEND | @JOHANNESWEIGEND | QAWARE GMBH | @QAWARE
2.
3. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
AGENDA
▸ Warum Go? / Go im Überblick!
▸ Stärken von Java
▸ Stärken von Go
▸ Go vs. Java an Beispielen
▸ Go Konzepte, die ein Java Programmierer kennen sollte
▸ Best of Breed - Für welche Programme macht Go Sinn?
4. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
WARUM GO?
▸ Go wurde als C++ Alternative zur systemnahen Programmierung entwickelt
▸ Designziele
• Einfachheit
• Schnelle Kompilierung
• Automatische und sichere Speicherverwaltung
• Laufzeitunterstützung
• Unterstützung von Nebenläufigkeit und Parallelität
• Einfache Anbindung von C/C++ Code
• Sprachfeatures aus modernen, dynamischen Sprachen
▸ Systemnahes Programmieren soll Spaß machen!
5. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
GO IST DIE WESENTLICHE SPRACHE IM CLOUD NATIVE STACK
▸ Alle wesentlichen Komponenten sind in Go geschrieben
• Container Platform: (Docker), rkt, ContainerD
• Cluster Orchestrator: Kubernetes, OpenShift
• Cross Cutting: Prometheus, Jaeger, …
6. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
APPLICATIONS
CLOUD PLATFORM
OS
GO
C/C++
JAVA
EINSATZGEBIETE VON JAVA UND GO - DER SWEET SPOT
ANALYTICS
WEBAPPS
MICROSERVICES
UTILITIES
CROSS CUTTING
DEVOPS
AI
SCALING
SECURITY
RESOURCEMGMT
DRIVER
OS EXTENSION
7. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
STÄRKEN VON JAVA
▸ Verbreitungsgrad
▸ Community
▸ Standards (JavaEE …)
▸ Open Source
▸ Open Source Ökosystem
▸ Multi Purpose - Multi Platform
▸ Tools
13 Mio JARs auf Maven Central
Platz 1 im TIOBE Index
8. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
WARUM VERWENDEN CLOUD PLATTFORMEN GO? (SCHWÄCHEN VON JAVA)
▸ Memory und CPU
• Java Anwendungen haben einen hohen Grundbedarf an Ressourcen (RAM, CPU - lange Startzeit
verursacht durch Class Loading und Classpath Scanning (Spring/CDI)).
▸ Container
• Java Anwendungen benötigen eine JVM. Aufgrund der JVM Abhängigkeiten enthalten viele
Container ein komplettes OS Filesystem Image.
▸ Footprint
• Ein Java Werkzeug benötigt aufgrund transitiver Abhängigkeiten schnell > 100 MB (z.B. Hadoop
Client). Aufgrund der dynamischen Class Loader Architektur kann erst zur Laufzeit entschieden
werden, welche Klassen wirklich benötigt werden. Oft ist das nur ein kleiner Prozentsatz des
vorhandenen Codes.
9. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
WARUM VERWENDEN CLOUD PLATTFORMEN GO? (SCHWÄCHEN VON JAVA)
▸ Concurrency
▸ Parallelprogrammierung mit Java basiert auf Threads und Synchronisation (Mutex/Lock). Erst mit Java7
kamen Erweiterungen für asynchrones Programmieren (CompletableFuture)
▸ Threads sind schwergewichtige Betriebssystem Ressourcen. Ein Thread belegt ca. 1 MB an Ressourcen.
▸ ⇨ Thread Pools sind hier keine Lösung, da langlaufende Aktionen zur Blockade führen können, falls
mehr Anfragen bearbeitet werden müssen als Threads im Pool zur Verfügung stehen.
▸ Komplexität
▸ Java ist einfach gestartet und hat sich über die Jahre zu einer sehr komplexen Programmiersprache
entwickelt. Wenige Programmierer überblicken alle Sprachmittel im JDK und bei JavaEE
▸ Es macht Sinn sich Go genauer anzusehen!
11. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
OOP IN JAVA - JAVA KENNT CODE NUR INNERHALB VON KLASSEN
// Rational represents a rational number numerator/denominator.
public class Rational {
final private int numerator;
final private int denominator;
public Rational(int numerator, int denominator) {
if (denominator == 0) {
throw new RuntimeException("division by zero");
}
int divisor = gcd(numerator, denominator);
this.numerator = numerator / divisor;
this.denominator = denominator / divisor;
}
public Rational Multiply(Rational y) {
return new Rational(numerator * y.numerator, denominator * y.denominator)
}
...
}
12. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
GO KENNT KEINE KLASSEN UND TRENNT DATEN UND FUNKTIONEN!
// Rational represents a rational number numerator/denominator.
type Rational struct {
numerator int
denominator int
}
// Multiply method for rational numbers (x1/x2 * y1/y2)
func (x Rational) Multiply(y Rational) Rational {
return NewRational(x.numerator*y.numerator, x.denominator*y.denominator)
}
// NewRational constructor function
func NewRational(numerator int, denominator int) Rational {
if denominator == 0 {
panic("division by zero")
}
r := Rational{}
divisor := gcd(numerator, denominator)
r.numerator = numerator / divisor
r.denominator = denominator / divisor
return r
}
15. // Page contains an array of words.
type Page []string
// Book is an array of pages.
type Book []Page
// Index contains a list of pages for each word in a book.
type Index map[string][]int
// MakeIndex generates an index structure
func MakeIndex(book Book) Index {
idx := make(Index)
for i, page := range book {
for _, word := range page {
pages := idx[word]
idx[word] = append(pages, i)
}
}
return idx
}
16. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
WARUM KEINE KLASSEN?
▸ Weniger Code
▸ Geringere Schachtelung
▸ Keine No-Op Anweisungen „public static void“
▸ Freiheitsgrade bei der Implementierung von Interfaces
▸ Embedding (=Aggregation) statt Vererbung
▸ Interfaces statt Klassenhierarchien
17. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
GO KENNT POINTER
▸ Pointer werden wie auch Werte per Kopie übergeben
▸ Es gibt keine Pointer Arithmetik (p++) wie in C/C++
▸ Pointer sind automatisch mit nil initialisiert
func swap1(x, y int) {
x, y = y, x
}
func swap2(x *int, y *int) {
*x, *y = *y, *x
}
func swap3(x **int, y **int) {
*x, *y = *y, *x
}
18. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
GO KENNT POINTER
func main() {
var a, b = 1, 2
fmt.Printf("a=%d, b=%dn", a, b)
swap1(a, b)
fmt.Printf("swap1(a,b) : a=%d, b=%dn", a, b)
swap2(&a, &b)
fmt.Printf("swap2(&a,&b) : a=%d, b=%dn", a, b)
pa, pb := &a, &b
swap3(&pa, &pb)
fmt.Printf("swap3(&pa, &pb): a=%d, b=%dn", a, b)
}
a=1, b=2
swap1(a,b) : a=1, b=2
swap2(&a,&b) : a=2, b=1
swap3(&pa, &pb): a=2, b=1
Program exited.
19. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
GO KENNT POINTER - WARUM?
▸ Einfache Anbindung von C-Code ohne Adapter (Java JNI)
▸ Unterscheidung zwischen Pointer und Value
▸ Kontrolle über das Speicherlayout von Strukturen (Java: Project Valhalla)
▸ Call by Value und Call by Reference für komplexe Datentypen
type Point struct { x, y, z int}
type Triangle struct {
a, b, c Point
}
type Triangles [] Triangle // flach !!!
21. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
INTERFACES LIEFERN POLYMORPHISMUS
▸ Alle Typen in Go implementieren das leere Interface {}
// Stack is a generic LIFO container for untyped objects.
type Stack struct {
data []interface{}
}
// NewStack constructs an empty stack.
func NewStack() *Stack {
return new(Stack)
}
// Push pushes a value on the stack.
func (s *Stack) Push(value interface{}) {
s.data = append(s.data, value)
}
22. // Node is the interface to eval an abstract syntax tree (AST)
type Node interface {
// Eval evaluates the AST. The variables of the expression are set to true or false in the vars map.
// Missing vars (there are no key in the map) are evaluated to false.
Eval(vars map[string]bool) bool
}
// Or is the logical OR Operator in an AST
type Or struct {
lhs Node
rhs Node
}
// Eval implements the Node interface
func (o Or) Eval(vars map[string]bool) bool {
return o.lhs.Eval(vars) || o.rhs.Eval(vars)
}
PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
INTERFACES AND POLYMORPHISM: ABSTRACT SYNTAX TREE
// And is the logical AND Operator in an AST
type And struct {
lhs Node
rhs Node
}
// Eval implements the Node interface
func (a And) Eval(vars map[string]bool) bool {
return a.rhs.Eval(vars) && a.rhs.Eval(vars)
}
24. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
GO ROUTINEN SIND KEINE THREADS!
▸ Eine Go Routine ist eine nebenläufige Funktion im gleichen Adressraum
▸ Go verwendet einen internen Thread Pool für die Ausführung
▸ Die Go Routinen laufen abschnittsweise auf den Threads
▸ Go Routinen werden kooperativ unterbrochen (z.B. bei fmt.Println()) -> Kooperatives
Multitasking
f("hello", "world") // f runs; we wait
go f("hello", "world") // f starts running
g() // does not wait for f to return
25. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
CHANNELS ERMÖGLICHEN SICHERE SYNCHRONISATION ZWISCHEN GO ROUTINEN
▸ Erzeugen
▸ Senden
▸ Empfangen
c1 := make(chan int) // buffer size = 0
c2 := make(chan int, 10) // buffer size = 10
c1 <- 1 // blocks until receiver reads
c2 <- 2 // blocks when buffer is full
x = <- c1 // blocks until sender sends
y = <- c2 // blocks when buffer is empty
26. SYNCHRONISATION ÜBER CHANNELS - PING / PONG
PING PONG// Ball contains the number of hits.
type Ball struct{ hits int }
func main() {
table := make(chan *Ball)
go player("ping", table)
go player("pong", table)
table <- new(Ball) // game on; toss the ball
time.Sleep(1 * time.Second)
<-table // game over; grab the ball
}
func player(name string, table chan *Ball) {
for {
ball := <-table
ball.hits++
fmt.Println(name, ball.hits)
time.Sleep(100 * time.Millisecond)
table <- ball
}
}
28. ANALOG ZU JAVA KENNT GO LOW LEVEL LOCKS
// BlockingQueue is a FIFO container with a fixed capacity.
// It blocks a reader when it is empty and a writer when it is full.
type BlockingQueue struct {
m sync.Mutex
c sync.Cond
data []interface{}
capacity int
}
// Put puts an item in the queue and blocks it the queue is full.
func (q *BlockingQueue) Put(item interface{}) {
q.c.L.Lock()
defer q.c.L.Unlock()
for q.isFull() {
q.c.Wait()
}
q.data = append(q.data, item)
q.c.Signal()
}
29. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
ZUSAMMENFASSUNG
▸ Go Routinen sind eine Abstraktion über Threads und skalieren sehr gut
▸ Mit Go Channels können die meisten Synchronisationsprobleme sehr elegant
gelöst werden
▸ Channels basieren auf Message Passing anstelle von Blockaden
▸ Go hat auch eine Low Level Api mit Locks für klassische
Synchronisationsprobleme
▸ Klassen aus java.util.concurrent können damit einfach portiert werden
31. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
EIN EINFACHER RESTFUL SERVICE
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
)
func main() {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/customer/{customerId}", getCustomerByID)
router.HandleFunc("/customer", getAllCustomers)
log.Println("CustomerServer: Listening on http://localhost:8080/customer ...")
log.Fatal(http.ListenAndServe(":8080", router))
}
32. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
EIN EINFACHER RESTFUL SERVICE
// Customer type
type Customer struct {
Name string `json:"name"`
Address string `json:"address"`
Tel string `json:"telephone"`
}
// Simple static customer data.
var customers = []Customer{
Customer{„Ken Thompson“, „US", "+11 170 12345678"},
Customer{„Rob Pike", "US", "+11 169 87654321"},
}
func getAllCustomers(w http.ResponseWriter, r *http.Request) {
if err := json.NewEncoder(w).Encode(customers); err != nil {
panic(err)
}
}
33. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
DOCKERFILE / DAS BUILDER PATTERN
▸ Der Container "builder" enthält die komplette Go Compiler Suite + Tools
▸ Der generierte Container enthält ausschließlich das von Go generierte Programm
▸ Das Programm ist statisch gelinkt und hat keine weiteren Abhängigkeiten
▸ Der finale Container ist nur wenige MB groß!!!
# Build
FROM golang:alpine as builder
RUN mkdir /build
ADD . /build/
WORKDIR /build
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o main .
# Repackage
FROM scratch
COPY --from=builder /build/main /app/
WORKDIR /app
CMD ["./main"]
35. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
DER GO WERKZEUGZEUGKASTEN GEHT ÜBER JAVA HINAUS
▸ Compile, Build, Run von Anwendungen aus vielen Packages / Quelldateien ohne
Buildskript mit „go build / go run“
▸ Unit Testing mit Test Coverage „go test / go tool cover“
▸ Profiling mit pprof (CPU und Memory) -> Flamegraphs (Brendan Gegg)
▸ Formatierung von Quelldateien mit „go fmt“
▸ Quellcode Style Checker mit „go vet“
▸ Slideshows mit ausführbarem Go Code im Browser mit „present“
37. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
FÜR WELCHE ANWENDUNGEN EIGNET SICH GO PERFEKT?
▸ Hilfsprozesse wie Sidecar Container auf Cloud Plattformen (Kubernetes,
OpenShift)
▸ Systemnahe Infrastrukturdienste wie Reverse Proxies, Authentication Proxies
▸ Automationsfunktionalität in der Cloud wie Elastic Scaling Automation
▸ Aggregator für Microservices (z.B. Backend for Frontend, gRPC auf RESTful
Adapter)
▸ Querschnitts-Dienste (Crosscutting) wie Monitoring, Logging …
38. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
FÜR WELCHE ANWENDUNGEN SOLLTE MAN EHER JAVA WÄHLEN?
▸ Anwendungen mit komplexen Schnittstellen (legacy, proprietary)
▸ Enterprise Anwendungen mit langer Lebensdauer (Standards !)
▸ Anwendungen mit einem hohen Wiederverwendungspotential durch
vorhandene Open Source Projekte
▸ Anwendungen mit engen Schnittstellen zu anderen Java Anwendungen
39. PROGRAMMIERSPRACHEN FÜR DIE CLOUD - JAVA UND GO IM VERGLEICH
DIE GRAUZONE HÄNGT VON DEN ENTWICKLERN UND STRATEGIE AB
▸ Entwickler, die bisher C++ und Java programmiert haben, werden Go mögen
und schnell produktiv sein
▸ Entwickler, die bisher nur Java programmiert haben, sollten mit Go eher
vorsichtig sein
▸ Firmen, die sich stark in Richtung Cloud Nativer Entwicklung orientieren,
sollten mit Go mutiger sein und Go auch für Teile der Anwendungsentwicklung
nutzen
?
41. WAS SOLLTEN JAVA PROGRAMMIERER VON GO LERNEN?
▸ Message Passing ist mit Java auch ohne native Channels und Go Routinen möglich.
Hier kann man von Go und der Einfachheit der Nutzung lernen.
▸ KISS ist relevant. In Java ist es inzwischen möglich ähnlich komplexen Code zu
schreiben wie früher in C++. Guter Java Code beschränkt sich auf das wesentliche. Es
ist nicht zielführend jedes Java Feature in eine Anwendung einzubauen!
▸ Semantik Versioning und Minimal Version Detection sind Konzepte, die sich auch mit
Java Tools umsetzen lassen.
▸ Es lassen sich mit ein wenig Erfahrung auch kleine, cloudfähige Java Container
bauen. Hier kann man sich an Go orientieren.
▸ Man sollte sich im Projekt überlegen, was man aus Java alles weglassen kann ohne
an Produktivität und Spaß zu verlieren.
42. PERFEKTION
IST NICHT DANN ERREICHT, WENN MAN NICHTS
MEHR HINZUFÜGEN, SONDERN WENN MAN NICHTS
MEHR WEGLASSEN KANN Antoine de Saint-Exupéry
43. CODEDAYS 2019
JOHANNES WEIGEND / QAWARE GMBH
@JOHANNESWEIGEND
@QAWARE
SIEHE AUCH
HTTPS://WWW.QAWARE.DE/FILEADMIN/USER_UPLOAD/SONDERDRUCK-JAVASPEKTRUM-05-2018-JAVA-VS-GO-WEB.PDF
WEITERE QAWARE TALKS
MITTWOCH, 23.01.2019 | 09:55 - 10:40 UHR KMI 4.2 | CLOUD COMPUTING CRON IN DER CLOUD - DIE TOP 10 HITPARADE | ALEX KRAUSE
MITTWOCH, 23.01.2019 | 11:00 - 11:45 UHR MI 4.2 (OOP) | SCHNELLIGKEIT UND AUSDAUER IN SOFTWARE-PROJEKTEN | MICHAEL ROHLEDER