Пройдите Глазами Java-Программиста

Эта статья не для тех, кто уже пишет на ходу.

Это для программистов на других языках, которые задаются вопросом, стоит ли go их времени.

Чем, например, go отличается от java и чем он может быть полезен.



Принципы го

Когда-то можно было просто создать новый язык программирования.

В наше время язык имеет шанс занять какое-то место только в том случае, если у него есть четкие принципы, которым следуют его создатели.

Другими словами, ваше собственное лицо.

Принципы go — простота и производительность.

Принцип простоты звучит так: Если можно обойтись без чего-то, то следует обойтись без этого.

Принцип производительности: Самое ценное — это время, потраченное разработчиком.

Его необходимо минимизировать всеми доступными способами.

Позвольте мне проиллюстрировать принцип простоты: В go очень мало языковых конструкций.

Например, только один цикл.

Го можно выучить за два вечера.

В Go отказались от динамической загрузки библиотек — результатом компиляции является один большой исполняемый файл.

В go нет предупреждений о компиляции.

Любая некорректность или многословие является ошибкой компиляции.

Go имеет встроенное форматирование кода на уровне языка.

Существует только один канонический тип кода go. Теперь несколько примеров продуктивности: Код go на 20-30 процентов короче аналогичного в Java (в нем просто нет лишних слов, например, нет точки с запятой в конце каждого предложения, нет круглых скобок в условных операторах или операторах цикла и т. д.).

Go имеет очень быстрый компилятор (на компиляцию большого проекта уходит несколько секунд).

Производительность достигается не только за счет самого языка, но и стандартных инструментов, которые идут в комплекте с ним.



Инструменты повышения производительности



Профайлеры

Начну с небольшого отступления о том, как я, Java-программист, пришел к использованию go. Я делал игровой проект — многопользовательский космический шутер.

Изначально я писал серверную часть на Java. Она работала.

Но очень быстро все сводилось к производительности.

На одном сервере может быть запущено не более 300 клиентов.

Этого слишком мало, чтобы игра стала прибыльной.

Что я могу с этим сделать как Java-программист? На самом деле не так уж и много: 1) Методом взгляда ищите неэффективные места в коде, пытайтесь их исправить и после каждого изменения снова запускайте нагрузочные тесты.

Этот метод явно неэффективен.

2) Погрузиться в изучение альтернативных серверных библиотек, попробовать разные варианты.

Это тоже не дает никаких гарантий — могут быть проблемы в моем собственном коде, да и вообще в самом языке Java. 3) Наконец-то я мог купить один или несколько платных профайлеров и попытаться получить от них некоторую информацию.

Но и здесь есть проблемы.

Если бы я использовал локальный сервер в нашем офисе, я бы не смог создать необходимую нагрузку, так как у нас в офисе нет необходимого количества свободных машин для запуска нескольких тысяч клиентов.

Если вы использовали внешний сервер, то для запуска профилировщика на нем требовалась довольно сложная настройка.

Наконец, выбор профилировщика сам по себе не является тривиальной задачей.

Go очень удобно решает эту проблему.

Профилировщик там является частью языка.

Вы можете включить его на любом сервере, а результатом его работы будет файл, который вы сможете скачать на свой компьютер и потихоньку изучать с помощью очень удобных инструментов.

Эти инструменты покажут вам, какие строки кода тратят больше всего памяти и процессорного времени.

Их использование дает возможность быстро и эффективно найти и исправить все проблемные места.

(Которые, кстати, почти всегда оказываются не там, где ожидаешь их найти.

) В моем проекте сервер, переписанный на go, изначально тянул только 20 клиентов (гораздо хуже, чем на Java).

После работы с профайлерами эта цифра выросла до 2500.

Детектор гонок

Еще одна головная боль всех авторов многопоточных приложений — это условия гонки.

Это тонкие ошибки, которые возникают только в том случае, если звезды расположены определенным образом.

То есть если потоки запускались в одном порядке - ошибка, если в другом - нет. И порядок непредсказуем.

В Go есть стандартный инструмент для решения этой проблемы — детектор гонок.

Если вы включите его, программа go будет регистрировать все небезопасные доступы к общей памяти из разных потоков.

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



Интересные дизайны

Моя цель — не обучать программированию на Go и я не буду разбирать все его структуры.

Я хотел бы остановиться лишь на трёх наиболее интересных из них.

Все они описывают парадигмы, которых нет в Java в чистом виде.

И эти парадигмы очень полезны.

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



Интерфейсы

Интерфейс go аналогичен интерфейсу Java или C#.

Это набор сигнатур методов, но без реализаций.

Основное отличие состоит в том, что go не требует от вас объявлять, что какая-то сущность реализует какой-то интерфейс.

Достаточно того, что у сущности просто есть все необходимые методы.

Что это дает? Развязка.

Вы можете одолжить чью-то библиотеку.

Найдите там сущность с каким-то набором методов, создайте интерфейс с таким же набором и используйте эту сущность как этот интерфейс.

Вам не нужно менять чужой код, не нужно привязывать свой код к чужим сущностям и не нужно писать адаптеры, которые представляют собой классический шаблонный код. Еще одна иллюстрация принципа продуктивности.



Горутины

Впервые процессы были изобретены в языках программирования для многозадачных приложений.

Они были тяжелыми, имели собственное адресное пространство и быстро потребляли системные ресурсы.

Потом появились темы (или темы).

Они были намного легче и работали в одном адресном пространстве.

Если обычно запускалось всего несколько или десятки процессов, то потоков могло быть уже сотни.

Однако при неосторожном использовании они могут занять все системные ресурсы.

Каждый поток по-прежнему занимал некоторые ресурсы, даже если был заблокирован.

Для ограничения количества потоков стали использоваться пулы потоков.

Горутины можно рассматривать как задачи, выполняемые одним общим большим пулом потоков.

В Go горутины чрезвычайно дешевы.

Вы можете запускать миллионы горутин без каких-либо проблем с производительностью.

Единственное требование — горутины должны быть «маленькими».

То есть горутина должна быстро выполнить свою работу и либо выйти, либо заблокироваться (что с точки зрения планировщика горутины одно и то же).



каналы

Каналы — это расово корректные средства связи между горутинами.

В go есть важное правило: Не общайтесь в общем состоянии.

Делитесь состоянием посредством общения.

Его смысл заключается в том, чтобы не использовать переменные, к которым имеет доступ более чем одна горутина.

Вместо этого пусть горутины пересылают данные друг другу.

Эта передача данных осуществляется именно по каналам.

Канал в go — это очередь (буфер), в которую можно писать с одного конца и читать с другого.

При этом горутина блокируется при попытке записи в переполненный канал или чтения из пустого канала.

Но самое интересное, что go предназначен для одновременного чтения из нескольких каналов.

Горутина может перечислить несколько каналов, на которых она будет сидеть и ждать появления сообщения в одном из них.

Получив это сообщение, горутина разблокируется, обрабатывает его и (обычно) снова ждет следующего сообщения.

Так, например, в чат-сервере горутина может ждать как сообщения от пользователя, так и сигнала на выход из чата.

Одновременно.



Управление зависимостями

Инструменты управления зависимостями, такие как maven или gradle, сегодня существуют для всех серьезных языков.

Go пошел дальше и поддержал управление зависимостями на уровне языка.

При импорте пакета (конструкция, аналогичная импорту в Java) вы можете указать как локальное имя, так и адрес пакета в любой современной системе контроля версий (например, git).

Например, «github.com/gorilla/websocket».

Go автоматически загрузит необходимый пакет и включит его в ваш проект. Если пакет уже был загружен ранее, он будет просто использован.

Вы также можете попросить перейти на обновление всех пакетов до последних версий.

Однако здесь есть один неприятный момент — go всегда скачивает самую свежую версию пакета.

Если над проектом работают несколько человек, это может привести к появлению разных версий пакетов от разных разработчиков.

Для решения этой проблемы используются внешние инструменты — пакетные менеджеры.

Одним из лучших на сегодняшний день является скольжение.

Скольжение основано на двух действиях: 1) Найдите все зависимости проекта и запишите их в файл.

2) Загрузите эти зависимости В этом случае файл можно редактировать вручную, указывая при необходимости другие версии пакетов (отличные от последней).

Идентификатор версии в GIT — это идентификатор фиксации (другие системы контроля версий используют идентификаторы, специфичные для фиксации).



Слабые стороны го

Любой принцип имеет не только сильные, но и слабые стороны.

По моему мнению, в го есть одна плохая и одна ужасная вещь, вытекающая из принципа простоты.

Плохо то, что нет дженериков.

Ужасно — обработка ошибок путём проверки кода, возвращаемого функцией.

Без дженериков придется отказаться от строгой типизации.

А обработка ошибок приводит к появлению большого количества однотипного кода.

Который к тому же можно вообще забыть написать и оставить ошибку необработанной.

Кроме того, следуя принципу производительности, было решено внедрить в go сборщик мусора.

Это само по себе неплохо.

Но важно понимать, что этот самый сборщик будет запускаться каждые несколько секунд и заметно тормозить систему.

Однако с этим можно успешно бороться.



выводы

Должен ли Java-программист полностью перейти на работу? Нет. По крайней мере, на данный момент. Java в среднем быстрее.

Он содержит больше полезных библиотек и проверенных способов создания приложений.

Стоит ли использовать go для определенных задач? Определенно стоит того.

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

Какой язык лучше изучать новичку — Go или Java? Однозначного ответа нет, но со временем аргументов в пользу го будет появляться все больше и больше.

Возраст языка играет роль.

Старый язык обрастает «историческими» вещами.

Что-то было добавлено уже давно, сейчас это никому не нужно, но выбросить нельзя, так как нарушится обратная совместимость.

Некоторые конструкции языка или стандартных библиотек, ранее актуальные, теперь выглядят неуместно; кажется более логичным сделать эти невостребованные вещи вне языкового стандарта.

Для некоторых стандартных задач накопилось множество альтернативных решений разного качества.

Для новичка все это — большой объем бесполезных, но необходимых знаний в стиле: «Здесь нужно быть особенно осторожным.

У нас здесь ЛУЖА».

Go был создан гораздо позже и сегодня содержит все, что востребовано сейчас, и ничего лишнего.

Теги: #Go #сравнение #vs #java #Go

Вместе с данным постом часто просматривают: