Go очима java програміста

Ця стаття не для тих, хто вже пише на go.
Вона для програмістів на інших мовах, яким инетересно, чи варто витрачати час на go.
Чим відрізняється go, наприклад, від java і чим може бути корисний.

Принципи go
Колись можна було просто взяти і створити нову мову програмування.
Зараз мова має шанс зайняти якесь місце тільки якщо у нього є чіткі принципи, яких дотримуються його творці. Іншими словами — власне обличчя.
Принципи go — це простота і продуктивність.

Принцип простоти звучить так:
Якщо без чого можна обійтися, без цього потрібно обійтися.

Принцип продуктивності:
Найцінніше — це час, що витрачається розробником. Його треба мінімізувати всіма доступними способами.

Проілюструю принцип простоти:
В go дуже мало мовних конструкцій. Наприклад, тільки один цикл. Go можна вивчити за два вечори.
В go відмовилися від динамічного завантаження бібліотек — результат компіляції один великий виконуваний файл
В go немає warning-ів при компіляції. Будь-яка некоректність або «багатослівність» — це помилка компіляції.
В go вбудований автоформат коду на рівні самої мови. Є тільки один канонічний вигляд коду на go

Тепер декілька прикладів продуктивності:
Код на go на 20-30 відсотків коротше аналогічного на яві (там просто не зайвих слів, наприклад, немає крапки з комою в кінці кожного речення, немає круглих дужок в операторах умови або циклу etc)
В go дуже швидкий компілятор (кілька секунд на компіляцію великого проекту)

Продуктивності служить не тільки саму мову, але і стандартні інструменти з ним поставляються.

Інструменти підвищення продуктивності

Профилировщики

почну з невеликого відступу про те як я, програміст java, прийшов до використання go.
Я робив ігровий проект — багатокористувацький космічний шутер.
Спочатку я написав серверну частину на java. Вона працювала.
Але дуже швидко все вперлося в продуктивність.
На одному сервері можна було запустити не більше 300 клієнтів.
Це занадто мало, щоб гра стала рентабельною.

Що я міг зробити з цим, як програміст java?
Насправді небагато:

1) Використовуючи метод пильного погляду шукати в коді неефективні місця, пробувати їх виправляти і, після кожної зміни, знову запускати навантажувальні тести. Цей спосіб очевидно неефективний.

2) Поринути у вивчення альтернативних серверних бібліотек, пробувати різні варіанти. Це теж не дає ніяких гарантій — можливо проблеми в моєму власному коді, або взагалі в самій мові java.

3) Нарешті, я міг купити один або кілька платних профілювальником і спробувати отримати якусь інформацію від них. Але і тут є проблеми. Профилировщики, які я бачив, вимагають, щоб їх запускали на тій же машині, що і сервер. Якщо використовувати локальний сервер, що стоїть у нас в офісі, я не міг створити потрібної навантаження, оскільки у нас в офісі немає потрібної кількості вільних машин, щоб запустити кілька тисяч клієнтів. Якщо ж використовувати зовнішній сервер, то була потрібна досить складна конфігурація, щоб запустити там профілювальник. Нарешті, вибір профилировщика сам по собі є нетривіальним завданням.

В go ця проблема вирішена дуже зручно.
Профілювальник там є частиною мови.
Включити її можна на будь-якому сервері, а результатом його роботи є файл, який можна завантажити на свою машину і не поспішаючи вивчити за допомогою дуже зручних інструментів.
Ці інструменти покажуть які саме рядки коду витрачають найбільше пам'яті і процесорних циклів.
Їх використання дає можливість швидко і ефективно знайти і виправити всі проблемні місця. (Які, до речі, майже завжди виявляються не там, де очікуєш їх знайти)

В моєму проекті сервер, переписаний на go спочатку «тягнув» тільки 20 клієнтів (багато гірше, ніж на яві). Після роботи з профилировщиками ця цифра зросла до 2500.

Race detector

Інший головний біль всіх письменників багатопоточних додатків — race conditions.
Це такі непомітних баги, які виникають тільки якщо зірки зійшлися певним чином. Тобто, якщо потоки запустилися в одному порядку, баг є, якщо в іншому — ні. А порядок непередбачуваний.
В go для вирішення цієї проблеми є стандартний інструмент — race detector. Якщо його включити, програма go буде писати в лог про всіх небезпечних зверненнях до загальної пам'яті з різних потоків.
Використовуючи race detector можна швидко, цілеспрямовано знайти і усунути всі проблемні місця.

Цікаві конструкції
Я не маю на меті навчити програмувати на go і не буду розбирати всі його конструкції. Хочу зупинитися тільки на трьох, найбільш цікавих. Всі вони описують парадигми, яких в яві в чистому вигляді немає. І ці парадигми дуже корисні.

Це інтерфейси, горутины і канали.

Інтерфейси

Інтерфейс в go схожий на інтерфейс в яві або c#. Це — набір сигнатур методів, але без реалізацій.
Основна різниця в тому, що go не вимагає оголошувати, що якась сутність імплементує якийсь інтерфейс.
Достатньо, щоб у сутності просто були всі потрібні методи.
Що це дає? Decoupling.
Ви можете взяти чужу бібліотеку. Знайти сутність там з якимось набором методів, створити у себе інтерфейс з тим же набором і використовувати цю сутність, як цей інтерфейс. Вам не треба змінювати чужий код, не треба зав'язувати свій код на чужі сутності і не треба писати адаптерів, які є класичним boilerplate кодом. Ще одна ілюстрація принципу продуктивності.

Горутины

Спочатку для багатозадачних додатків в мовах програмування були придумані процеси. Вони були важкими, мали власний адресний простір і швидко «жерли» ресурси системи. Потім з'явилися треди (або нитки). Вони були набагато легше і працювали в одному адресному просторі. Якщо процесів зазвичай запускали одиниці або десятки, то тредів можна було мати вже сотні.
Однак, при необережному використанні і вони могли забрати всі ресурси системи. Кожен тред все-таки займав якісь ресурси, навіть якщо був заблокований.
Щоб обмежити кількість тредів, почали використовувати тред пули.

Горутины можна мислити як завдання, що виконуються одним загальним великим тред пулом. В Go горутины дуже дешеві. Ви можете запустити мільйони горутин без проблем для продуктивності. Єдина вимога — горутины повинні бути «маленькі». Тобто горутина повинна швидко зробити свою роботу і вийти, або блокуватися (що з точки зору планувальника горутин одне і теж)

Канали

Канали є расово правильним засобом комунікацій між горутинами.
В go є важливе правило:

Don't communicate shared by state. Share state by communication.

Його сенс — не користуйтеся змінними, до яких має доступ більше однієї горутины.
Замість цього змусьте горутины пересилати дані один одному.
Ця пересилання даних якраз і робиться через канали.

Канал go чергу це (буфер) в один кінець якого можна писати, а з іншого читати.
При цьому, горутина блокується при спробі писати в переповнений канал або читати з пустого каналу.

Але найцікавіше — go має конструкцію для одночасного читання з декількох каналів.
Горутина може перерахувати кілька каналів, на яких вона буде сидіти і чекати появи повідомлення в одному з них. Отримавши це повідомлення, горутина розблокується, обробляє його і (зазвичай) знову чекає наступного повідомлення.
Так, наприклад, у чат сервері горутина може чекати повідомлення від користувача, так і сигналу про вихід з чату. Одночасно.

Управління залежностями
Такі інструменти управління залежностями, як maven або gradle сьогодні існують для всіх серйозних мов.
В go пішли далі і зробили підтримку управління залежностями на рівні самої мови.
При імпорті пакета (конструкцією, аналогічної import в яві) можна вказати як локальне ім'я, адресу пакету в будь-якій сучасній системі контролю версій (наприклад, в git).

Наприклад, «github.com/gorilla/websocket»

Go самостійно завантажить потрібний пакет і включить його в ваш проект. Якщо якийсь пакет вже був раніше завантажений, то він просто буде використаний. Ви також можете попросити go оновити всі пакети до їх останніх версій.

Тут, втім, є один неприємний момент — go завжди завантажує останню версію пакета. Якщо над проектом працює кілька людей, це може призвести до різних версій пакетів у різних розробників.

Для вирішення цієї проблеми використовуються зовнішні інструменти — менеджери пакетів.
Один з кращих на сьогоднішній день — glide.
В основі glide – дві дії:
1) Знайти всі залежно проекту, і записати їх у файл
2) Завантажити ці залежності

При цьому файл можна редагувати вручну, зазначаючи, при необхідності, інші версії пакетів (відмінні від останньої).

В якості ідентифікатора версії в GIT використовується ідентифікатор коміта (в інших системах контролю версій використовуються специфічні для них ідентифікатори)

Слабкі сторони go
Будь принцип має не тільки сильні, але і слабкі.
В go, на мій погляд, є одна погана і одна жахлива річ, що випливають із принципу простоти.
Погана — це відсутність generic-ів. Жахлива — обробка помилок шляхом перевірки повертається функцією коду.

Без generic-ів доводиться відмовлятися від суворої типізації.
А обробка помилок призводить до великої кількості однотипного коду. Який, до того ж, можна зовсім забути написати і залишити помилку необробленої.

Крім цього, дотримуючись принципу продуктивності, було вирішено ввести в go garbage collector. Саме по собі це непогано. Але важливо розуміти, що цей самий колектор буде запускатися кожні кілька секунд і помітно пригальмовувати систему. З цим, втім, можна успішно боротися.

Висновки
Варто ява програмісту повністю переходити на go? Немає. У всякому разі, поки. Ява в середньому швидше. У ній більше корисних бібліотек і напрацьованих способів створення додатків.

Варто використовувати go для вирішення певних завдань? Однозначно, варто.

Які завдання краще за все вирішувати на go? Створення багатопоточних високонавантажених серверних рішень, в яких потоки багато комунікують між собою.

Яку мову краще вивчати новачкові — go або яву? Однозначної відповіді немає, але з часом аргументів на користь go буде все більше.

Вік мови відіграє роль. Старий мова обростає «історичними» речами. Щось було додано давно, тепер нікому не потрібно, але викинути не можна, оскільки порушиться зворотна сумісність.
Якісь конструкції мови або стандартних бібліотек, раніше актуальні, зараз виглядають недоречно, здається, логічніше робити ці незатребувані речі поза стандарту мови.
Для якихось стандартних проблем накопичилося безліч альтернативних рішень різного якості. Для новачка все це — велика кількість непотрібних але необхідних знань в стилі: «Тут потрібно бути особливо обережним. Тут у нас КАЛЮЖА»

Go створювався набагато пізніше і на сьогодні в ньому є все, що затребуване зараз і нічого зайвого.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.