SlideShare a Scribd company logo
Особенности разработки
высоконагруженного сервера на Java



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

• Всего 5000 серверов
  – Web, Business logic, Download, Storage…
  – Все ПО написано на Java


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



                                              1
Remote Service

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

                   long[] friendIds = getConnections (userId)
                                                                Graph cluster
  Business logic
     server
                   List<User> friends = getUsers (friendIds)
                                                                User service
                                                                  cluster


                                                                                2
RPC сценарий

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


   Deserialize arguments   request → Method method, Object[] args


      Invoke method        Object result = method.invoke(args);


      Serialize result     result → byte[] response


      Send response        connection.write(response);



                                                                    3
java.net I/O (Sockets)

• Поток на каждое соединение
• Максимум 10 тыс. потоков

  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
      }
  }



                                                        4
NIO (SocketChannels)
• Selector, неблокирующие read / write
• Direct ByteBuffers
    while (true) {
        if (selector.select() > 0) { // blocking call
            Iterator<SelectionKey> iterator =
                selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                if (key.isReadable()) {
                    doRead(key);
                } else if (key.isWritable()) {
                    doWrite(key);
                }
                iterator.remove();
            }
        }
    }
                                                        5
NIO frameworks

• Apache MINA
  – http://mina.apache.org
• Netty
  – https://netty.io


• Основаны на NIO
• Событийно-управляемая (event-driven) модель




                                            6
Socket I/O → Netty

• Плюсы
  + Сокращение числа потоков (4000 → 200)

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




                                            7
Blocking socket selector (BLOS)

• Сочетает преимущества Selector + Blocking I/O
• Возможно в OS, но не поддерживается в Java
       Linux     epoll
       Solaris   /dev/poll
       BSD       kqueue
       Windows   I/O completion ports


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

                                                   8
Java Socket → Native

• 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);
   }




                                                          9
Архитектура BLOS сервера
•   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

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

• java.net.Socket
  – Finalizers => GC impact
    (объекты SocketImpl не собираются до Full GC)
  – Нет поддержки Direct Buffers


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



                                                    11
Решение

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


• В Tomcat используется похожий подход
  – APR (Apache Portable Runtime)
    http://tomcat.apache.org/tomcat-7.0-doc/apr.html



                                                       12
Remote Service
• RPC сценарий
                Read Request

             Deserialize arguments

                Invoke method

                Serialize result

                Send response


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

• Built-in Java Serialization
   – Слишком медленная
   – Большой объем получаемых данных
• JBoss Serialization
   – Не поддерживает разные версии классов
• Avro, Thrift, Protocol Buffers
• Ручная сериализация – Externalizable
   – Не вариант (тысячи классов!)



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

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



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

• Типы сериализаторов
  – Встроенные (базовые типы, массивы, коллекции)
  – Генерируемые по полям класса
• Сериализатор класса имеет уникальный ID –
  64-битный хеш от имен, типов и порядка полей

  class Event {
    String name = “jug.msk.ru”;
    int year = 2013;              UID   10 j u g . m s k . r u 2013 1
    boolean started = true;
  }


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

• Запись объекта:
  1. Serializer serializer = Repository.getByClass(obj.getClass());
  2. outputStream.writeLong(serializer.uid);
  3. serializer.write(obj, outputStream);


• Чтение объекта:
  1. long uid = inputStream.readLong();
  2. Serializer serializer = Repository.getById(uid);
        may throw SerializerNotFoundException
  3. Object obj = serializer.read(inputStream);



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

• Чтение и запись private полей чужих объектов




• Создание экземпляров класса




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

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


• Создание экземпляров класса
  – sun.misc.Unsafe.allocateInstance()
  – Динамическая генерация байткода




                                             19
Unsafe
  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
  // As simple as malloc()
  Object x = unsafe.allocateInstance(X.class);

  // Read private field f of object x
  long offset = unsafe.objectFieldOffset(f);
  Object result = unsafe.getObject(x, offset);


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

1. Построить массив byte[] с бинарным
   представлением класса
2. Вызвать ClassLoader.defineClass()
   ClassLoader должен быть унаследован


• ASM framework (http://asm.ow2.org)
  – Построить представление класса с помощью
    org.objectweb.asm.ClassWriter
  – Преобразовать его в массив byte[]:
    ClassWriter.toByteArray()

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

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

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

• Reflection внутри использует MagicAccessorImpl

                                              22
Производительность (1/2)

• Количество потоков: 4000 → 200
• Среднее время запроса: -20%




                                   23
Производительность (2/2)

• GC overhead: -30%
• Сетевой трафик: -50%




                           24
Спасибо!

• Примеры
  – https://github.com/odnoklassniki/one-nio


• Контакты
  – andrey.pangin@odnoklassniki.ru
  – www.odnoklassniki.ru/ap


• Работа в Одноклассниках
  – http://v.ok.ru


                                               25

More Related Content

PDF
Decompressed vmlinux: linux kernel initialization from page table configurati...
PDF
Linux Kernel - Virtual File System
PDF
Reverse Mapping (rmap) in Linux Kernel
PPT
Linux kernel memory allocators
PPTX
Linux Memory Management with CMA (Contiguous Memory Allocator)
PDF
Page cache in Linux kernel
PPTX
The TCP/IP Stack in the Linux Kernel
Decompressed vmlinux: linux kernel initialization from page table configurati...
Linux Kernel - Virtual File System
Reverse Mapping (rmap) in Linux Kernel
Linux kernel memory allocators
Linux Memory Management with CMA (Contiguous Memory Allocator)
Page cache in Linux kernel
The TCP/IP Stack in the Linux Kernel

What's hot (20)

PDF
UEFI時代のブートローダ
PDF
spinlock.pdf
PDF
Kernel Recipes 2019 - No NMI? No Problem! – Implementing Arm64 Pseudo-NMI
PDF
7 hands on
PDF
Memory Mapping Implementation (mmap) in Linux Kernel
PPTX
Linux Kernel Booting Process (1) - For NLKB
PDF
Anatomy of a Container: Namespaces, cgroups & Some Filesystem Magic - LinuxCon
PPTX
Ext filesystem4
PPT
Cache memory and cache
PDF
Cilium - Fast IPv6 Container Networking with BPF and XDP
PPT
Storage Management using LVM
PDF
Introduction to eBPF and XDP
PDF
BPF Internals (eBPF)
PPTX
Memory access tracing [poug17]
PPTX
Linux MMAP & Ioremap introduction
PDF
Kernel Recipes 2017 - Understanding the Linux kernel via ftrace - Steven Rostedt
PPTX
Understanding DPDK algorithmics
PPTX
Slab Allocator in Linux Kernel
PDF
Linux Performance Analysis and Tools
UEFI時代のブートローダ
spinlock.pdf
Kernel Recipes 2019 - No NMI? No Problem! – Implementing Arm64 Pseudo-NMI
7 hands on
Memory Mapping Implementation (mmap) in Linux Kernel
Linux Kernel Booting Process (1) - For NLKB
Anatomy of a Container: Namespaces, cgroups & Some Filesystem Magic - LinuxCon
Ext filesystem4
Cache memory and cache
Cilium - Fast IPv6 Container Networking with BPF and XDP
Storage Management using LVM
Introduction to eBPF and XDP
BPF Internals (eBPF)
Memory access tracing [poug17]
Linux MMAP & Ioremap introduction
Kernel Recipes 2017 - Understanding the Linux kernel via ftrace - Steven Rostedt
Understanding DPDK algorithmics
Slab Allocator in Linux Kernel
Linux Performance Analysis and Tools
Ad

Similar to Java tricks for high-load server programming (20)

PDF
Незаурядная Java как инструмент разработки высоконагруженного сервера
PPT
Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин
PDF
Выжимаем из сервера максимум (Андрей Паньгин)
PDF
андрей паньгин
PPTX
Developing highload servers with Java
PDF
Анатомия веб-сервиса, Андрей Смирнов
PDF
Анатомия веб-сервиса (РИТ-2014)
PPT
ВВЕДЕНИЕ В NODE.JS
PDF
как написать масштабируемую баннерокрутилку. денис бирюков, артем гавриченков...
PDF
CodeFest 2013. Иванов В. — Уменьшение расхода оперативной памяти в Java-прило...
PDF
YuryByyanov (e-legion) @ CodeCamp2011
PDF
Юрий Буянов «Архитектура Goozy»
PDF
Анатомия веб-сервиса, Андрей Смирнов (ex-Skype)
PDF
Анатомия веб сервиса (HighLoad-2014)
PDF
Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...
PDF
Леонид Васильев "Python в инфраструктуре поиска"
PDF
Клиент-серверные приложения на iPhone
PDF
Cocoa Networking
PPTX
Система обработки бизнес-логики server-side приложения на Groovy
PDF
Caching data outside Java Heap and using Shared Memory in Java
Незаурядная Java как инструмент разработки высоконагруженного сервера
Проект «Одноклассники» Mail.Ru Group, Андрей Паньгин
Выжимаем из сервера максимум (Андрей Паньгин)
андрей паньгин
Developing highload servers with Java
Анатомия веб-сервиса, Андрей Смирнов
Анатомия веб-сервиса (РИТ-2014)
ВВЕДЕНИЕ В NODE.JS
как написать масштабируемую баннерокрутилку. денис бирюков, артем гавриченков...
CodeFest 2013. Иванов В. — Уменьшение расхода оперативной памяти в Java-прило...
YuryByyanov (e-legion) @ CodeCamp2011
Юрий Буянов «Архитектура Goozy»
Анатомия веб-сервиса, Андрей Смирнов (ex-Skype)
Анатомия веб сервиса (HighLoad-2014)
Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...
Леонид Васильев "Python в инфраструктуре поиска"
Клиент-серверные приложения на iPhone
Cocoa Networking
Система обработки бизнес-логики server-side приложения на Groovy
Caching data outside Java Heap and using Shared Memory in Java
Ad

Java tricks for high-load server programming

  • 1. Особенности разработки высоконагруженного сервера на Java Андрей Паньгин ведущий разработчик проекта Одноклассники
  • 2. Серверы Одноклассников • Всего 5000 серверов – Web, Business logic, Download, Storage… – Все ПО написано на Java • Высоконагруженный сервер – 20 тыс. одновременных подключений – 30 тыс. запросов в секунду – Трафик до 1 Gb/s 1
  • 3. Remote Service • Компоненты портала как отдельные сервисы – Система сообщений, граф дружб, поиск, лента и др. • Внутренняя коммуникация между сервисами long[] friendIds = getConnections (userId) Graph cluster Business logic server List<User> friends = getUsers (friendIds) User service cluster 2
  • 4. RPC сценарий Read Request byte[] request = connection.read(…); Deserialize arguments request → Method method, Object[] args Invoke method Object result = method.invoke(args); Serialize result result → byte[] response Send response connection.write(response); 3
  • 5. java.net I/O (Sockets) • Поток на каждое соединение • Максимум 10 тыс. потоков 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 } } 4
  • 6. NIO (SocketChannels) • Selector, неблокирующие read / write • Direct ByteBuffers while (true) { if (selector.select() > 0) { // blocking call Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); if (key.isReadable()) { doRead(key); } else if (key.isWritable()) { doWrite(key); } iterator.remove(); } } } 5
  • 7. NIO frameworks • Apache MINA – http://mina.apache.org • Netty – https://netty.io • Основаны на NIO • Событийно-управляемая (event-driven) модель 6
  • 8. Socket I/O → Netty • Плюсы + Сокращение числа потоков (4000 → 200) • Минусы – Конструирование запроса по частям – Влияние на GC – Снижение производительности на 20% 7
  • 9. Blocking socket selector (BLOS) • Сочетает преимущества Selector + Blocking I/O • Возможно в OS, но не поддерживается в Java Linux epoll Solaris /dev/poll BSD kqueue Windows I/O completion ports • Реализуется посредством JNI – Простые Java-обертки над системными вызовами 8
  • 10. Java Socket → Native • 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); } 9
  • 11. Архитектура BLOS сервера • 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 10
  • 12. Проблемы работы с сетью в Java • java.net.Socket – Finalizers => GC impact (объекты SocketImpl не собираются до Full GC) – Нет поддержки Direct Buffers • NIO не спасает – Не срабатывают I/O timeouts – Возможна утечка нативной памяти 11
  • 13. Решение • И снова JNI – Нет finalize(), нет утечки памяти – Время GC до 10 раз меньше – Необходимость закрывать сокеты вручную • В Tomcat используется похожий подход – APR (Apache Portable Runtime) http://tomcat.apache.org/tomcat-7.0-doc/apr.html 12
  • 14. Remote Service • RPC сценарий Read Request Deserialize arguments Invoke method Serialize result Send response • Эффективность сериализации – ключ к производительности RPC – Затрачиваемое на сериализацию время – Размер передаваемых по сети данных 13
  • 15. Сериализация • Built-in Java Serialization – Слишком медленная – Большой объем получаемых данных • JBoss Serialization – Не поддерживает разные версии классов • Avro, Thrift, Protocol Buffers • Ручная сериализация – Externalizable – Не вариант (тысячи классов!) 14
  • 16. Требования к сериализации • Быстрая • Компактная • Обрабатывает простые изменения внутри класса – Добавление поля – Удаление поля – Изменение порядка полей – Изменение типа поля (int → long) 15
  • 17. Архитектура сериализации (1/2) • Типы сериализаторов – Встроенные (базовые типы, массивы, коллекции) – Генерируемые по полям класса • Сериализатор класса имеет уникальный ID – 64-битный хеш от имен, типов и порядка полей class Event { String name = “jug.msk.ru”; int year = 2013; UID 10 j u g . m s k . r u 2013 1 boolean started = true; } 16
  • 18. Архитектура сериализации (2/2) • Запись объекта: 1. Serializer serializer = Repository.getByClass(obj.getClass()); 2. outputStream.writeLong(serializer.uid); 3. serializer.write(obj, outputStream); • Чтение объекта: 1. long uid = inputStream.readLong(); 2. Serializer serializer = Repository.getById(uid);  may throw SerializerNotFoundException 3. Object obj = serializer.read(inputStream); 17
  • 19. Трудности реализации • Чтение и запись private полей чужих объектов • Создание экземпляров класса 18
  • 20. Трудности реализации • Чтение и запись private полей чужих объектов – Reflection (медленно!) – sun.misc.Unsafe • Создание экземпляров класса – sun.misc.Unsafe.allocateInstance() – Динамическая генерация байткода 19
  • 21. Unsafe 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 // As simple as malloc() Object x = unsafe.allocateInstance(X.class); // Read private field f of object x long offset = unsafe.objectFieldOffset(f); Object result = unsafe.getObject(x, offset); 20
  • 22. Генерация байткода 1. Построить массив byte[] с бинарным представлением класса 2. Вызвать ClassLoader.defineClass()  ClassLoader должен быть унаследован • ASM framework (http://asm.ow2.org) – Построить представление класса с помощью org.objectweb.asm.ClassWriter – Преобразовать его в массив byte[]: ClassWriter.toByteArray() 21
  • 23. А как же private поля? • JVM при загрузке нового класса верифицирует байткод • Если класс унаследован от sun.reflect.MagicAccessorImpl, верификатор не будет проверять права доступа для байткодов getfield и putfield • Reflection внутри использует MagicAccessorImpl 22
  • 24. Производительность (1/2) • Количество потоков: 4000 → 200 • Среднее время запроса: -20% 23
  • 25. Производительность (2/2) • GC overhead: -30% • Сетевой трафик: -50% 24
  • 26. Спасибо! • Примеры – https://github.com/odnoklassniki/one-nio • Контакты – andrey.pangin@odnoklassniki.ru – www.odnoklassniki.ru/ap • Работа в Одноклассниках – http://v.ok.ru 25