Технические аспекты знакоства с девушкой в Интернете
Upcoming SlideShare
Loading in...5
×
 

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

on

  • 2,780 views

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

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

Statistics

Views

Total Views
2,780
Views on SlideShare
2,625
Embed Views
155

Actions

Likes
12
Downloads
29
Comments
0

5 Embeds 155

https://twitter.com 147
https://twimg0-a.akamaihd.net 3
http://twitter.com 2
https://si0.twimg.com 2
http://eventifier.co 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution-ShareAlike LicenseCC Attribution-ShareAlike License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

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

  • Технические аспекты знакомства с девушкой в интернете Алексей Найден, Алексей Носков Evil Martiansвоскресенье, 16 декабря 12 г.
  • воскресенье, 16 декабря 12 г.
  • воскресенье, 16 декабря 12 г.
  • Wannafun.ru • Онлайн speed dating • Знакомство только с теми, кто в сети • Поток лиц aka «матрица» • 3 минуты чата для принятия решения • 48% / 52%воскресенье, 16 декабря 12 г.
  • Проект в цифрах • 500 000 пользователей • 2000 онлайн • 2-3 события в секунду на юзера • Более 100 http-запросов в минуту на юзера • Более 5 000 000 чатов • Более 45 000 000 сообщенийвоскресенье, 16 декабря 12 г.
  • На чем всё работает Erlang Postgres Redis EventMachine Rails Resqueвоскресенье, 16 декабря 12 г.
  • воскресенье, 16 декабря 12 г.
  • Онлайн-взаимодействия • Знакомства: входящие/исходящие, сообщения • Контакт-лист: личные сообщения, онлайн/ оффлайн • Уведомлениявоскресенье, 16 декабря 12 г.
  • Взаимодействие с браузером Pusher (http://pusher.com/) Faye (http://faye.jcoglan.com/) Push-канал к клиенту Pub/sub Нет серверной логики Нет серверной логики Socket.io (http://socket.io/) Абстракция над WebSocket, Flash и Polling Произвольная серверная логикавоскресенье, 16 декабря 12 г.
  • Серверная реализация socket.io • NodeJS • На тестах падала VM — epic fail • EventMachine • Не было актуальной версии • Erlang • Не было актуальной версиивоскресенье, 16 декабря 12 г.
  • Что хорошего в Erlang • Нет коллбеков - простой последовательный код • Нет разделяемого состояния, структуры данных неизменяемы - concurrency проще • Иерархия супервизоров - высокая устойчивость • Прозрачная распределенность • Бесшовный деплойвоскресенье, 16 декабря 12 г.
  • Архитектура чат-сервера • Соединения обслуживаются Cowboy • Каждая сессия - отдельный процесс • Вспомогательные процессы для работы с БД, Redisвоскресенье, 16 декабря 12 г.
  • Начало знакомствавоскресенье, 16 декабря 12 г.
  • Одновременный ответвоскресенье, 16 декабря 12 г.
  • Синхронная реализациявоскресенье, 16 декабря 12 г.
  • воскресенье, 16 декабря 12 г.
  • Очереди задач • Resque • Быстро работает, использует Redis • Удобный Web UI • Redis полезен и для других задач: • Хранение счетчиков • Синхронизация состояния • Кэшированиевоскресенье, 16 декабря 12 г.
  • Уникальные задачи • Сохранение сообщений • Прогрев кэша • Расчет статистики Можно использовать Redis для блокировкивоскресенье, 16 декабря 12 г.
  • Обычный код def perform return unless redis.setnx("lock", true) # do task actions ensure redis.del "lock" end Реализация: resque-lock (<= 1.0.0)воскресенье, 16 декабря 12 г.
  • воскресенье, 16 декабря 12 г.
  • Правильный код 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 г.
  • воскресенье, 16 декабря 12 г.
  • Хорошие индексы Хорошие = Ускоряющие необходимые запросы 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 г.
  • Плохие индексы # 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 г.
  • Отличные индексы! # 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 г.
  • Массивы и hstore • Как сериализация, только лучше • Могут индексироватьсявоскресенье, 16 декабря 12 г.
  • Размер таблиц 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 г.
  • Индексирование Поля: 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 г.
  • Кэширование последовательностей • Выбираем последовательность на несколько шагов вперед • Кэшируем идентификаторы в 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 г.
  • воскресенье, 16 декабря 12 г.
  • Кэширование матрицы • Проблема • Различные фильтры: мин/макс возраст (от 16 до 70) + пол • 3080 возможных фильтров • (1 + 2 + … + 55) * 2 = 55 * 56 • Решение: аппроксимация фильтроввоскресенье, 16 декабря 12 г.
  • Кэширование матрицы • Проблема • Различные фильтры: мин/макс возраст (от 16 до 70) + пол • 3080 возможных фильтров • (1 + 2 + … + 55) * 2 = 55 * 56 • Решение: аппроксимация фильтроввоскресенье, 16 декабря 12 г.
  • Кэширование с аппроксимацией • Возраст округляется до кратного X (= 4) • Минимальный - вниз, максимальный - вверх • 210 фильтров • (1 + 2 + … + 14) * 2 = 14 * 15 • Кэшируется порция заведомо большего размера • После извлечения из кэша выкидываются лишние записивоскресенье, 16 декабря 12 г.
  • Обработка фотографий • 200–300 регистраций в минуту, половина грузит JPG на 5 мегабайт • Первым делом уменьшайте размер входящих изображений • CarrierWave лучше отделён от модели, чем Paperclip, обратно совместимвоскресенье, 16 декабря 12 г.
  • Обработка фотографий • RMagick MiniMagick не хранит в себе временного файла, использует память отдельного процесса, не поддерживает создание изображений • GraphicsMagick — форк ImageMagick, ориентированный на стабильность и производительность • Прирост в скорости до 2-3 раз, но это не серебрянная пуля: меньше фич, иногда производительность страдаетвоскресенье, 16 декабря 12 г.
  • Отправка СМС • SMPP – открытый протокол, поддерживаемый большинством SMS-шлюзов • Бинарный, за счет чего выше скорость передачи и footprint воркеров • github.com/raykrueger/ruby-smpp – реализация для EventMachineвоскресенье, 16 декабря 12 г.
  • Тестирование • Модульное • Ruby — RSpec • Erlang — EUnit • Интеграционное? • Нагрузочное?воскресенье, 16 декабря 12 г.
  • 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 г.
  • Боты-тестеры • Нагрузочное тестирование чата • Определение проблем с concurrency • Помощь при ручном тестировании • Настраиваемое поведениевоскресенье, 16 декабря 12 г.
  • Реализация ботов • Акторы на основе 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 г.
  • Как ловить JS ошибки на клиенте • В сложных приложениях — сложные сценарии и граничные случаи • Обратная связь пользователь - разработчик. Максимум информации собирается автоматически • Echoes.js (github.com/kossnocorp/echoes). На клиенте собираем логи, фильтруем важные и прикладываем к запросувоскресенье, 16 декабря 12 г.
  • 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 г.
  • Мониторинг приложения • Длины очередей в Resque • Кол-во несохраненных сообщений • Кол-во знакомств в разных состояниях • Длина очереди модерациивоскресенье, 16 декабря 12 г.
  • Head-huntung Wannafun: red.scorpix@gmail.com Evil Martians: surrender@evl.msвоскресенье, 16 декабря 12 г.
  • ВПРСВ НТ? ЗБС! Алексей Носков Алексей Найден @alno @alexnayden github.com/alno github.com/anayden alexey.noskov@evl.ms alexey.nayden@evl.ms Все изображения являются собственностью их автороввоскресенье, 16 декабря 12 г.