SlideShare una empresa de Scribd logo
1 de 18
Descargar para leer sin conexión
#golang @SkrzCzDev
Skrz DEV Cirkus
21.10.2015
V článku na Zdrojáku “Jak Skrz.cz řadí 20K nabídek podle real-time
analytiky” (https://www.zdrojak.cz/clanky/jak-skrz-cz-radi-20k-nabidek-podle-
real-time-analytiky/) jsem psal, že řazení probíhá v microservice napsané v Go.

Před ranking service ale vzniknul “adbandit”, service, která se stará o
automatickou optimalizaci nabídek v bannerech.
Skrz používá bannery od Seznamu pro promo nabídek (např. na Novinky.cz,
Proženy.cz). Nejdříve se dělal XML export položek, které se zobrazily v bannerech.
Problém však byl, že člověk viděl X krát jednu a tu samou položku a nedokázali jsme
položku, na kterou lidé neklikají, rychle stáhnout (XML si Seznam stahoval každých 10
minut).

Dospěli jsme tedy k řešení přes IFRAME a servírování banneru přímo z našich serverů.
Nejdříve to byl statický soubor, který jeden skript několikrát do minuty přegeneroval.
Ale chtěli jsme ještě dynamičtěji reagovat, a tak vzniknul “adserver” - server v
ReactPHP.

V průměru adserver obsluhuje 100-200 req/s, v peaku 500-1000 req/s a to s latencí
~50ms a velmi malými nároky na server (zabere třeba 4 jádra, 2GB RAM)
machine learning
statistika
http://www.mlguru.cz/bayesovsky-bandita-chytrejsi-a-levnejsi-ab-testovani/
Problém, který jsme řešili byl následující: Máme X ploch, Y pozic na plochu a Z reklam
(nabídek), které se mohou na pozicích zobrazovat. Chceme maximalizovat počet prokliků.

Nejdříve jsme reklamy vybíraly náhodně. Fungovalo to, ale ROI nebyla taková, jakou bychom
chtěli. Samozřejmě možnost je řadit podle CTR (click-through rate - poměr prokliků vůči počtu
impresí). Problém je ale např. s novými reklamami - nemají prokliky ani imprese, nelze určit
CTR. Jak moc by se měly takové reklamy zobrazovat? Kolik jim dát prostoru?

Narazil jsem na článek od Jirky Materny “Bayesovský bandita: chytřejší a levnější A/B
testování”. Neříkal bych tomu úplně “machine learning”, je to spíše chytřejší použití statistiky.
Nicméně výsledkem je online učící se algoritmus.
😰
Pro pochopení, co je Beta rozdělení a
proč dobře modeluje problém, který
jsme řešili, doporučji projít odkazovaný
článek.
PHP?
Pak ale přišlo v čem to napsat. Potřebujeme v každém požadavku najít pro každou
reklamu hodnotu náhodné veličiny odpovídající jejímu Beta rozdělení. V peaku 1000
req/s, maximálně řekněme 100 reklam, tzn. potřebujeme Beta rozdělení spočíst 100-
tisickrát každou sekundu. Kompilovaná implementace Beta rozdělení to dokáže na
single-core milionkrát za sekundu, takže to jde.

Pro PHP existuje PECL balíček stats. Ale ten má poslední aktualizaci 2012 a nejsou
pro něj Debianí balíčky. Tedy pro deployment na produkci je to “no-no”. PHP
implementace Beta rozdělení, pokud bude jen 100 krát pomalejší, nebude stíhat. 

Jaké jsou tedy další možnosti?
C? 💀 Java? 💩
Céčko by to mohlo stíhat. Jenže v něm nikdo neumí,
balíčkovací systém, kompilace, deloyment - hodně
složité.

Java je jednoduchá. Hodně lidí v ní umí, ale nikomu se
v tom dělat nechce :) A opět je tu kompilace a
deployment…
Go! 👍
roku 2009 jsem napsal 1. český tutorial 😛
http://programujte.com/clanek/2009112200-go-novy-
programovaci-jazyk-od-google/
(většina problémů, co se tam píše, už naštěstí neplatí 😊)
Go jsme znal, hodně jednoduchý jazyk, kdokoli se do něj
dokáže dostat během chvále, hodí se na psaní serverů.
Deployment je prostě nahrání jedné statické binárky na
server.

Má knihovny na komunikaci po síti a našel jsem
knihovnu na počítání mimo jiného Beta rozdělení (https://
code.google.com/p/gostat/). Takže Go!

Projdu na následujících reálné ukázky kódu adbandity,
jak vypadá Go.
func NewServingService(...) *ServingService {
service := &ServingService{
cfg: cfg,
dbmap: dbmap,
connection: connection,
doneChan: make(chan interface{}),
// ...
}
service.Start()
return service
}
Adbandit běží vedle Adserveru a komunikují spolu po síti.
Nejlepší by bylo, kdyby se Adserver přepsal celý do Go, ale
obsahoval už hodně logiky kolem výběru nabídek, byly v něm
připravené šablony pro bannery apod.

Adbandit se tedy skládá z několika service. `ServiceService`
se stará o vyřizování requestů na bannery. Adserver přijme
request, rozhodne, jestli má jít na Adbandit, a pokud ano,
přepošle na Adbandit, ten vrátí v JSONu položky, které má
Adserver zobrazit. Ten je hodí do šablony, dotáhne další
potřebné data a vyrenderuje.

Takhle se dělají v Go “konstruktory” - vytvoří se funkce
(uvozená “func”) s prefixem “New”. Metoda “Start” spustí v
service vše potřebné.
// Config - struct defining bandit config file
type Config struct {
Database Database
Rabbitmq Rabbitmq
HTTP HTTP
MutationsFile string `yaml:"mutations_file"`
// ...
}
// Database - SQL database configuration
type Database struct {
Driver string
Host string
Port int
User string
Password string
Database string
}
// Rabbitmq - RabbitMQ configuration
type Rabbitmq struct {
Host string
Port int
User string
Password string
Vhost string
RPCQueue string `yaml:"rpc_queue"`
}
// HTTP configuration
type HTTP struct {
Host string
Port int
HitDomain string `yaml:"hit_domain"`
}
Proměnná “cfg” na předchozím slajdu byla struktura typu
“Config”. Všimněte si, že Go nejdříve píše název field
struktury (proměnné) a poté typ (narozdíl např. od C/Java).
Taky zde není nic jako `public`/`private`. Go to rozlišuje
podle toho, jestli je první písmeno velké (public), nebo
malé (private).

Za typem field jde uvést ještě tzv. “struct tag”. Používají ho
různé serializační knihovny, např. zde YAML.

Hezké zarovnání za vás vyřeší “go fmt”. Obecně se
nemusíte v týmu hádat o coding style, všechny Go
zdrojáky mají jeden - jak vám to naformátuje “go fmt”.
//
// start HTTP server
//
service.server = &http.Server{
Addr: fmt.Sprintf("%s:%d",
service.cfg.HTTP.Host,
service.cfg.HTTP.Port,
),
Handler: http.HandlerFunc(service.handleHTTPRequest),
}
go func() {
if err := service.server.ListenAndServe(); err != nil {
panic(err)
}
}() Ve standardní knihovně je implementace HTTP serveru -
balíček `net/http`.

Před jakékoli volání funkce jde napsat `go`. To vytvoří tzv
“goroutine” - kus kódu, který se začne vykonávat
konkurenčně se současným. Tady v goroutine spustíme na
HTTP serveru metodu `ListenAndServe` - ta začne
poslouchat na socketu a vyřizovat requesty daným
handlerem.
//
// start RPC server
//
for i := 0; i < runtime.NumCPU(); i++ {
channel, err := service.connection.Channel()
if err != nil {
log.Panicf("could not create channel: %sn", err)
}
requestDelivery, err := channel.Consume(...)
if err != nil {
log.Panicf("could not start consumer: %sn", err)
}
go func(channel *amqp.Channel, requestDelivery <-chan amqp.Delivery, i int) {
for {
select {
case delivery := <-requestDelivery:
service.handleRequestDelivery(channel, delivery)
case <-service.doneChan:
if err := channel.Close(); err != nil {
log.Panicf("could not close channel: %sn", err)
}
break
}
}
}(channel, requestDelivery, i)
}
HTTP bohužel nestíhalo vyřizovat requesty, hlavně na
straně Adserveru - procesy umíraly na moc otevřených file
descriptorů.

Rozhodl jsem se zkusit nejjednodušší řešení - místo HTTP
jako transportního prokotolu jsem použil RPC přes
RabbitMQ (http://www.rabbitmq.com/tutorials/tutorial-six-
go.html). Na straně PHP přes async knihovnu BunnyPHP
(https://github.com/jakubkulhan/bunny).
func (service *ServingService) handleRequestDelivery(
channel *amqp.Channel,
delivery amqp.Delivery
) {
req := mq.ParseAdbanditRequest(delivery.Body)
buf := bytes.NewBuffer(nil)
service.handleAdbanditRequest(
buf,
int(req.BannerID),
req.Count,
req.UID,
false,
)
channel.Publish("", delivery.ReplyTo, false, false, amqp.Publishing{
CorrelationId: delivery.CorrelationId,
ContentType: "application/json",
Body: buf.Bytes(),
})
delivery.Ack(false)
} Ukázka, jak vypadá RPC přes RabbitMQ. `delivery.Body`
je request v JSON formátu. `mq.ParseAdbanditRequest`
pomocí standardní knihovny a “struct tagů” vyparsuje
data requestu.
txDeliveryChan, err := service.channel.Consume(...)
if err != nil {
panic(err)
}
service.txDeliveryChan = txDeliveryChan
go func() {
for {
select {
case delivery := <-service.actionDeliveryChan:
service.handleActionDelivery(delivery)
case delivery := <-service.txDeliveryChan:
service.handleTxDelivery(delivery)
case <-service.ticker.C:
service.tick()
case <-service.doneChan:
log.Print("ServingService done")
break
}
}
}()
Aby se mohly updatovat alfa a beta parametry Beta rozdělení,
Adbandit čte z RabbitMQ ještě všechna data o akcích uživatelů -
tzn. klicích na reklamy, impresím na reklamy.

Go poskytuje krásný “select” statement, který blokuje, dokud
nepřijde nějaká zpráva a podle toho začne vykonávat danou
větech.
#AwesomeGo
• výkon!
• built-in concurrency přímo v jazyku
• žádný callback/promise-hell jako v JS
• skvělá standardní knihovna, dobré 3rd party
• RabbitMQ knihovna:https://github.com/streadway/amqp
• database/sql: http://go-database-sql.org/
• mysql driver: https://github.com/go-sql-driver/mysql
• YAML: http://gopkg.in/yaml.v2
• CLI: https://github.com/codegangsta/cli
• go fmt; go test; a vůbec go tool
• rychlá kompilace, jednoduchá cross-kompilace
• env GOOS=linux GOARCH=amd64 go build .
Go se osvědčilo. Výkon o moc lepší být už
nemůže (je to zkompilované do binárkky),
poskytuje skvělou standardní knihovnu,
dobré 3rd-party knihovny a super tooling,
např.:

a) `go fmt` pro formátování kódu - neřešíte
v týmu coding style

b) `go test` pro unit testy a benchmarky,
nemusíte instalovat zvlášť nějaký
`goUnit`.

c) `go` příkaz umí i další věci (např. detekci
race conditions), zatím jsem však
nepotřeboval.
#NotSoAwesomeGo
• IDE?
• Atom? plugin do IntelliJ IDEA / PhpStorm?
• $GOPATH
• package manager / vendoring
• http://getgb.io/
• https://github.com/Masterminds/glide
• řešení $GO15VENDOREXPERIMENT?
• DI container
• https://github.com/facebookgo/inject
• https://github.com/karlkfi/inject
• https://github.com/peter-edge/go-inject
Věc, co mě nejvíc štve, je `$GOPATH` - všechno
se instaluje globálně, nelze pořádně řešit verze
závislostí. Existují různé 3rd-party package
managery pro Go. V Go 1.5 přibyla podpora pro
`vendor`, taková lokální `$GOPATH`, něco jako
`node_modules`. Package managery (např. Glide)
to již začaly využívat. Doufám, že co nejdříve
přidají to, co dělá Glide přímo do `go` toolu.

Taky mi chybí pořádný DI container. Z vypsaných
knihoven mi ani jedna napřijde, že by to řešila
dobře.
#GoNext
• Concurrency patterns:

https://talks.golang.org/2012/concurrency.slide#1
• Pipelines and cancellation:

https://blog.golang.org/pipelines
• Webové aplikace s net/http:

https://golang.org/doc/articles/wiki/
• gRPC:

http://www.grpc.io/
• Seznam dobrých knihoven:

http://awesome-go.com/
Odkazy, které doporučuji projít.

gRPC je RPC framework od Google.
Pokud bych měl znovu řešit propojení
Adserver/Adbandit, sáhl bych právě pro
gRPC. Ve Skrzu je použito právě pro
ranking service.
#GoInTheWild
• Parse

http://blog.parse.com/learn/how-we-moved-our-api-from-ruby-to-go-and-saved-our-sanity/
• Docker

https://www.docker.com/
• dl.google.com

http://talks.golang.org/2013/oscon-dl.slide#1
• VividCortex (monitoring výkonu databází)

https://www.vividcortex.com/
• CockroachDB (open-source DB ~ Google Spanner)

https://github.com/cockroachdb
• Mattermost (open-source on-premise kopie Slack)

https://github.com/mattermost/platform
• Bleve (full-text search)

https://github.com/blevesearch/bleve
• Prometheus (monitoring, time-series databáze)

http://prometheus.io/
• InfluxDB (time-series databáze)

https://influxdb.com/
Zajímavé projekty, které používají Go.
Díky!
Otázky?
Chcete se o Go dozvědět víc? 

Přijďte na GoLang Meetup 12.11.2015

http://srazy.info/golang-meetup/5676

Más contenido relacionado

Similar a #golang @SkrzCzDev (Skrz DEV Cirkus 21.10.2015)

Optimalizace Symfony na devu
 Optimalizace Symfony na devu Optimalizace Symfony na devu
Optimalizace Symfony na devuVašek Purchart
 
Vašek Purchart - Optimalizace Symfony na devu (2. sraz přátel Symfony v Praze)
Vašek Purchart - Optimalizace Symfony na devu (2. sraz přátel Symfony v Praze)Vašek Purchart - Optimalizace Symfony na devu (2. sraz přátel Symfony v Praze)
Vašek Purchart - Optimalizace Symfony na devu (2. sraz přátel Symfony v Praze)Péhápkaři
 
RocDevs/PHPPrague - Proč by sakra někdo měl chtít dělat eshop?
RocDevs/PHPPrague - Proč by sakra někdo měl chtít dělat eshop?RocDevs/PHPPrague - Proč by sakra někdo měl chtít dělat eshop?
RocDevs/PHPPrague - Proč by sakra někdo měl chtít dělat eshop?Tomáš Strejček
 
Jak zlepšit zabezpečení čtvrtiny celého webu
Jak zlepšit zabezpečení čtvrtiny celého webuJak zlepšit zabezpečení čtvrtiny celého webu
Jak zlepšit zabezpečení čtvrtiny celého webuMichal Špaček
 
20110511 Vývoj software - produktivně, efektivně, kvalitně
20110511 Vývoj software - produktivně, efektivně, kvalitně20110511 Vývoj software - produktivně, efektivně, kvalitně
20110511 Vývoj software - produktivně, efektivně, kvalitněJiří Mareš
 
moderni webapp - frontend
moderni webapp - frontendmoderni webapp - frontend
moderni webapp - frontendTomas Hodbod
 
Víceúrovňová obrana vysvětlená na Cross-Site Scriptingu
Víceúrovňová obrana vysvětlená na Cross-Site ScriptinguVíceúrovňová obrana vysvětlená na Cross-Site Scriptingu
Víceúrovňová obrana vysvětlená na Cross-Site ScriptinguMichal Špaček
 
2009 X33EJA Moderní Technologie Pro Vývoj JEE
2009 X33EJA Moderní Technologie Pro Vývoj JEE2009 X33EJA Moderní Technologie Pro Vývoj JEE
2009 X33EJA Moderní Technologie Pro Vývoj JEEMartin Ptáček
 
PoSobota 96 ČB 28.4.2018
PoSobota 96 ČB 28.4.2018PoSobota 96 ČB 28.4.2018
PoSobota 96 ČB 28.4.2018Brilo Team
 
Opensource ve veřejné správě
Opensource ve veřejné správěOpensource ve veřejné správě
Opensource ve veřejné správěOndřej Profant
 
Bezpečnostní útoky na webové aplikace
Bezpečnostní útoky na webové aplikaceBezpečnostní útoky na webové aplikace
Bezpečnostní útoky na webové aplikaceMichal Špaček
 
Deployment PHP aplikací | WebExpo 2011
Deployment PHP aplikací | WebExpo 2011Deployment PHP aplikací | WebExpo 2011
Deployment PHP aplikací | WebExpo 2011Jan Mittner
 
Bezpečnostní útoky na webové aplikace, Čtvrtkon 5
Bezpečnostní útoky na webové aplikace, Čtvrtkon 5Bezpečnostní útoky na webové aplikace, Čtvrtkon 5
Bezpečnostní útoky na webové aplikace, Čtvrtkon 5Michal Špaček
 
SEO Restart 2023: Lukáš Kostka - AI a R studio – optimalizace meta tagů na ,,...
SEO Restart 2023: Lukáš Kostka - AI a R studio – optimalizace meta tagů na ,,...SEO Restart 2023: Lukáš Kostka - AI a R studio – optimalizace meta tagů na ,,...
SEO Restart 2023: Lukáš Kostka - AI a R studio – optimalizace meta tagů na ,,...Taste
 
Hobby Developer 3.0: Tipy a triky pro web
Hobby Developer 3.0: Tipy a triky pro webHobby Developer 3.0: Tipy a triky pro web
Hobby Developer 3.0: Tipy a triky pro webTomáš Muchka
 
Co sledovat a jak měřit u mobilního webu
Co sledovat a jak měřit u mobilního webuCo sledovat a jak měřit u mobilního webu
Co sledovat a jak měřit u mobilního webuAkce Dobrého webu
 
Rockaway AWS Hackaton – Kick-off Meeting Brno
Rockaway AWS Hackaton – Kick-off Meeting BrnoRockaway AWS Hackaton – Kick-off Meeting Brno
Rockaway AWS Hackaton – Kick-off Meeting BrnoRockawayCapital
 
Continuous Integration
Continuous IntegrationContinuous Integration
Continuous Integrationdanielkolman
 

Similar a #golang @SkrzCzDev (Skrz DEV Cirkus 21.10.2015) (20)

Optimalizace Symfony na devu
 Optimalizace Symfony na devu Optimalizace Symfony na devu
Optimalizace Symfony na devu
 
Vašek Purchart - Optimalizace Symfony na devu (2. sraz přátel Symfony v Praze)
Vašek Purchart - Optimalizace Symfony na devu (2. sraz přátel Symfony v Praze)Vašek Purchart - Optimalizace Symfony na devu (2. sraz přátel Symfony v Praze)
Vašek Purchart - Optimalizace Symfony na devu (2. sraz přátel Symfony v Praze)
 
RocDevs/PHPPrague - Proč by sakra někdo měl chtít dělat eshop?
RocDevs/PHPPrague - Proč by sakra někdo měl chtít dělat eshop?RocDevs/PHPPrague - Proč by sakra někdo měl chtít dělat eshop?
RocDevs/PHPPrague - Proč by sakra někdo měl chtít dělat eshop?
 
Jak zlepšit zabezpečení čtvrtiny celého webu
Jak zlepšit zabezpečení čtvrtiny celého webuJak zlepšit zabezpečení čtvrtiny celého webu
Jak zlepšit zabezpečení čtvrtiny celého webu
 
20110511 Vývoj software - produktivně, efektivně, kvalitně
20110511 Vývoj software - produktivně, efektivně, kvalitně20110511 Vývoj software - produktivně, efektivně, kvalitně
20110511 Vývoj software - produktivně, efektivně, kvalitně
 
moderni webapp - frontend
moderni webapp - frontendmoderni webapp - frontend
moderni webapp - frontend
 
Víceúrovňová obrana vysvětlená na Cross-Site Scriptingu
Víceúrovňová obrana vysvětlená na Cross-Site ScriptinguVíceúrovňová obrana vysvětlená na Cross-Site Scriptingu
Víceúrovňová obrana vysvětlená na Cross-Site Scriptingu
 
2009 X33EJA Moderní Technologie Pro Vývoj JEE
2009 X33EJA Moderní Technologie Pro Vývoj JEE2009 X33EJA Moderní Technologie Pro Vývoj JEE
2009 X33EJA Moderní Technologie Pro Vývoj JEE
 
TNPW2-2011-06
TNPW2-2011-06TNPW2-2011-06
TNPW2-2011-06
 
PoSobota 96 ČB 28.4.2018
PoSobota 96 ČB 28.4.2018PoSobota 96 ČB 28.4.2018
PoSobota 96 ČB 28.4.2018
 
Opensource ve veřejné správě
Opensource ve veřejné správěOpensource ve veřejné správě
Opensource ve veřejné správě
 
Bezpečnostní útoky na webové aplikace
Bezpečnostní útoky na webové aplikaceBezpečnostní útoky na webové aplikace
Bezpečnostní útoky na webové aplikace
 
Deployment PHP aplikací | WebExpo 2011
Deployment PHP aplikací | WebExpo 2011Deployment PHP aplikací | WebExpo 2011
Deployment PHP aplikací | WebExpo 2011
 
Bezpečnostní útoky na webové aplikace, Čtvrtkon 5
Bezpečnostní útoky na webové aplikace, Čtvrtkon 5Bezpečnostní útoky na webové aplikace, Čtvrtkon 5
Bezpečnostní útoky na webové aplikace, Čtvrtkon 5
 
SEO Restart 2023: Lukáš Kostka - AI a R studio – optimalizace meta tagů na ,,...
SEO Restart 2023: Lukáš Kostka - AI a R studio – optimalizace meta tagů na ,,...SEO Restart 2023: Lukáš Kostka - AI a R studio – optimalizace meta tagů na ,,...
SEO Restart 2023: Lukáš Kostka - AI a R studio – optimalizace meta tagů na ,,...
 
Instalace WordPress
Instalace WordPressInstalace WordPress
Instalace WordPress
 
Hobby Developer 3.0: Tipy a triky pro web
Hobby Developer 3.0: Tipy a triky pro webHobby Developer 3.0: Tipy a triky pro web
Hobby Developer 3.0: Tipy a triky pro web
 
Co sledovat a jak měřit u mobilního webu
Co sledovat a jak měřit u mobilního webuCo sledovat a jak měřit u mobilního webu
Co sledovat a jak měřit u mobilního webu
 
Rockaway AWS Hackaton – Kick-off Meeting Brno
Rockaway AWS Hackaton – Kick-off Meeting BrnoRockaway AWS Hackaton – Kick-off Meeting Brno
Rockaway AWS Hackaton – Kick-off Meeting Brno
 
Continuous Integration
Continuous IntegrationContinuous Integration
Continuous Integration
 

#golang @SkrzCzDev (Skrz DEV Cirkus 21.10.2015)

  • 1. #golang @SkrzCzDev Skrz DEV Cirkus 21.10.2015 V článku na Zdrojáku “Jak Skrz.cz řadí 20K nabídek podle real-time analytiky” (https://www.zdrojak.cz/clanky/jak-skrz-cz-radi-20k-nabidek-podle- real-time-analytiky/) jsem psal, že řazení probíhá v microservice napsané v Go. Před ranking service ale vzniknul “adbandit”, service, která se stará o automatickou optimalizaci nabídek v bannerech.
  • 2. Skrz používá bannery od Seznamu pro promo nabídek (např. na Novinky.cz, Proženy.cz). Nejdříve se dělal XML export položek, které se zobrazily v bannerech. Problém však byl, že člověk viděl X krát jednu a tu samou položku a nedokázali jsme položku, na kterou lidé neklikají, rychle stáhnout (XML si Seznam stahoval každých 10 minut). Dospěli jsme tedy k řešení přes IFRAME a servírování banneru přímo z našich serverů. Nejdříve to byl statický soubor, který jeden skript několikrát do minuty přegeneroval. Ale chtěli jsme ještě dynamičtěji reagovat, a tak vzniknul “adserver” - server v ReactPHP. V průměru adserver obsluhuje 100-200 req/s, v peaku 500-1000 req/s a to s latencí ~50ms a velmi malými nároky na server (zabere třeba 4 jádra, 2GB RAM)
  • 3. machine learning statistika http://www.mlguru.cz/bayesovsky-bandita-chytrejsi-a-levnejsi-ab-testovani/ Problém, který jsme řešili byl následující: Máme X ploch, Y pozic na plochu a Z reklam (nabídek), které se mohou na pozicích zobrazovat. Chceme maximalizovat počet prokliků. Nejdříve jsme reklamy vybíraly náhodně. Fungovalo to, ale ROI nebyla taková, jakou bychom chtěli. Samozřejmě možnost je řadit podle CTR (click-through rate - poměr prokliků vůči počtu impresí). Problém je ale např. s novými reklamami - nemají prokliky ani imprese, nelze určit CTR. Jak moc by se měly takové reklamy zobrazovat? Kolik jim dát prostoru? Narazil jsem na článek od Jirky Materny “Bayesovský bandita: chytřejší a levnější A/B testování”. Neříkal bych tomu úplně “machine learning”, je to spíše chytřejší použití statistiky. Nicméně výsledkem je online učící se algoritmus.
  • 4. 😰 Pro pochopení, co je Beta rozdělení a proč dobře modeluje problém, který jsme řešili, doporučji projít odkazovaný článek.
  • 5. PHP? Pak ale přišlo v čem to napsat. Potřebujeme v každém požadavku najít pro každou reklamu hodnotu náhodné veličiny odpovídající jejímu Beta rozdělení. V peaku 1000 req/s, maximálně řekněme 100 reklam, tzn. potřebujeme Beta rozdělení spočíst 100- tisickrát každou sekundu. Kompilovaná implementace Beta rozdělení to dokáže na single-core milionkrát za sekundu, takže to jde. Pro PHP existuje PECL balíček stats. Ale ten má poslední aktualizaci 2012 a nejsou pro něj Debianí balíčky. Tedy pro deployment na produkci je to “no-no”. PHP implementace Beta rozdělení, pokud bude jen 100 krát pomalejší, nebude stíhat. Jaké jsou tedy další možnosti?
  • 6. C? 💀 Java? 💩 Céčko by to mohlo stíhat. Jenže v něm nikdo neumí, balíčkovací systém, kompilace, deloyment - hodně složité. Java je jednoduchá. Hodně lidí v ní umí, ale nikomu se v tom dělat nechce :) A opět je tu kompilace a deployment…
  • 7. Go! 👍 roku 2009 jsem napsal 1. český tutorial 😛 http://programujte.com/clanek/2009112200-go-novy- programovaci-jazyk-od-google/ (většina problémů, co se tam píše, už naštěstí neplatí 😊) Go jsme znal, hodně jednoduchý jazyk, kdokoli se do něj dokáže dostat během chvále, hodí se na psaní serverů. Deployment je prostě nahrání jedné statické binárky na server. Má knihovny na komunikaci po síti a našel jsem knihovnu na počítání mimo jiného Beta rozdělení (https:// code.google.com/p/gostat/). Takže Go! Projdu na následujících reálné ukázky kódu adbandity, jak vypadá Go.
  • 8. func NewServingService(...) *ServingService { service := &ServingService{ cfg: cfg, dbmap: dbmap, connection: connection, doneChan: make(chan interface{}), // ... } service.Start() return service } Adbandit běží vedle Adserveru a komunikují spolu po síti. Nejlepší by bylo, kdyby se Adserver přepsal celý do Go, ale obsahoval už hodně logiky kolem výběru nabídek, byly v něm připravené šablony pro bannery apod. Adbandit se tedy skládá z několika service. `ServiceService` se stará o vyřizování requestů na bannery. Adserver přijme request, rozhodne, jestli má jít na Adbandit, a pokud ano, přepošle na Adbandit, ten vrátí v JSONu položky, které má Adserver zobrazit. Ten je hodí do šablony, dotáhne další potřebné data a vyrenderuje. Takhle se dělají v Go “konstruktory” - vytvoří se funkce (uvozená “func”) s prefixem “New”. Metoda “Start” spustí v service vše potřebné.
  • 9. // Config - struct defining bandit config file type Config struct { Database Database Rabbitmq Rabbitmq HTTP HTTP MutationsFile string `yaml:"mutations_file"` // ... } // Database - SQL database configuration type Database struct { Driver string Host string Port int User string Password string Database string } // Rabbitmq - RabbitMQ configuration type Rabbitmq struct { Host string Port int User string Password string Vhost string RPCQueue string `yaml:"rpc_queue"` } // HTTP configuration type HTTP struct { Host string Port int HitDomain string `yaml:"hit_domain"` } Proměnná “cfg” na předchozím slajdu byla struktura typu “Config”. Všimněte si, že Go nejdříve píše název field struktury (proměnné) a poté typ (narozdíl např. od C/Java). Taky zde není nic jako `public`/`private`. Go to rozlišuje podle toho, jestli je první písmeno velké (public), nebo malé (private). Za typem field jde uvést ještě tzv. “struct tag”. Používají ho různé serializační knihovny, např. zde YAML. Hezké zarovnání za vás vyřeší “go fmt”. Obecně se nemusíte v týmu hádat o coding style, všechny Go zdrojáky mají jeden - jak vám to naformátuje “go fmt”.
  • 10. // // start HTTP server // service.server = &http.Server{ Addr: fmt.Sprintf("%s:%d", service.cfg.HTTP.Host, service.cfg.HTTP.Port, ), Handler: http.HandlerFunc(service.handleHTTPRequest), } go func() { if err := service.server.ListenAndServe(); err != nil { panic(err) } }() Ve standardní knihovně je implementace HTTP serveru - balíček `net/http`. Před jakékoli volání funkce jde napsat `go`. To vytvoří tzv “goroutine” - kus kódu, který se začne vykonávat konkurenčně se současným. Tady v goroutine spustíme na HTTP serveru metodu `ListenAndServe` - ta začne poslouchat na socketu a vyřizovat requesty daným handlerem.
  • 11. // // start RPC server // for i := 0; i < runtime.NumCPU(); i++ { channel, err := service.connection.Channel() if err != nil { log.Panicf("could not create channel: %sn", err) } requestDelivery, err := channel.Consume(...) if err != nil { log.Panicf("could not start consumer: %sn", err) } go func(channel *amqp.Channel, requestDelivery <-chan amqp.Delivery, i int) { for { select { case delivery := <-requestDelivery: service.handleRequestDelivery(channel, delivery) case <-service.doneChan: if err := channel.Close(); err != nil { log.Panicf("could not close channel: %sn", err) } break } } }(channel, requestDelivery, i) } HTTP bohužel nestíhalo vyřizovat requesty, hlavně na straně Adserveru - procesy umíraly na moc otevřených file descriptorů. Rozhodl jsem se zkusit nejjednodušší řešení - místo HTTP jako transportního prokotolu jsem použil RPC přes RabbitMQ (http://www.rabbitmq.com/tutorials/tutorial-six- go.html). Na straně PHP přes async knihovnu BunnyPHP (https://github.com/jakubkulhan/bunny).
  • 12. func (service *ServingService) handleRequestDelivery( channel *amqp.Channel, delivery amqp.Delivery ) { req := mq.ParseAdbanditRequest(delivery.Body) buf := bytes.NewBuffer(nil) service.handleAdbanditRequest( buf, int(req.BannerID), req.Count, req.UID, false, ) channel.Publish("", delivery.ReplyTo, false, false, amqp.Publishing{ CorrelationId: delivery.CorrelationId, ContentType: "application/json", Body: buf.Bytes(), }) delivery.Ack(false) } Ukázka, jak vypadá RPC přes RabbitMQ. `delivery.Body` je request v JSON formátu. `mq.ParseAdbanditRequest` pomocí standardní knihovny a “struct tagů” vyparsuje data requestu.
  • 13. txDeliveryChan, err := service.channel.Consume(...) if err != nil { panic(err) } service.txDeliveryChan = txDeliveryChan go func() { for { select { case delivery := <-service.actionDeliveryChan: service.handleActionDelivery(delivery) case delivery := <-service.txDeliveryChan: service.handleTxDelivery(delivery) case <-service.ticker.C: service.tick() case <-service.doneChan: log.Print("ServingService done") break } } }() Aby se mohly updatovat alfa a beta parametry Beta rozdělení, Adbandit čte z RabbitMQ ještě všechna data o akcích uživatelů - tzn. klicích na reklamy, impresím na reklamy. Go poskytuje krásný “select” statement, který blokuje, dokud nepřijde nějaká zpráva a podle toho začne vykonávat danou větech.
  • 14. #AwesomeGo • výkon! • built-in concurrency přímo v jazyku • žádný callback/promise-hell jako v JS • skvělá standardní knihovna, dobré 3rd party • RabbitMQ knihovna:https://github.com/streadway/amqp • database/sql: http://go-database-sql.org/ • mysql driver: https://github.com/go-sql-driver/mysql • YAML: http://gopkg.in/yaml.v2 • CLI: https://github.com/codegangsta/cli • go fmt; go test; a vůbec go tool • rychlá kompilace, jednoduchá cross-kompilace • env GOOS=linux GOARCH=amd64 go build . Go se osvědčilo. Výkon o moc lepší být už nemůže (je to zkompilované do binárkky), poskytuje skvělou standardní knihovnu, dobré 3rd-party knihovny a super tooling, např.: a) `go fmt` pro formátování kódu - neřešíte v týmu coding style b) `go test` pro unit testy a benchmarky, nemusíte instalovat zvlášť nějaký `goUnit`. c) `go` příkaz umí i další věci (např. detekci race conditions), zatím jsem však nepotřeboval.
  • 15. #NotSoAwesomeGo • IDE? • Atom? plugin do IntelliJ IDEA / PhpStorm? • $GOPATH • package manager / vendoring • http://getgb.io/ • https://github.com/Masterminds/glide • řešení $GO15VENDOREXPERIMENT? • DI container • https://github.com/facebookgo/inject • https://github.com/karlkfi/inject • https://github.com/peter-edge/go-inject Věc, co mě nejvíc štve, je `$GOPATH` - všechno se instaluje globálně, nelze pořádně řešit verze závislostí. Existují různé 3rd-party package managery pro Go. V Go 1.5 přibyla podpora pro `vendor`, taková lokální `$GOPATH`, něco jako `node_modules`. Package managery (např. Glide) to již začaly využívat. Doufám, že co nejdříve přidají to, co dělá Glide přímo do `go` toolu. Taky mi chybí pořádný DI container. Z vypsaných knihoven mi ani jedna napřijde, že by to řešila dobře.
  • 16. #GoNext • Concurrency patterns:
 https://talks.golang.org/2012/concurrency.slide#1 • Pipelines and cancellation:
 https://blog.golang.org/pipelines • Webové aplikace s net/http:
 https://golang.org/doc/articles/wiki/ • gRPC:
 http://www.grpc.io/ • Seznam dobrých knihoven:
 http://awesome-go.com/ Odkazy, které doporučuji projít. gRPC je RPC framework od Google. Pokud bych měl znovu řešit propojení Adserver/Adbandit, sáhl bych právě pro gRPC. Ve Skrzu je použito právě pro ranking service.
  • 17. #GoInTheWild • Parse
 http://blog.parse.com/learn/how-we-moved-our-api-from-ruby-to-go-and-saved-our-sanity/ • Docker
 https://www.docker.com/ • dl.google.com
 http://talks.golang.org/2013/oscon-dl.slide#1 • VividCortex (monitoring výkonu databází)
 https://www.vividcortex.com/ • CockroachDB (open-source DB ~ Google Spanner)
 https://github.com/cockroachdb • Mattermost (open-source on-premise kopie Slack)
 https://github.com/mattermost/platform • Bleve (full-text search)
 https://github.com/blevesearch/bleve • Prometheus (monitoring, time-series databáze)
 http://prometheus.io/ • InfluxDB (time-series databáze)
 https://influxdb.com/ Zajímavé projekty, které používají Go.
  • 18. Díky! Otázky? Chcete se o Go dozvědět víc? 
 Přijďte na GoLang Meetup 12.11.2015
 http://srazy.info/golang-meetup/5676