Технические аспекты знакоства с девушкой в Интернете

3,058 views

Published on

Обзор опыта и проблем, решенных при разработке speed-dating сайта Wannafun.ru

Published in: Technology
0 Comments
12 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,058
On SlideShare
0
From Embeds
0
Number of Embeds
122
Actions
Shares
0
Downloads
32
Comments
0
Likes
12
Embeds 0
No embeds

No notes for slide

Технические аспекты знакоства с девушкой в Интернете

  1. 1. Технические аспекты знакомства с девушкой в интернете Алексей Найден, Алексей Носков Evil Martiansвоскресенье, 16 декабря 12 г.
  2. 2. воскресенье, 16 декабря 12 г.
  3. 3. воскресенье, 16 декабря 12 г.
  4. 4. Wannafun.ru • Онлайн speed dating • Знакомство только с теми, кто в сети • Поток лиц aka «матрица» • 3 минуты чата для принятия решения • 48% / 52%воскресенье, 16 декабря 12 г.
  5. 5. Проект в цифрах • 500 000 пользователей • 2000 онлайн • 2-3 события в секунду на юзера • Более 100 http-запросов в минуту на юзера • Более 5 000 000 чатов • Более 45 000 000 сообщенийвоскресенье, 16 декабря 12 г.
  6. 6. На чем всё работает Erlang Postgres Redis EventMachine Rails Resqueвоскресенье, 16 декабря 12 г.
  7. 7. воскресенье, 16 декабря 12 г.
  8. 8. Онлайн-взаимодействия • Знакомства: входящие/исходящие, сообщения • Контакт-лист: личные сообщения, онлайн/ оффлайн • Уведомлениявоскресенье, 16 декабря 12 г.
  9. 9. Взаимодействие с браузером Pusher (http://pusher.com/) Faye (http://faye.jcoglan.com/) Push-канал к клиенту Pub/sub Нет серверной логики Нет серверной логики Socket.io (http://socket.io/) Абстракция над WebSocket, Flash и Polling Произвольная серверная логикавоскресенье, 16 декабря 12 г.
  10. 10. Серверная реализация socket.io • NodeJS • На тестах падала VM — epic fail • EventMachine • Не было актуальной версии • Erlang • Не было актуальной версиивоскресенье, 16 декабря 12 г.
  11. 11. Что хорошего в Erlang • Нет коллбеков - простой последовательный код • Нет разделяемого состояния, структуры данных неизменяемы - concurrency проще • Иерархия супервизоров - высокая устойчивость • Прозрачная распределенность • Бесшовный деплойвоскресенье, 16 декабря 12 г.
  12. 12. Архитектура чат-сервера • Соединения обслуживаются Cowboy • Каждая сессия - отдельный процесс • Вспомогательные процессы для работы с БД, Redisвоскресенье, 16 декабря 12 г.
  13. 13. Начало знакомствавоскресенье, 16 декабря 12 г.
  14. 14. Одновременный ответвоскресенье, 16 декабря 12 г.
  15. 15. Синхронная реализациявоскресенье, 16 декабря 12 г.
  16. 16. воскресенье, 16 декабря 12 г.
  17. 17. Очереди задач • Resque • Быстро работает, использует Redis • Удобный Web UI • Redis полезен и для других задач: • Хранение счетчиков • Синхронизация состояния • Кэшированиевоскресенье, 16 декабря 12 г.
  18. 18. Уникальные задачи • Сохранение сообщений • Прогрев кэша • Расчет статистики Можно использовать Redis для блокировкивоскресенье, 16 декабря 12 г.
  19. 19. Обычный код def perform return unless redis.setnx("lock", true) # do task actions ensure redis.del "lock" end Реализация: resque-lock (<= 1.0.0)воскресенье, 16 декабря 12 г.
  20. 20. воскресенье, 16 декабря 12 г.
  21. 21. Правильный код http://redis.io/commands/setnx def perform now = Time.now.to_i timeout = now + 60 unless redis.setnx("lock", timeout) # Lock is active return if now <= redis.get("lock").to_i # Lock is not expired return if now <= redis.getset("lock", timeout).to_i end # do task actions 11 redis.del "lock" end Реализация: resque-lock (>= 1.1.0)воскресенье, 16 декабря 12 г.
  22. 22. воскресенье, 16 декабря 12 г.
  23. 23. Хорошие индексы Хорошие = Ускоряющие необходимые запросы create_table messages do |t| t.references source t.references destination t.string body t.timestamp created_at t.timestamp read_at end # History of messages received from given user SELECT * FROM messages WHERE destination_id = ? AND source_id = ? ORDER BY created_at DESC LIMIT 10 # Unread messages of user SELECT * FROM messages WHERE destination_id = ? AND read_at IS NULL ORDER BY created_at DESC LIMIT 10воскресенье, 16 декабря 12 г.
  24. 24. Плохие индексы # History of messages received from given user add_index messages, [source_id, destination_id] # Unread messages of user add_index messages, [destination_id] Limit -> Sort Sort Key: created_at Sort Method: top-N heapsort Memory: 25kB -> Index Scan using messages_between_users on messages Index Cond: ((source_id = ?) AND (destination_id = ?)) Total runtime: 6.451 ms Limit -> Sort Sort Key: created_at Sort Method: quicksort Memory: 26kB -> Bitmap Heap Scan on messages Recheck Cond: (destination_id = ?) Filter: (read_at IS NULL) -> Bitmap Index Scan on messages_unread Index Cond: (destination_id = ?) Total runtime: 123.983 msвоскресенье, 16 декабря 12 г.
  25. 25. Отличные индексы! # History of messages received from given user add_index messages, [source_id, destination_id, created_at], :order => { created_at => desc } # Unread messages of user add_index messages, [destination_id, created_at], :order => { created_at => desc },:where => read_at IS NULL Limit -> Index Scan using messages_between_users on messages Index Cond: ((source_id = ?) AND (destination_id = ?)) Total runtime: 0.209 ms Limit -> Index Scan using messages_unread on messages Index Cond: (destination_id = ?) Total runtime: 0.183 msвоскресенье, 16 декабря 12 г.
  26. 26. Массивы и hstore • Как сериализация, только лучше • Могут индексироватьсявоскресенье, 16 декабря 12 г.
  27. 27. Размер таблиц create_table users_usual do |t| t.boolean flag1 ... t.boolean flag20 end create_table users_hstore do |t| t.hstore flags # gem activerecord-postgres-hstore end create_table users_array do |t| t.integer_array flags # gem activerecord-postgres-array end 5 000 000 записей, флаги независимы, P[flag=yes] = 0.01 Usual table size: 249 MB Hstore table size: 219 MB Array table size: 257 MBвоскресенье, 16 декабря 12 г.
  28. 28. Индексирование Поля: Seq Scan on users_usual Filter: (flag2 AND flag7 AND flag13) Total runtime: 799.959 ms Hstore: Bitmap Heap Scan on users_hstore Recheck Cond: (flags @> 2=>y, 7=>y, 13=>y::hstore) -> Bitmap Index Scan on users_hstore_flags Index Cond: (flags @> 2=>y, 7=>y, 13=>y::hstore) Total runtime: 350.778 ms Массив: Bitmap Heap Scan on users_array Recheck Cond: (flags @> {2,7,13}::integer[]) -> Bitmap Index Scan on users_array_flags Index Cond: (flags @> {2,7,13}::integer[]) Total runtime: 48.118 msвоскресенье, 16 декабря 12 г.
  29. 29. Кэширование последовательностей • Выбираем последовательность на несколько шагов вперед • Кэшируем идентификаторы в Redis id = redis.lpop(cache_key) # Get next value from cache unless id # No cached value ids = connection.select_values some_heavy_scope.select(id).to_sql id = ids.shift redis.multi do |r| ids.each{ |id| r.rpush cache_key, id } r.expire cache_key, 7200 # Expire cache after 2 hours end endвоскресенье, 16 декабря 12 г.
  30. 30. воскресенье, 16 декабря 12 г.
  31. 31. Кэширование матрицы • Проблема • Различные фильтры: мин/макс возраст (от 16 до 70) + пол • 3080 возможных фильтров • (1 + 2 + … + 55) * 2 = 55 * 56 • Решение: аппроксимация фильтроввоскресенье, 16 декабря 12 г.
  32. 32. Кэширование матрицы • Проблема • Различные фильтры: мин/макс возраст (от 16 до 70) + пол • 3080 возможных фильтров • (1 + 2 + … + 55) * 2 = 55 * 56 • Решение: аппроксимация фильтроввоскресенье, 16 декабря 12 г.
  33. 33. Кэширование с аппроксимацией • Возраст округляется до кратного X (= 4) • Минимальный - вниз, максимальный - вверх • 210 фильтров • (1 + 2 + … + 14) * 2 = 14 * 15 • Кэшируется порция заведомо большего размера • После извлечения из кэша выкидываются лишние записивоскресенье, 16 декабря 12 г.
  34. 34. Обработка фотографий • 200–300 регистраций в минуту, половина грузит JPG на 5 мегабайт • Первым делом уменьшайте размер входящих изображений • CarrierWave лучше отделён от модели, чем Paperclip, обратно совместимвоскресенье, 16 декабря 12 г.
  35. 35. Обработка фотографий • RMagick MiniMagick не хранит в себе временного файла, использует память отдельного процесса, не поддерживает создание изображений • GraphicsMagick — форк ImageMagick, ориентированный на стабильность и производительность • Прирост в скорости до 2-3 раз, но это не серебрянная пуля: меньше фич, иногда производительность страдаетвоскресенье, 16 декабря 12 г.
  36. 36. Отправка СМС • SMPP – открытый протокол, поддерживаемый большинством SMS-шлюзов • Бинарный, за счет чего выше скорость передачи и footprint воркеров • github.com/raykrueger/ruby-smpp – реализация для EventMachineвоскресенье, 16 декабря 12 г.
  37. 37. Тестирование • Модульное • Ruby — RSpec • Erlang — EUnit • Интеграционное? • Нагрузочное?воскресенье, 16 декабря 12 г.
  38. 38. RSpec для Rails и Erlang • Запуск Erlang при создании сессии • Отдельный поток с EM, обслуживающий все соединения • Socket.io поверх em-websocket-client • Очередь входящих сообщений s = open_session_with_chat # Delegates to ActionDispatch::Integration::Session s.post "/users/sign_in", email: 123@example.com, pass: 12345 # Wait for a message (with timeout) s.receive(:connect).should be # Send message s.send_event :contact_message, contact_id, text: "Hi!"воскресенье, 16 декабря 12 г.
  39. 39. Боты-тестеры • Нагрузочное тестирование чата • Определение проблем с concurrency • Помощь при ручном тестировании • Настраиваемое поведениевоскресенье, 16 декабря 12 г.
  40. 40. Реализация ботов • Акторы на основе EventMachine • Socket.io поверх em-websocket-client • Набор "шаблонов поведения" class Caller < Wannafun::Actor behave :get_matrix behave :accept_calls behave :call_to_users behave :talk_in_calls end EventMachine.run do Wannafun::ActorSet.new(Caller, options).start! endвоскресенье, 16 декабря 12 г.
  41. 41. Как ловить JS ошибки на клиенте • В сложных приложениях — сложные сценарии и граничные случаи • Обратная связь пользователь - разработчик. Максимум информации собирается автоматически • Echoes.js (github.com/kossnocorp/echoes). На клиенте собираем логи, фильтруем важные и прикладываем к запросувоскресенье, 16 декабря 12 г.
  42. 42. Echoes.js echo.log(Test, logging, namespace: app.lol_module.45) echo.log([trololo]) [ { "timestamp": 1341468018606, "body": ["Test", "logging"], "namespace": "app.lol_module.45" }, { "timestamp": 1341468018606, "body": [["trololo"]], "namespace": "" } ] echo.logs.grep some #=> [{ body: [Something] }, { body: [I want some LSD.]}]воскресенье, 16 декабря 12 г.
  43. 43. Мониторинг приложения • Длины очередей в Resque • Кол-во несохраненных сообщений • Кол-во знакомств в разных состояниях • Длина очереди модерациивоскресенье, 16 декабря 12 г.
  44. 44. Head-huntung Wannafun: red.scorpix@gmail.com Evil Martians: surrender@evl.msвоскресенье, 16 декабря 12 г.
  45. 45. ВПРСВ НТ? ЗБС! Алексей Носков Алексей Найден @alno @alexnayden github.com/alno github.com/anayden alexey.noskov@evl.ms alexey.nayden@evl.ms Все изображения являются собственностью их автороввоскресенье, 16 декабря 12 г.

×