Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

21
Незаурядная Java как инструмент разработки высоконагруженного сервера Андрей Паньгин ведущий разработчик проекта Одноклассники

description

Презентация с технической секции #BitByte - фестиваля профессионального развития, который прошел 19 мая в Санкт-Петербурге. Андрей Паньгин, Ведущий инженер проекта компании Mail.Ru Group «Одноклассники»: «Незаурядная Java как инструмент разработки высоконагруженного сервера».

Transcript of Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Page 1: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Незаурядная Java как инструмент разработки высоконагруженного сервера

Андрей Паньгинведущий разработчикпроекта Одноклассники

Page 2: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Серверы Одноклассников

2

• Всего >3000 серверов– Web, Business logic, Download, Storage,

Remote Service…– Все ПО написано на Java

• Высоконагруженный сервер– 20 тыс. одновременных подключений– 30 тыс. запросов в секунду– Трафик до 1 Gb/s

Page 3: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Типичный Java Server

3

• java.net I/O (Sockets)– Поток на каждое соединение– Максимум 10 тыс. потоков

• NIO (SocketChannels)– Direct buffers– Selector– Неблокирующий ввод-вывод

• Event-driven NIO frameworks– Apache MINA– Netty

Page 4: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Remote Service

4

• Компоненты портала как отдельные сервисы– Система сообщений, граф дружб, поиск, лента и др.

• Внутренняя коммуникация между сервисами• RPC сценарий

Read RequestRead Request

Deserialize argumentsDeserialize arguments

Invoke methodInvoke method

Serialize resultSerialize result

Send responseSend response

Object result = method.invoke(args);

byte[] request = connection.read(…);

request Method method, Object[] args

result byte[] response

connection.write(response);

Page 5: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Socket I/O Netty

5

• Конструирование запроса по частям• Влияние на GC• Снижение производительности

– Среднее время запроса +20%

Page 6: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Blocking socket selector (BLOS)

6

• Сочетает преимущества Selector + Blocking I/O• Возможно в OS, но не поддерживается в Java

• Реализуется посредством JNI– Простые Java-обертки над системными вызовами

Linux epollSolaris /dev/pollBSD kqueueWindows I/O completion ports

Page 7: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Java Socket Native

7

• java.net.Socket java.net.SocketImpl impl java.io.FileDescriptor fd int fd

Object getField(Object holder, Class cls, String name) {

Field f = cls.getDeclaredField(name);

f.setAccessible(true);

return f.get(holder);

}

Page 8: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Архитектура BLOS сервера

8

• 1 acceptor thread• N selector threads (обычно N = кол-во CPU)• Динамический пул потоков-исполнителей• Запрос исполняется непрерывно до конца

Acceptor threadAcceptor thread

Selector 1Selector 1

Selector 2Selector 2

readread deserializedeserialize invokeinvoke serializeserialize sendsend

readread deserializedeserialize invokeinvoke serializeserialize sendsend

Worker thread pool

Page 9: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Проблемы работы с сетью в Java

9

• java.net.Socket– Нет поддержки Direct Buffers– Finalizers => GC impact

• NIO не спасает– Не срабатывают I/O timeouts– Возможна утечка нативной памяти

Page 10: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Решение

10

• И снова JNI– Нет finalize()– Время GC до 10 раз меньше– Необходимость закрывать сокеты вручную

• В Tomcat используется похожий подход– APR (Apache Portable Runtime)

Page 11: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Remote Service

11

• RPC сценарий

• Эффективность сериализации –ключ к производительности RPC– Затрачиваемое на сериализацию время– Размер передаваемых по сети данных

Read RequestRead Request

Deserialize argumentsDeserialize arguments

Invoke methodInvoke method

Serialize resultSerialize result

Send responseSend response

Page 12: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Сериализация

12

• Built-in Java Serialization– Слишком медленная– Большой объем получаемых данных

• JBoss Serialization– Не поддерживает модификацию классов

• Ручная сериализация– Не вариант (тысячи классов!)

Page 13: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Требования к сериализации

13

• Быстрая• Компактная• Обрабатывает простые изменения

внутри класса– Добавление поля– Удаление поля– Изменение порядка полей– Изменение типа поля (int long)

Page 14: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Архитектура сериализации (1/2)

14

• Типы сериализаторов– Встроенные (примитивные типы, обертки, массивы)– Collection и Map– Сериализация произвольных классов по полям– Самосериализуемые классы (readObject / writeObject)

• Каждому сериализуемому классу сопоставляется уникальный 64-битный ID –хеш от имен, типов и порядка полей

Page 15: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Архитектура сериализации (2/2)

15

• Запись объекта:1. Serializer serializer = Repository.getByClass(obj.getClass());

2. writeLong(serializer.uid);

3. Serializer.write(obj, this);

• Чтение объекта:1. long uid = readLong();

2. Serializer serializer = Repository.getById(uid); may throw SerializerNotFoundException

3. Object obj = serializer.read(this);

Page 16: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Трудности реализации

16

• Чтение/запись private полей чужих объектов– Reflection (медленно!)– sun.misc.Unsafe

• Создание объектов без вызова конструктора– sun.misc.Unsafe.allocateInstance()– ReflectionFactory.newConstructorForSerialization()– Динамическая генерация байткода

Page 17: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Unsafe

17

import sun.misc.Unsafe;

private static Unsafe getUnsafe() throws Exception {

Field f = Unsafe.class.getDeclaredField("theUnsafe");

f.setAccessible(true);

return (Unsafe) f.get(null);

}

// Instantiate class X without calling X's constructor

Object x = unsafe.allocateInstance(X.class);

// Read private field f of object x

long offset = unsafe.objectFieldOffset(f);

Object result = unsafe.getObject(x, offset);

Page 18: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Генерация байткода

18

1. Создать свой ClassLoader

2. Построить массив byte[] с бинарным представлением класса

3. Вызвать ClassLoader.defineClass()

• ASM framework (http://asm.ow2.org)– Построить представление класса с помощью

org.objectweb.asm.ClassWriter– Преобразовать его в массив byte[]:

ClassWriter.toByteArray()

Page 19: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

А как же private поля?

19

• JVM при загрузке нового класса верифицирует байткод

• Если класс унаследован от sun.misc.MagicAccessorImpl,верификатор не будет проверять права доступа для байткодов getfield и putfield

Page 20: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Производительность

20

• Среднее время запроса: -20%• Сетевой трафик: -50%• Продолжительность GC: -30%• Количество потоков: 4000 200

Page 21: Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин

Спасибо!

21

• Примеры– https://github.com/odnoklassniki/rmi-samples.git

• Контакты– [email protected]– www.odnoklassniki.ru/ap