GoingPractical
ЯзыкGoдлямасштабируемыхкоммуникационныхплатформ
ITooLabsPaaS:стараяархитектура
VMWare1
LB1SW1
SW2
MFE1 BE1
DS1
SFE1 AUX12LB LoadBalancerDS* 2 DataStore
SignallingFront-End
VMWareHost
Mail/WebFront-Ends
Back-EndDNS,NTP,FAX
CoreSwitchVM*
2BE
2
AUX
2MFE
22
2
SFE
GW*
DS2
ReplicaKon
RAID-6
RAID-6
VMWare2
LB2 MFE2 BE2SFE2 AUX2
2010100subscribers60BHCA
ITooLabsPaaS:стараяархитектура
KVM3
KVM1
LB1SW1
SW2
MFE1 BE1
DS1
SFE1 AUX1
SFE3BSS Accoun(ng&Billing2
2LB LoadBalancerDS* 2 DataStore
BackupServer
SignallingFront-End
VMHost
Mail/WebFront-Ends
Back-EndDNS,NTP,FAX
CoreSwitchKVM*
1
4BE
4
AUX
2MFE
22
2
BS*
SFE
GW*
DS2
ReplicaKon
RAID-6
RAID-6
BS
KVM4
KVM2
LB2 MFE2 BE2SFE2 AUX2
SFE4
20111000subscribers900BHCA
BSS1 BSS2
ITooLabsPaaS:стараяархитектура
KVM3
KVM1
LB1SW1
SW2
MFE1 BE1
DS1
SFE1 AUX1
SFE3
TSBC 2 TrunkSBC
BSS Accoun5ng&Billing2
2LB LoadBalancerDS* 2 DataStore
BackupServer
SignallingFront-End
VMHost
Mail/WebFront-Ends
Back-EndDNS,NTP,FAX
CoreSwitchKVM*
6BE
4
AUX
2MFE
32
2
BS*
SFE
GW*
DS2
Replica5on
RAID-10
RAID-10
BS
KVM4
KVM2
LB2 MFE2 BE2SFE2 AUX2
SFE4BE3
20136300subscribers8000BHCA
BSS1 BSS2TSBC1 TSBC2
Свойпродукт:требования• Высокаяскоростьразработки!• МаксимальнаяутилизацияmulticoreH/W!• Высокаянадежность!• Высокаяустойчивостьксбоям!• Ежедневныерелизы!
Свойпродукт:основа• MulticoreH/W• Скоростьразработки• Высокаяустойчивость
Ø ПараллельноевыполнениеØ КонкурентностьØ Кластер
Свойпродукт:основа• MulticoreH/W• Скоростьразработки• Высокаяустойчивость
Ø ПараллельноевыполнениеØ КонкурентностьØ Кластер
Эрланг
Свойпродукт:основа• MulticoreH/W• Скоростьразработки• Высокаяустойчивость
Ø ПараллельноевыполнениеØ КонкурентностьØ Кластер
ЭрлангJVM(Scala)
Свойпродукт:основа• MulticoreH/W• Скоростьразработки• Высокаяустойчивость
Ø ПараллельноевыполнениеØ КонкурентностьØ Кластер
ЭрлангJVM(Scala)
Go
Go:историяХронология
• стартовалв2007• открытв2009• мыстартовалипроект – январь2012• Go1– апрель2012• Go1.1– май 2013• Go1.2– декабрь2013• Go1.3 – июль2014• Go1.4– декабрь2014• Go1.5– август2015
МыначалиработатьсGoещедопервогостабильногорелиза!
Инициатор
Авторы
• КенТомсон(UNIX,UTF-8,Plan9)• РобПайк (UTF-8,Plan9,Inferno,Limbo)• РобертГризмер (JavaHotSpot,V8Code
Generation)• >700участников!
Go:первыйвзгляд1 const n = 10242 const str = "строка"3 const ( 4 bit0, mask0 uint32 = 1<<iota, 1<<iota - 1; 5 bit1, mask1 uint32 = 1<<iota, 1<<iota - 1; 6 ) 7 8 var x, y *float 9 var z = 1.010 11 type Point struct { x, y int } 12 13 var p1 Point 14 p2 := Point{ 0, 0 } 15 16 var pp1 *Point = new(Point) 17 pp2 := &Point{} 18 19 points := []T{ Point{1,1}, Point{2,2} } 20 21 pointsMap := map[string]Point{ 22 "start": Point{0,0},23 }
// Целая константа// Строковая константа// Объявление блока констант// bit0 = 1, mask0 = 0// bit1 = 2, mask2 = 1
// Объявление с типом// Объявление без указания типа
// Описание типа (структуры)
// Объявление переменной типа Point// Объявление с инициализацией
// Объявление переменной – указателя на Point// Объявление указателя с указателем объекта
// Объявление и инициализация массива
// Объявление и инициализации ассоциативного массива
Go:Hello,World1 package main 2 3 import ( 4 "os"5 "flag"6 ) 7 8 var word = flag.String("w", "world", "word") 9 var noEOL = flag.Bool("n", false, ̀ no \n`) 10 11 func main() { 12 flag.Parse() 13 s := "Hello, " + *word 14 if !*noEOL { 15 s += "\n"16 } 17 os.Stdout.WriteString(s)18 }
• C-подобный синтаксис• Точкасзапятойнеобязательна• Скобки ()вуправляющихструктурах необязательны• Фигурныескобки{}обязательны• Кодорганизованвпакеты• Типизациястатическая• Типвыводитсяавтоматически
(вбольшинствеслучаев)
growler:~$ go run hello.go -w "Go World"Hello, Go Worldgrowler:~$ go build hello.gogrowler:~$ ./helloHello, worldgrowler:~$
Go:Hello,World(overHTTP)1 package main 2 3 import ( 4 "net/http"5 "encoding/json"6 stats "github.com/c9s/goprocinfo/linux"7 ) 8 9 func handler(w http.ResponseWriter, r *http.Request) { 10 w.Header().Set("Content-Type", "text/json") 11 stats, _ := stats.ReadLoadAvg("/proc/loadavg") 12 resp, _ := json.MarshalIndent(stats, "", " ") 13 w.Write(resp) 14 } 15 16 func main() { 17 http.HandleFunc("/", handler) 18 http.ListenAndServe(":8080", nil) 19 }
• Функциявыступаетпараметром• importссылаетсясразунарепозиторийgithub• httpиjson вштатнойбиблиотеке• Естьинтроспeкция(reflection, используетсяв
encoding/json)growler:~$ export GOPATH=`pwd`growler:~$ go get github.com/c9s/goprocinfo/linuxgrowler:~$ go build test.gogrowler:~$ ./test &[1] 18110growler:~$ curl -s http://localhost:8080{
"last1min": 0.66,"last5min": 0.58,"last15min": 0.65,"process_running": 1,"process_total": 2038,"last_pid": 18121
}growler:~$ kill %1[1]+ Exit 2 ./testgrowler:~$
Go:методыиинтерфейсы1 type Rect struct { xl, yl, xu, yu float32 } 2 type Circ struct { x, y, r float32 } 3 4 func (r *Rect) Square() float32 { 5 return math.Pow((c.xu - c.xl), 2) +6 math.Pow((c.yu - c.yl), 2)7 } 8 9 func (c *Rect) Square() float32 { 10 return math.Pi * math.Pow(c.r, 2) 11 } 12 13 type Shape interface { 14 func Square() float32 15 } 16 17 var s1 Shape = &Rect{} 18 var s2 Shape = &Circ{} 19 20 var any interface{} = nil
// Объявление метода
// Объявление метода
// Объявление интерфейса
// Объявление и инициализация переменной типа Shape// -”-
// Объявление переменной, которая может ссылаться// на любой объект
Go:goroutines1 package main 2 3 import "net"4 5 func serve(conn net.Conn) { 6 var ( buf = make([]byte, 1024); r int; err error ) 7 defer conn.Close()8 for { 9 if r, err = conn.Read(buf); err != nil { 10 break11 } 12 if _, err = conn.Write(buf[0:r]); err != nil { 13 break14 } 15 } 16 } 17 18 func main() { 19 sock, _ := net.Listen("tcp", ":5000") 20 for { conn, _ := sock.Accept(); go serve(conn) } 21 }
• Легковесныепотокивыполнения(<2Kb)• Ничтожныезатратынасоздание• Собственный планировщик• Передачауправления:
• Выражение go• Блокирующийвызов(I/O)• Сборкамусора• Операциясканалами
• Параллельноевыполнение внесколькопотоков(поумолчаниючислопотоков== числуядер CPU)
growler:~$ go run test.go &[1] 14694growler:~$ echo Hello | nc localhost 5000Hellogrowler:~$ kill %1[1]+ Exit 2 go run test.gogrowler:~ $
Go:channels1 package main 2 3 import ( 4 "net/http"5 "strconv"6 ) 7 var ch chan int 8 func count() { 9 i := 010 for { ch <- i; i += 1 } 11 } 12 func handler(w http.ResponseWriter, r *http.Request) { 13 i := <- ch 14 w.Write([]byte(strconv.Itoa(i) + "\n")) 15 } 16 func main() { 17 ch = make(chan int); go count() 18 http.HandleFunc("/", handler) 19 http.ListenAndServe(":8080", nil) 20 }
• Канал– средствокоммуникациимеждуgoroutines• ОднонаправленнаяочередьвNэлементов• ПоумолчаниюN==1• Каналможетбытьпараметромиличленом
структуры• Каналявляетсяитератором (fori:=rangech)• Каналможнопередатьчерезканал!
growler:~$ go run test.go &[1] 23469growler:~$ ( while [ $(curl -s http://localhost:8080) -lt 1000 ]; do true; done ) &[2] 23476growler:~$ ( while [ $(curl -s http://localhost:8080) -lt 1000 ]; do true; done ) &[3] 23515growler:~$ curl -s http://localhost:80801002[2]- Done[3]+ Donegrowler:~$
Go:select
1 func server(service chan *request, quit chan bool) { 2 for { 3 select { 4 case req := <-service: 5 go process(req) 6 case <-quit: 7 break8 } 9 } 10 }
Операторselectпохожнаоператорswitchипозволяетвыбратьпервоепришедшеесообщениеизнесколькихканалов.
Go:CAPI(CGO)1 package main 2 /* 3 #ifdef __APPLE__ 4 #include <sys/types.h> 5 #include <pwd.h> 6 #include <uuid/uuid.h> 7 #else 8 #include <sys/types.h> 9 #include <stdlib.h> 10 #endif 11 #include <pwd.h> 12 */13 import "C"14 import "fmt"15 16 func GetName(uid uint32) string { 17 cpw := C.getpwuid(C.uid_t(uid)) 18 return C.GoString(cpw.pw_name) 19 } 20 21 func main() { 22 fmt.Printf("%s\n", GetName(2000)) 23 }
CGOдаетвозможность использоватьвызовыCнепосредственно вкодеиподключатьсторонниебиблиотеки
growler:~$ go run test.gogrowlergrowler:~$
Go:инструменты• Однакоманда(go)• Сборка:gobuild<package>• Тест:gotest<package>• Запуск:gorun<package>• Обновлениезависимостей:goget<package>• Форматированиеисходников:gofmt• Документация:godoc• Профайлинг:gotoolpprof• Генераторпарсеров:gotoolyacc
Go:IDE• IntelliJIDEACE15+GoPlugin• Eclipse+GoPlugin• NetBeans+GoPlugin
(GoWorks)• VIM• Emacs• Atom/Sublime/Notepad++/… IntelliJIDEACE15+GoPlugin– лучшееGoIDEна
текущиймомент (верьте,япробовалвсе!)
Go:библиотеки
Go:эксплуатация• Статическаясборка– никакихrun-timeзависимостей!
• Единственныйбинарник – этооченьудобно• (мыдажеперешликстатическойсборкидругогокомпонента– медиа-процессоранаC++)
• Стектрейсыесть,нобываютнужныоченьредко
Go:сообщество
GovsNode.jsGo1 import ( "fmt"; "net/http" ) 2 3 func handler(w http.ResponseWriter, 4 r *http.Request) { 5 fmt.Fprintf(w, "Hello World") 6 } 7 8 func main() { 9 http.HandleFunc("/", handler) 10 http.ListenAndServe(":8080", nil) 11 }
Node.js1 var http = require('http'); 2 3 var handler = function (req, res) { 4 res.writeHead(200, {'Content-Type':5 'text/plain'}); 6 res.end('Hello World\n'); 7 } 8 9 var webServer = http.createServer(handler) 10 webServer.listen(8080, '127.0.0.1');
• Компилируется (пустьибыстро)• Многопотоков• Усложнение логикинеусложняеткод:
простопишемвResponseWriter!
• Интерпретатор(запустили– сразуработает)• Одинпоток (чтобыбыломного– нуженcluster)• Усложнение логики?callbacks?promise?Q?async.js?
yield?
Go:недостатки?Множество!• Нетparametricpolymorphysm/templates/generics(только
специальныеоперациидляработысarray/map)• Нетотладчика(всееще)• Провоцируетcut’n’paste• Жесткодиктуетправила(включаяформатирование!)• Нетисключений(естьpanic/recover,ноегонельзяиспользовать
дляуправления)• Очень(слишком!)простойсинтаксис• Нетместафантазии!
Почему,всёже,Go?• быстрая(БЫСТРАЯ)сборка?• развертываниеоднимбинарником?• конкурентность?• эффективнаяутилизацияmulticoreCPU?• быстроеразвитие?• могучеесообщество?
Почему,всёже,Go?
НЕТ.Всёпроще.
GoРАБОТАЕТ.
Go– предельнопрактичныйязык дляинженеров*
*занятыхразработкойвеб-сервисовираспределенныхсистем
Go:ктоиспользует?• Google:
– Kubernets– YouTube– dl.google.com– …имногоедругое
• Docker• InfluxData• Twitter (https://blog.twitter.com/2015/handling-five-
billion-sessions-a-day-in-real-time)• Dropbox
(https://blogs.dropbox.com/tech/2014/07/open-sourcing-our-go-libraries/)
• Koding,Node.js->Golang(https://www.quora.com/Why-did-Koding-switch-from-Node-js-to-Go)
• Parse,RoR->Golang(http://blog.parse.com/learn/how-we-moved-our-api-from-ruby-to-go-and-saved-our-sanity/)
• Medium(https://medium.com/medium-eng/how-medium-goes-social-b7dbefa6d413)
• Rackspace (https://github.com/rackspace/rack)• Baidu
(https://twitter.com/jbuberel/status/617776229437980673)
• ...иещемногие
Источник:https://github.com/golang/go/wiki/GoUsers
ITooLabsCentrex• Динамическийall-activeкластер• Равномерноераспределениенагрузки(hashring)• Protobufдлявнутрикластерныхкоммуникаций• SIP/HTTP/WebSocket/…длявнешних• Медиа-процессор(ok,этонаC++)• ВстроенныйинтерпретаторJavaScript(неnode!)• Генераторнагрузки/тестировщиквызовов!• DevOps-friendly:
installapp default git ”[email protected]:app/app” 0a3124f
• 1годспринятиярешениядопервогоразвертывания!
ITooLabsPaaS:enterITooLabsCentrex
KVM5
KVM3
KVM1
LB1SW1
SW2
MFE1 BE1
DS1
SFE1 AUX1
SFE3
2HS History&Sta-s-cs2I-AS ITooLabsCS:TrunkSBC,Records
TSBC 2 TrunkSBC
BSS Accoun;ng&Billing2
2LB LoadBalancer
DS* 2 DataStore
BackupServer
SignallingFront-End
VMHost
Mail/WebFront-Ends
Back-EndDNS,NTP,FAX
CoreSwitch
KVM*
5BE
6
AUX
2MFE
42
2
BS*
SFE
GW*
SFE5
DS2
Replica;on BE4
RAID-10
RAID-10
BS
KVM4
KVM2
LB2 MFE2 BE2SFE2 AUX2
SFE4BE3
2014
11000subscribers
13500BHCA
BSS1 BSS2TSBC1 TSBC2
KVM6
V
I-AS1
V
I-AS2HS1 HS2
ITooLabsPaaS:enterITooLabsCentrex
KVM5 KVM6
KVM7 KVM8
KVM3
KVM1
LB1SW1
SW2
MFE1 BE1
DS1
SFE1 AUX1
SFE3
TSBC 2 TrunkSBC
BSS Accoun>ng&Billing2
ITooLabsCSMGW:RecordsI-MS 22I-AS ITooLabsCS:TrunkSBC,Records2 History&Sta>s>csHS
2LB LoadBalancerDS* 2 DataStore
BackupServer
SignallingFront-End
VMHost
Mail/WebFront-Ends
Back-EndDNS,NTP,FAX
CoreSwitchKVM*
6BE
8
AUX
2MFE
22
2
BS*
SFE
GW*
DS2
Replica>on
RAID-10
RAID-10
BS I-MS1
KVM4
KVM2
LB2 MFE2 BE2SFE2 AUX2
SFE4BE3
I-MS2
201516000subscribers25000BHCA
BSS1 BSS2TSBC1 TSBC2
SFE5 BE4
V
I-AS1
V
I-AS2HS1 HS2 SFE6
Кейс:голосоваяпочтана70mlnабонентов
• МобильныйоператорвИндонезии,70mlnабонентов• 1800вызовов всекунду/50000одновременныхдиалогов
(6480000BHCA!)натесте• 50000одновременныхдиалогов натесте• ~280 вызововвсекундудневнаятекущаянагрузка
(~1000000BHCA,~36mlnабонентов,+2mlnкаждуюнеделю)• 100000000вызововвнеделю• 600вызововвсекундудневнаяцелеваянагрузка• 16серверов(7ASнаGo,9MGнаC++;можнобыломеньше!)• Cephвкачествехранилищазаписей(тройнаярепликация)• Выдерживаетсистемныйсбойдо2серверов• Rollingupdates
Кейс:голосоваяпочтана70mlnабонентов
31.12.2015,черезмесяцпослестарта, былозаписано160000сообщений заодиндень
ВОПРОСЫ?