Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

gRPC в продакшне для мобильных приложений

757 views

Published on

Доклад с gophercon russia 2018.

Published in: Engineering
  • Be the first to comment

  • Be the first to like this

gRPC в продакшне для мобильных приложений

  1. 1. gRPC в продакшне Минкин Андрей. Mad Devs
  2. 2. Кто я? • Системный администратор 7+ лет • Python 5+ лет • Go последние 3 года
  3. 3. Про что доклад
  4. 4. Как все начиналось • Служба такси • Водительское приложение • Много хотелок • Много технического долга • Невозможность поддержки • Пройденная точка невозврата
  5. 5. Как оно работало • Client-server • На TCP сокетах • Poll модель
  6. 6. Чего хотелось бы • Больше фич • Рассылки с сервера • Единый протокол
  7. 7. Смена протокола • Избавиться от текущих костылей (TCP+UDP) • Избавиться от костыльной сериализации • Убить последний питон на серверах (почти)
  8. 8. Что рассматривали? • MQTT • UDP+велосипеды и вариации(QUIC, uTP) • gRPC
  9. 9. Чем не подошли MQTT/QUIC/UDP • Много ручной работы • Нужно писать брокер • Сложно договориться о дизайне на берегу
  10. 10. Что такое gRPC • Protocol Buffers • Design first • HTTP/2 • Двунаправленный стриминг • Мультиплексинг • HTTPS • Множество языков
  11. 11. C/C++
  12. 12. Почему мы выбрали gRPC? • Design first • Генерация кода • Относительно легковестно по трафику • HTTP/2 streaming + fallback
  13. 13. Подробнее тут • Документация и код • http://www.grpc.io • https://github.com/grpc • https://github.com/grpc-ecosystem • Помощь и поддержка • https://gitter.im/grpc/grpc • https://groups.google.com/forum/#!forum/grpc-io
  14. 14. Архитектура до Водитель Twisted Django+redis TwistedВодитель
  15. 15. Storage • Redis • Percona server for MySQL
  16. 16. Первое решение • 1 стрим – 1 канал • Храним все в map • Клиент для HTTP API • Свой storage • Pub/sub в Redis на уровне приложения
  17. 17. Show me the code func (a *API) Orders(in *empty.Empty, stream pb.Foreman_OrdersServer) error { ctx := stream.Context() user, ok := user.FromContext(ctx) if !ok { return grpc.Errorf(codes.Unauthenticated, "user not found") } OrdersChan := pubsub.AddOrderReader(user.CabNumber) defer pubsub.LeaveOrderReader(user.CabNumber, OrdersChan) // .. }
  18. 18. type OrderSubscriber struct { mu *sync.RWMutex OrderSubs map[string]chan *pb.Order }
  19. 19. func AddOrderReader(cabNumber string) chan *pb.Order { ordersChan := make(chan *pb.Order) orderSubscriber.JoinOrderSubs(cabNumber, ordersChan) grpclog.Println("Join to new order readers:", cabNumber) return ordersChan }
  20. 20. func (o *OrderSubscriber) JoinOrderSubs(cabNumber string, orderChan chan *pb.Order) { o.mu.Lock() o.OrderSubs[cabNumber] = orderChan o.mu.Unlock() }
  21. 21. Внедряем • 12 factor • Docker • Nginx-stream + SSL
  22. 22. Мониторинг • https://github.com/grpc-ecosystem/go-grpc-prometheus
  23. 23. Проблемы с подключением • Приложение не может подключиться к серверу • Клиент на Go может • Дебажим, читаем спеку http/2 • Погружаемся в Android
  24. 24. В чем проблема то? • Android – нет поддержки NPN • Nginx – нет поддержки ALPN
  25. 25. Решение • -nginx • +nghttpx
  26. 26. Начали ЗБТ приложения
  27. 27. Приложение теряет связь • Рандомно отваливается стрим • Приложение не переподключается
  28. 28. Почему? • Сервер паникует • Нет явного статуса стрима, но есть статус канала • Оператор связи рубит пустые http соединения(O’rly?)
  29. 29. Почему падает сервер • Паники & гонки
  30. 30. Трассировка и отладка • http://charithe.github.io/tracing-grpc-services-in-go.html • net/http/pprof
  31. 31. Давай это порефакторим • - каналы • + пабсаб на поток
  32. 32. Было func (a *API) Orders(in *empty.Empty, stream pb.Foreman_OrdersServer) error { ctx := stream.Context() user, ok := user.FromContext(ctx) if !ok { return grpc.Errorf(codes.Unauthenticated, "user not found") } OrdersChan := pubsub.AddOrderReader(user.CabNumber) defer pubsub.LeaveOrderReader(user.CabNumber, OrdersChan) // .. }
  33. 33. type OrderSubscriber struct { mu *sync.RWMutex OrderSubs map[string]chan *pb.Order }
  34. 34. func AddOrderReader(cabNumber string) chan *pb.Order { ordersChan := make(chan *pb.Order) orderSubscriber.JoinOrderSubs(cabNumber, ordersChan) grpclog.Println("Join to new order readers:", cabNumber) return ordersChan }
  35. 35. func (o *OrderSubscriber) JoinOrderSubs(cabNumber string, orderChan chan *pb.Order) { o.mu.Lock() o.OrderSubs[cabNumber] = orderChan o.mu.Unlock() }
  36. 36. Стало (a *API) Orders(in *empty.Empty, stream pb.Foreman_OrdersServer) error { // .. ordersChan := make(chan *pb.Order, 10) errs := make(chan error, 1) pubsub := a.redisClient.Subscribe("orders") defer pubsub.Close() // ..
  37. 37. Стало // .. go func() { for { msg, err := pubsub.ReceiveMessage() // .. order := &pb.Order{} err = json.Unmarshal([]byte(msg.Payload), order) // .. if order.Status == pb.Order_NEW { ordersChan <- order } } }()
  38. 38. Стало for { select { case order := <-ordersChan: // business logic here case <-ctx.Done(): return ctx.Err() case e := <-errs: close(ordersChan) return e } }
  39. 39. Масштабирование • Рефакторинг дал более хороший механизм масштабирования • Нет состояния – нет проблем
  40. 40. Работаем с падающими стримами • Circuit breaker для упавших стримов • +keepalive
  41. 41. Keepalive на мобильных операторах • Обычно рубят NAT сессии через 1-5 минут • Выставили 20 секунд для мобильного приложения
  42. 42. waitForReady • https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md • Ждет READY состояния • Падает на всех других (SHUTDOWN, Deadline exceeded…)
  43. 43. Плохая связь • Fallback на unary • Circuit breaker • Observer на клиенте со счетчиками • Ошибка или таймаут • Перезапуск через N секунд в случае достижения maxRetries
  44. 44. Как балансировать gRPC • Со стороны клиента • Со стороны сервера
  45. 45. Как балансировать на сервере • Ngx socket
  46. 46. Как балансировать на сервере • Ngx socket – нет APLN
  47. 47. Как балансировать на сервере • Ngx socket – нет APLN • Встроенные механизмы
  48. 48. Как балансировать на сервере • Ngx socket – нет APLN • Встроенные механизмы – клиент наружу
  49. 49. Как балансировать на сервере • Ngx socket – нет APLN • Встроенные механизмы – клиент наружу • Proxy_pass в NGINX
  50. 50. Как балансировать на сервере • Ngx socket – нет APLN • Встроенные механизмы – клиент наружу • Proxy_pass в NGINX – все плохо с поддержкой HTTP/2
  51. 51. Как балансировать на сервере • Ngx socket – нет APLN • Встроенные механизмы – клиент наружу • Proxy_pass в NGINX – все плохо с поддержкой HTTP/2 • NGHTTPX
  52. 52. Как балансировать на сервере • Ngx socket – нет APLN • Встроенные механизмы – клиент наружу • Proxy_pass в NGINX – все плохо с поддержкой HTTP/2 • NGHTTPX – ликает
  53. 53. Как балансировать на сервере • Ngx socket – нет APLN • Встроенные механизмы – клиент наружу • Proxy_pass в NGINX – все плохо с поддержкой HTTP/2 • NGHTTPX – ликает • HAProxy?
  54. 54. NGHTTPX vs HAProxy • Есть опыт с HAProxy • Поддержка H2 • Тесты ОК • Полет нормальный
  55. 55. Rest • Синхронный • Документация опциональна • Внешняя отказоустойчивость • Все о ресурсах gRPC • Асинхронный • Документация обязательна • Встроенная отказоустойчивость • Все о API
  56. 56. Websocket gRPC • https://www.infoq.com/articles/websocket-and-http2-coexist
  57. 57. Выводы • gRPC можно использовать вместо REST. И это удобно • gRPC применим и в мобильных приложениях (с некоторыми оговорками) • Балансировать на сервере удобно через HAProxy • waitForReady может как помочь, так и усложнить жизнь
  58. 58. Контакты • @maddevsio • https://maddevs.io • @Gen1us2k

×