49
Разработка высоконагруженного сервера на Java Андрей Паньгин Одноклассники, ведущий инженер

Developing highload servers with Java

Embed Size (px)

Citation preview

Page 1: Developing highload servers with Java

Разработка высоконагруженного сервера на Java

Андрей ПаньгинОдноклассники, ведущий инженер

Page 2: Developing highload servers with Java

План доклада• Архитектура ОК• Работа с сетью• Удалённые вызовы и сериализация• Кеширование• Паузы JVM, оптимизация GC

Page 3: Developing highload servers with Java

Факты об ОК• 8000 серверов• 48 млн. уникальных пользователей в день• 8 млн. пользователей онлайн• 300 000 web запросов в секунду• 4 ПБ данных (без учёта дублирования)

Page 4: Developing highload servers with Java

Экстремальные нагрузки• На что способен один сервер – 50,000 вызовов/с в сек (push delivery)– 40 Гбит/с (video download)– 100,000 пользователей онлайн (xmpp)– 384 ГБ в памяти (cache)

• Это всё Java!

Page 5: Developing highload servers with Java

Архитектура ОК

Web

Web

Mobile

Mobile

API

API

Business logic

Business logic

Business logic

Business logic

Business logic

Business logic

GraphGraphGraph

GraphGraphMessagingGraphGraphPhoto

GraphGraphFeed

BLOBstorage

SQL servers

Cassandra

ServicesBusiness layerFront-end

Storages

HTTP

RPC

RPC

Page 6: Developing highload servers with Java

Внутренняя коммуникация• Удалённый вызов методов (RPC)

Business logicserver

Graph cluster

User servicecluster

long[] friendIds = getConnections (userId)

List<User> friends = getUsers (friendIds)

Page 7: Developing highload servers with Java

План доклада• Архитектура ОК• Работа с сетью• Удалённые вызовы и сериализация• Кеширование• Паузы JVM, оптимизация GC

Page 8: Developing highload servers with Java

java.net.Socket I/O• Поток на каждое соединение

InputStream in = socket.getInputStream();OutputStream out = socket.getOutputStream();

while (true) { int bytesRead = in.read(...); // blocking call if (bytesRead > 0) { byte[] response = processRequest(...); out.write(response); // blocking call }}

Page 9: Developing highload servers with Java

Проблемы Socket I/O• 10 тыс. соединений = 10 тыс. потоков• Finalizers => утечка памяти• byte[] массивы, копирование

Page 10: Developing highload servers with Java

NIO

while (true) { if (selector.select() > 0) { // blocking call for (SelectionKey key : selector.selectedKeys()) { if (key.isReadable()) { doRead(key); } else if (key.isWritable()) { doWrite(key); } } selector.selectedKeys().clear(); }}

• Selector + неблокирующие read/write

Page 11: Developing highload servers with Java

NIO frameworks• Apache MINA– http://mina.apache.org

• Netty– http://netty.io

• Основаны на NIO• Event-driven модель

Page 12: Developing highload servers with Java

Проблемы NIO• Selector глючный, не потокобезопасный• Нельзя делать select() на blocking сокетах• Не работает setSoTimeout()• Ограниченная поддержка SSL/TLS

• Не поддерживаются все возможности ОС– TCP опции: TCP_DEFER_ACCEPT, TCP_CORK– Флаги send/recv: MSG_MORE, MSG_PEEK

Page 13: Developing highload servers with Java

Решение• JNI библиотека– Tomcat Native (APR connector)– one-nio

• Управление сокетами вручную• Поддержка OpenSSL

Page 14: Developing highload servers with Java

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

Acceptor thread

Selector 1

Selector N

Worker thread pool

NCPU

read

read

process write

process write

Page 15: Developing highload servers with Java

Пример сервераpublic class MyHttpServer extends HttpServer {

public MyHttpServer() throws IOException { super(new ConnectionString("https://0.0.0.0")); }

@Path("/hello") public Response hello() { return Response.ok("Hello world"); }

public static void main(String[] args) throws Exception { new MyHttpServer().start(); }}

Page 16: Developing highload servers with Java

Пример клиентаpublic class MyHttpClient {

public static void main(String[] args) throws Exception { HttpClient client = new HttpClient("https://localhost");

Response response = client.get("/hello"); System.out.println("Status: " + response.getStatus()); System.out.println(response.toString());

client.close(); }}

Page 17: Developing highload servers with Java

План доклада• Архитектура ОК• Работа с сетью• Удалённые вызовы и сериализация• Кеширование• Паузы JVM, оптимизация GC

Page 18: Developing highload servers with Java

RPC сценарийMethod m, Object[] args

Object result

long[] friendIds = graph.getFriends(userId);

Page 19: Developing highload servers with Java

Method m, Object[] args

byte[] request

Method m, Object[] args

Object result

byte[] response

Object result

Исполнение

Десериализация

Десериализация

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

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

Клиент Сервер

0.4 мс

Page 20: Developing highload servers with Java

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

• Быстрая, компактная• Минимум ручной работы• Поддержка эволюции– Java Serialization– JBoss– Thrift, Avro, Protobuf– Kryo

Page 21: Developing highload servers with Java

one.nio.serial• Массивы и коллекции– count obj1 … objN

• Мапы– count key1 value1 … keyN valueN

• Enum– short ordinal()

• Externalizable– readExternal / writeExternal

• Остальные Serializable– non-static non-transient fields

Page 22: Developing highload servers with Java

Схема сериализации• Serializer– Long UID – хеш от имён, типов и порядка полей

• Repository– Map<Class, Serializer> для сериализации– Map<Long, Serializer> для десериализации

class Person { String name = "Victor"; int yearOfBirth = 1980; boolean married = true;}

UID 19806 V i c t o r 1-18

String UID

Page 23: Developing highload servers with Java

Обмен схемами1. Client Server: request2. Server throws SerializerNotFoundException ?– Client Server: provideSerializer(serializer)– Goto 1

3. Deserialize response4. Client throws SerializerNotFoundException ?– Client Server: requestSerializer(uid)– Add to repository– Goto 3

Page 24: Developing highload servers with Java

Особенности реализации на Java• Чтение и запись private полей– Reflection (медленно!)– sun.misc.Unsafe

• Создание экземпляров класса– sun.misc.Unsafe.allocateInstance()– Генерация байткода: http://asm.ow2.org

• Обход Java верификатора– Наследование sun.reflect.MagicAccessorImpl

Page 25: Developing highload servers with Java

План доклада• Архитектура ОК• Работа с сетью• Удалённые вызовы и сериализация• Кеширование• Паузы JVM, оптимизация GC

Page 26: Developing highload servers with Java

Числа, которые нужно знатьL1 cache reference 0.5 ns

Main memory reference 100 ns

Compress 1K bytes w/ cheap algorithm 3,000 ns

Send 2K bytes over 1 Gbps network 20,000 ns

Read 1 MB sequentially from memory 250,000 ns

Round trip within same datacenter 500,000 ns

Read 1 MB sequentially from network 10,000,000 ns

Read 1 MB sequentially from disk 30,000,000 ns

Send packet CA->Netherlands->CA 150,000,000 ns

Page 27: Developing highload servers with Java

Кеширование• Что кешировать?– Данные из медленного хранилища (БД)– Результаты вычислений

• Где кешировать?– Java Heap (не подходит для объемов > 10 GB)– Off-heap memory

Page 28: Developing highload servers with Java

Как выйти за пределы Heap• Native код (JNI обертки над malloc / free)– Платформозавимый

• ByteBuffer.allocateDirect– Размер буфера ≤ 2 GB– Освобождается автоматически при GC– Освобождение вручную:

((sun.nio.ch.DirectBuffer) buf).cleaner().clean();• Unsafe.allocateMemory / freeMemory

Page 29: Developing highload servers with Java

Решения для off-heap кешей• JSR 107: javax.cache– Ehcache, Coherence

• Apache DirectMemory• MapDB• Chronicle Map• one-nio

Page 30: Developing highload servers with Java

Требования к кешам• Ключи и значения произвольных типов– Long, String, byte[], сериализованные объекты

• Быстродействие• Атомарные операции: read-modify-write• До 384 GB RAM, до 100 млн. объектов• Экспирация по времени• Вытеснение LRU• Персистентность

Page 31: Developing highload servers with Java

Персистентность• Решает проблему холодного старта• 2 уровня– Сохранение между перезапусками приложения– Снимки на диске (snapshots)

• Создание снимков– Stop-the-world– По сегментам– Copy-on-write (fork trick)

Page 32: Developing highload servers with Java

Shared Memory• Механизм IPC– Linux: /dev/shm– Поддерживается sendfile()

• Создание объекта Shared Memory в Java– new RandomAccessFile("/dev/shm/cache", "rw");

• Отображение в адресное пространство:– FileChannel.map() MappedByteBuffer– 2GB, no unmapping

Page 33: Developing highload servers with Java

Mapping > 2GB

// MappingMethod map0 = FileChannelImpl.class.getDeclaredMethod( "map0", int.class, long.class, long.class);map0.setAccessible(true);long addr = (Long) map0.invoke(f.getChannel(), 1, 0L, f.length());

// UnmappingMethod unmap0 = FileChannelImpl.class.getDeclaredMethod( "unmap0", long.class, long.class);unmap0.setAccessible(true);unmap0.invoke(null, addr, length);

Page 34: Developing highload servers with Java

Проблема абсолютных адресов• Только относительная адресация– Хранение смещений вместо адресов

• Relocation– Сдвиг всех абсолютных адресов на старте

Page 35: Developing highload servers with Java

Распределение памяти• Doug Lea’s malloc• one.nio.mem.Malloc, MallocMT

16 24 32 48 … 256 384 … 1G

sz sz sz sz sz sz

Page 36: Developing highload servers with Java

Кеши в one-nio

SharedMemoryLongMapLong V

SharedMemoryStringMapString V

SharedMemoryBlobMapLong byte[]

OffheapBlobMapbyte[] byte[]

OffheapMapK V

SharedMemoryMapK V

Page 37: Developing highload servers with Java

Устройство кеша

0 addr 0addr…

hashCode(key) % capacity

hash time nextkey (?)

value

hash time 0key (?)

value

0

hash time 0key (?)

value

entry

Page 38: Developing highload servers with Java

Возможности кеша• Потокобезопасность– get, put, remove, lockRecordForRead/Write

• Прямой доступ к off-heap, сериализация• Поддержка shared memory• Стратегии очистки– BasicCleanup (TTL), SamplingCleanup (LRU)

Page 39: Developing highload servers with Java

План доклада• Архитектура ОК• Работа с сетью• Удалённые вызовы и сериализация• Кеширование• Паузы JVM, оптимизация GC

Page 40: Developing highload servers with Java

Паузы JVM• Требования к latency– хорошо: 100 мс– плохо: 1 с

• GC– ParNew– CMS (initial mark, remark)

• G1 GC– -XX:MaxGCPauseMillis=200– Много улучшений в 8u40

Page 41: Developing highload servers with Java

Тюнинг GC

-XX:+PrintGCDetails-XX:+PrintGCApplicationStoppedTime

-XX:+PrintClassHistogramBeforeFullGC-XX:+PrintClassHistogramAfterFullGC-XX:+PrintPromotionFailure

Page 42: Developing highload servers with Java

Тюнинг GC

-XX:+UseConcMarkSweepGC-XX:+ExplicitGCInvokesConcurrent-XX:+CMSClassUnloadingEnabled (default in 8)

-XX:+UnlockDiagnosticVMOptions-XX:ParGCCardsPerStrideChunk=32768

Page 43: Developing highload servers with Java

Тюнинг GC

-XX:+UseCMSInitiatingOccupancyOnly-XX:CMSInitiatingOccupancyFraction=75

-XX:CMSWaitDuration=10000-XX:+CMSScavengeBeforeRemark

-XX:+ParallelRefProcEnabled-XX:+CMSParallelInitialMarkEnabled (default in 8)

Page 44: Developing highload servers with Java

Паузы JVM• VM operations– Thread dump, heap dump, debugging– Biased lock revocation (-XX:-UseBiasedLocking)– Deoptimization

-XX:+PrintSafepointStatistics-XX:PrintSafepointStatisticsCount=1

Page 45: Developing highload servers with Java

Safepoint sync• Непрерываемые операции– System.arraycopy(), clone(), ByteBuffer.get()– MappedByteBuffer I/O

-XX:+SafepointTimeout-XX:SafepointTimeoutDelay=1000

Page 46: Developing highload servers with Java

GC-friendly структуры

class Entry { long key; Object value;}

Entry[] entries;

long[] keys;Object[] values;

class Blob { long offset; int length; byte[] sha1_hash;}

class Blob { long offset; int length; int hash0, hash1, hash2, hash3, hash4;}

Page 47: Developing highload servers with Java

GC-friendly структуры

class Props { Map<String, String> map;}

class Props extends HashMap<String, String>

• String char[] или byte[]• ByteBuffer wrappers

Page 48: Developing highload servers with Java

GC-friendly структуры• LinkedList ArrayList• HashMap open-address hash table• Коллекции примитивов (Trove)

• BitSet(100 000) 100 x BitSet(1000)

Page 49: Developing highload servers with Java

Спасибо!• Наш Open Source– https://github.com/odnoklassniki

• Блог– http://habrahabr.ru/company/odnoklassniki/blog/

• Контакты– [email protected]

• Работа в ОК– http://v.ok.ru