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

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

Jun 16, 2015

Download

Documents

Андрей Паньгин рассказывает о трюках, которые позволяют нам делать высоконагруженные сетевые серверы на Java
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Незаурядная Java как инструмент разработки высоконагруженного сервера

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

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

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

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

1

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

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

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

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

Типичный Java Server

2

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

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

• Event-driven NIO frameworks– Apache MINA– Netty

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

Remote Service

3

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

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

Read Request

Deserialize arguments

Invoke method

Serialize result

Send response

Object result = method.invoke(args);

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

request � Method method, Object[] args

result � byte[] response

connection.write(response);

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

Socket I/O � Netty

4

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

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

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

Blocking socket selector (BLOS)

5

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

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

Linux epoll

Solaris /dev/poll

BSD kqueue

Windows I/O completion ports

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

Java Socket � Native

6

• java.net.Socketjava.net.SocketImpl impl

java.io.FileDescriptor fdint fd

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

Field f = cls.getDeclaredField(name);

f.setAccessible(true);

return f.get(holder);

}

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

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

7

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

Acceptor thread

Selector 1

Selector 2

read deserialize invoke serialize send

read deserialize invoke serialize send

Worker thread pool

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

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

8

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

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

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

Решение

9

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

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

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

Remote Service

10

• RPC сценарий

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

Read Request

Deserialize arguments

Invoke method

Serialize result

Send response

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

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

11

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

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

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

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

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

12

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

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

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

13

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

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

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

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

14

• Запись объекта: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: Незаурядная Java как инструмент разработки высоконагруженного сервера

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

15

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

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

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

Unsafe

16

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 construc tor

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: Незаурядная Java как инструмент разработки высоконагруженного сервера

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

17

1. Создать свой ClassLoader2. Построить массив byte[] с бинарным представлением класса

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

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

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

ClassWriter.toByteArray()

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

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

18

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

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

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

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

19

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

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

Спасибо!

20

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

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