Семь тысяч Rps, один go
-
Upload
badoo-development -
Category
Technology
-
view
5.680 -
download
4
Transcript of Семь тысяч Rps, один go
![Page 1: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/1.jpg)
Семь тысяч rps, один Go.Сергей Камардин, @mail.ru
![Page 2: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/2.jpg)
Вступление
![Page 3: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/3.jpg)
Что нужно сделать?Сервер между браузером и сторонним сервисом. Нужно уметь:
● Держать много соединений по WebSocket;● Делать много HTTPS запросов;● Разбирать ответы от сервиса;● Инлайнить ресурсы;● Много работать с JSON.
![Page 4: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/4.jpg)
Какую технологию выбрать?Выбирали по соотношению эффективности разработки и производительности результата:
● Node.js;● Rust;● Go;● Scala.
bit.do/mailgo
![Page 5: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/5.jpg)
WebSocket
WebSocket — протокол полнодуплексной связи поверх TCP-соединения, предназначенный для обмена сообщениями между браузером и веб-сервером в режиме реального времени.
● Бинарный;● Использует HTTP для Upgrade соединения;● Поддерживается всеми современными браузерами.
![Page 6: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/6.jpg)
WebSocket
Имплементации:
● golang.org/x/net/websocket● github.com/gorilla/websocket
Аргументы за gorilla/websocket:
● Активно поддерживается;● Умеет control messages;
![Page 7: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/7.jpg)
JSON-RPCПротокол вызова удаленных процедур.
● Двунаправленный;● Асинхронный.
--> {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}
<-- {"jsonrpc": "2.0", "result": 19, "id": 1}
--> {"jsonrpc": "2.0", "method": "divide", "params": [42, 0], "id": 2}
<-- {"jsonrpc": "2.0", "error": "division by zero", "id": 2}
![Page 8: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/8.jpg)
Реальность
![Page 9: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/9.jpg)
Проблемы● Как сократить количество соединений к серверу?● Что будет, если сторонний сервис заснет?● Как сократить затраты CPU на установку HTTPS соединений?
![Page 10: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/10.jpg)
Переиспользование соединенийПри большой нагрузке, исходящие соединения практически не переиспользуются.
![Page 11: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/11.jpg)
Переиспользование соединенийСтандартная библиотека позволяет влиять на это:
provider.SetClient(&http.Client{
Transport: &http.Transport{
...
MaxIdleConnsPerHost: 128,
...
},
})
![Page 12: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/12.jpg)
Переиспользование соединенийСтандартная библиотека позволяет влиять на это.
![Page 13: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/13.jpg)
Если сервис зависнетВ случае таймаута мы можем только закрыть соединение.
![Page 14: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/14.jpg)
Если сервис зависнет
![Page 15: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/15.jpg)
TLS и рукопожатиеУстановка HTTPS соединения забирает много CPU:
![Page 16: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/16.jpg)
TLS и рукопожатиеУстановка HTTPS соединения забирает много CPU:
![Page 17: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/17.jpg)
Переиспользование TLS сессий
provider.SetClient(&http.Client{
Transport: &http.Transport{
...
TLSClientConfig: &tls.Config{
ClientSessionCache: tls.NewLRUClientSessionCache(128),
},
...
},
})
Стандартная библиотека позволяет влиять и на это:
![Page 18: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/18.jpg)
Переиспользование TLS сессийСтандартная библиотека позволяет влиять и на это:
![Page 19: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/19.jpg)
Переиспользование TLS сессий
![Page 20: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/20.jpg)
Если сервис зависнетВ случае таймаута мы можем только закрыть соединение и открыть снова.
![Page 21: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/21.jpg)
Если сервис зависнетВ случае таймаута мы все еще можем только закрыть соединение. Но теперь мы переустанавливаем его гораздо быстрее.
![Page 22: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/22.jpg)
JSON
![Page 23: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/23.jpg)
JSON
type MyCoolStruct struct {
Key string
Value int32
}
Можно значительно сэкономить время, если генерировать код сериализации/десериализации структур:
![Page 24: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/24.jpg)
{"key": "question", "value": 42}
JSON
func decodeMyStruct(in *lexer.Lexer, out *MyCoolStruct) {
for !in.HasNext() {
switch in.UnsafeString() {
case "key":
out.Key = in.String()
case "value":
out.Value = in.Int32()
}
}
}
Можно значительно сэкономить время, если генерировать код сериализации/десериализации структур:
![Page 25: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/25.jpg)
JSON
BenchmarkEncodingJson-8 1000000 1236 ns/op 288 B/op 4 allocs/op
BenchmarkEasyJson-8 10000000 179 ns/op 0 B/op 0 allocs/op
Можно значительно сэкономить время, если генерировать код сериализации/десериализации структур:
![Page 26: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/26.jpg)
JSON
Библиотеки реализующие данный подход:
● github.com/pquerna/ffjson● github.com/mailru/easyjson
Аргументы за mailru/easyjson:
● На данный момент самая быстрая;● Гибкая конфигурация (auto to_snake_case и тд);
![Page 27: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/27.jpg)
Микрооптимизации
![Page 28: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/28.jpg)
Микрооптимизации● sync.Pool;● избегать копирований;● избегать аллокаций;● инлайнить функции;● работать с байтами;
![Page 29: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/29.jpg)
Inline функцийfunc CheckChar(c byte) int {
if isGoodChar(c) {
return 42
} else {
return 0
}
}
func isGoodChar(c byte) bool {
switch c {
case 'x', 'y', 'z':
return false;
default:
return true;
}
}
![Page 30: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/30.jpg)
Inline функцийfunc CheckChar(c byte) int {
if isGoodChar(c) {
return 42
} else {
return 0
}
}
func isGoodChar(c byte) bool {
return c != 'x' && c != 'y' && c != 'z'
}
![Page 31: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/31.jpg)
Inline функцийfunc CheckChar(c byte) int {
if c != 'x' && c != 'y' && c != 'z' {
return 42
} else {
return 0
}
}
func isGoodChar(c byte) bool {
return c != 'x' && c != 'y' && c != 'z'
}
![Page 32: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/32.jpg)
Inline функций
$ go build -gcflags=-m inline.go
inline/inline.go:20: can inline isGoodChar
inline/inline.go:3: can inline CheckChar
inline/inline.go:4: inlining call to isGoodChar
Можно проверить с помощью gcflags -m:
BenchmarkInline-8 2000000000 1.28 ns/op
BenchmarkNoInline-8 500000000 3.84 ns/op
Или косвенно по бенчмаркам:
![Page 33: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/33.jpg)
Как обнаружить проблемы
![Page 34: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/34.jpg)
Утечка памятиКлассический пример утечки с использованием горутин:
func LoadImages(done chan struct{}, urls []string) (ret [][]byte) {
results := make(chan []byte)
for _, url := range urls {
go loadImage(results, url)
}
for count := 0; count < len(urls); count++ {
select {
case <-done: // client has canceled loading
return
case r := <-results: // yet another image
ret = append(ret, r)
}
}
return
}
![Page 35: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/35.jpg)
Эмуляция нагрузкиДля HTTP:
● apache jmeter;● wrk;● ab;
Для WebSocket:
● gws;● tcpkali;● apache jmeter;
![Page 36: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/36.jpg)
Apache JMeter● Очень гибкий;● Множество отчетов;● Умеет следить за состоянием машин;● Умеет remote testing;● Можно поставить WebSocket sampler;
![Page 37: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/37.jpg)
WRKhttps://github.com/wg/wrk/
● Весьма прост;● Можно писать lua-скрипты;● Очень быстрый, может завалить nginx;
![Page 38: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/38.jpg)
GWShttps://github.com/gobwas/gws
● Можно писать lua-скрипты;● Может быть как клиентом так и сервером;
![Page 39: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/39.jpg)
Концовка
![Page 40: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/40.jpg)
SummaryПереиспользуем ресурсы:
● Коннекты;● TLS сессии;● Слайсы и структуры;
Оптимизируем код:
● Парсим синтаксис без reflection;● Профилируем;● Пишем бенчмарки;● Нагружаем и мониторим;
![Page 41: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/41.jpg)
Статистика● 170 000 живых соединений;● 1500 в секунду новых WebSocket соединений;● 7000 json-rpc сообщений в секунду;● Около 20 000 загрузок небольших изображений (~10Kb) в секунду
(~200mb/s) для последующего инлайна;
![Page 42: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/42.jpg)
Спасибо!
![Page 43: Семь тысяч Rps, один go](https://reader033.fdocument.pub/reader033/viewer/2022042520/58803e201a28abfd0a8b58f5/html5/thumbnails/43.jpg)
Ссылки● http://www.jsonrpc.org/specification● http://www.gorillatoolkit.org/pkg/websocket● https://github.com/mailru/easyjson● https://github.com/gobwas/gws● https://github.com/gobwas/glob● https://github.com/machinezone/tcpkali● https://github.com/divan/gobenchui● https://github.com/valyala/fasthttp