Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.
#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...
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,...
machine learning
statistika
http://www.mlguru.cz/bayesovsky-bandita-chytrejsi-a-levnejsi-ab-testovani/
Problém, který jsme...
😰
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 odpo...
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é.
...
Go! 👍
roku 2009 jsem napsal 1. český tutorial 😛
http://programujte.com/clanek/2009112200-go-novy-
programovaci-jazyk-od-go...
func NewServingService(...) *ServingService {
service := &ServingService{
cfg: cfg,
dbmap: dbmap,
connection: connection,
...
// Config - struct defining bandit config file
type Config struct {
Database Database
Rabbitmq Rabbitmq
HTTP HTTP
Mutation...
//
// start HTTP server
//
service.server = &http.Server{
Addr: fmt.Sprintf("%s:%d",
service.cfg.HTTP.Host,
service.cfg.HT...
//
// start RPC server
//
for i := 0; i < runtime.NumCPU(); i++ {
channel, err := service.connection.Channel()
if err != n...
func (service *ServingService) handleRequestDelivery(
channel *amqp.Channel,
delivery amqp.Delivery
) {
req := mq.ParseAdb...
txDeliveryChan, err := service.channel.Consume(...)
if err != nil {
panic(err)
}
service.txDeliveryChan = txDeliveryChan
g...
#AwesomeGo
• výkon!
• built-in concurrency přímo v jazyku
• žádný callback/promise-hell jako v JS
• skvělá standardní knih...
#NotSoAwesomeGo
• IDE?
• Atom? plugin do IntelliJ IDEA / PhpStorm?
• $GOPATH
• package manager / vendoring
• http://getgb....
#GoNext
• Concurrency patterns:

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

https://...
#GoInTheWild
• Parse

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

htt...
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
Prochain SlideShare
Chargement dans…5
×

#golang @SkrzCzDev (Skrz DEV Cirkus 21.10.2015)

268 vues

Publié le

Go se @SkrzCz používá pro výkonově nejnáročnější části aplikaci. Jedna z nich je servírování bannerů a výběr těch správných reklam do nich. 1000 req/s v peaku a "adbandit" má minimální nároky na server. Podívejte se, jak použít Go ve spolupráci s ReactPHP a RabbitMQ.

Publié dans : Ingénierie
  • Soyez le premier à commenter

#golang @SkrzCzDev (Skrz DEV Cirkus 21.10.2015)

  1. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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

×