Как я познакомился с Go и gRPC, исходя из карьеры в области бэкэнд-разработки REST с Java.

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

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

Но у некоторых есть возможность время от времени менять свой основной язык.

У меня была возможность работать с тремя основными языками за последние 20 лет, сначала ASP.NET, затем PHP и, наконец, Java, с поздним обходом в Javascript (Angular и React и немного NodeJS).

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

Итак, я баловался с ним последние два месяца, и это мои впечатления.

На мой взгляд, у Го четыре сильных стороны:

  • Независимость от платформы / кроссплатформенность
  • Встроенные средства тестирования и тестирования
  • Возможности параллелизма
  • Простота

Исходя из опыта работы с Java, я буду обсуждать каждый момент, связанный с моим опытом работы с Java.

Независимость от платформы

С установленным Go очень легко создать исполняемый файл для запуска на любой целевой платформе, приведенный ниже код создаст .exe даже при запуске в MacOS или Linux:

$env GOOS=windows GOARCH=amd64 go build

Это делает развертывание чрезвычайно простым, а микросервисные архитектуры - простым вариантом, например, вы можете использовать свой macbook pro в качестве машины для разработки и подготовить исполняемые файлы для своих клиентов, использующих Windows, или для развертывания на сервере, на котором работает Linux.

В Java вы можете создавать файлы .jar (или. class) и запускать их, но большим недостатком является то, что вам нужен JRE установлена ​​ на целевой машине.

Встроенное тестирование и бенчмаркинг

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



Репозиторий выше показывает очень простую программу-калькулятор, файловая структура ниже показывает, как тестовые файлы располагаются рядом с каждой операцией:

А ниже - образец одного из этих тестов:

Обратите внимание на пару вещей в этом файле:

  • имя пакета не обязательно должно совпадать с каталогом, в котором он находится (package div_test, но каталог div)
  • импорт для протестированного пакета содержит путь от корня src в проекте
  • переменная t из пакета testing

Запуск тестов довольно прост, наиболее распространенный способ - использовать подстановочный знак ./…:

$ go test -v -cover ./…

Это проверит все файлы, в имени которых есть _test, а также покажет покрытие модульных тестов:

Бенчмаркинг:



Это другое репо показывает, насколько легко тестировать функции, в этом примере сравнивается конкатенация строк, эквивалент известной Java String vs StringBuffer, но в Go.

Клонируйте репо и выполните следующую команду:

go test -bench=.

Это запустит тестирование функций (подробности см. В файле main_test.go). Результаты тестов показывают следующее:

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

В сравнении с Java тестирование довольно громоздко, так как вам нужно решить, какую структуру использовать (JUnit, TestNG) и менеджер пакетов плюс конфигурацию для его настройки, и это только для тестирования, для тестирования производительности установка и настройка - это намного больше работы.

Возможности параллелизма

Это очень важно для людей, рассматривающих проект на Go. С помощью горутин очень легко реализовать не только параллелизм, но и модель памяти Go позволяет совместно использовать параллельные переменные вместо блокировки, и вместо тяжелых потоков он имеет более легкий подход, который позволяет меньше использовать ЦП. Это не обязательно означает, что Go автоматически является потокобезопасным, но с этим труднее столкнуться с проблемами параллелизма.

Не только это, но и флаг `-race`, который позволяет проверять возможные состояния гонки при запуске тестов: https://blog.golang.org/race-detector

Еще одна интересная особенность модели параллелизма Go состоит в том, что каждая процедура go занимает всего около 2 КБ из кучи по сравнению с 1 МБ, которое использует обычный поток Java.

Java по сравнению с ним более громоздкий, однако на Java нет возможности писать параллельные решения, только есть много потоков и / или блокировок, которые необходимо тщательно обрабатывать, плюс влияние на память намного выше.

Простота

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

Он также имеет неявные интерфейсы (если реализован известный метод, он автоматически реализует интерфейс, без явного упоминания имени интерфейса).

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

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

По моему личному мнению, этот аспект Go был очень освежающим и заставил меня сильно полюбить этот язык.

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

Пример использования Go: буферы протокола и gRPC

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

Подобно Go, Google разработал свое решение для подключения сервисов, на самом деле я обнаружил, что они отказались от REST в пользу gRPC для всего своего взаимодействия во внутреннем производстве, на облачной платформе Google и в публичных местах. -стоящие API.

Почему gRPC?

gRPC - это современная высокопроизводительная среда RPC (удаленного вызова процедур) с открытым исходным кодом, которая может работать в любой среде. Он может эффективно соединять службы внутри и между центрами обработки данных с подключаемой поддержкой балансировки нагрузки, трассировки, проверки работоспособности и аутентификации. Это также применимо на последней миле распределенных вычислений для подключения устройств, мобильных приложений и браузеров к серверным службам (взято с домашней страницы проекта)

gRPC использует буферы протокола для сериализации данных по сети, вместо XML или JSON.

Ключевое различие между gRPC и REST заключается в способе, которым RPC определяет согласование контракта. В то время как REST определяет свои взаимодействия через термины, стандартизированные в его запросах, RPC функционирует на основе идеи контрактов, в которых согласование определяется и ограничивается отношениями клиент-сервер, а не самой архитектурой. RPC дает клиенту большую часть полномочий (и ответственности) за выполнение, одновременно перекладывая большую часть обработки и вычислений на удаленный сервер, на котором размещен ресурс.

Преимущества gRPC перед REST, SOAP и даже graphQL огромны. Вот некоторые из них:

  • gRPC - бесплатная платформа с открытым исходным кодом, разработанная Google.
  • gRPC является частью Cloud Native Computation Foundation (CNCF), как и Docker и Kubernetes.
  • на высоком уровне он позволяет вам определять ЗАПРОС и ОТВЕТ для RPC, а остальное обрабатывает за вас.
  • Построен на основе HTTP / 2
  • низкая задержка
  • поддерживает потоковую передачу
  • независимый от языка
  • легко подключить аутентификацию, балансировку нагрузки, ведение журнала и мониторинг
  • netflix, square и, конечно, google переносят свои внутренние служебные коммуникации на gRPC

В следующем репо демонстрируется простой сервер gRPC с клиентом, выполняющим вызовы на простом удаленном калькуляторе:



Существует некоторая настройка, которую вам нужно будет выполнить на вашем компьютере (объяснено в файле readme), но основной рабочий процесс выглядит следующим образом:

  • Создайте свои данные в файле .proto (в protobuf это называется сообщением)
message SumRequest {
    int32 a = 1;
    int32 b = 2;
}
  • Добавьте свои услуги в этот файл
service CalculatorService{
    rpc Sum(SumRequest) returns (SumResponse) {};
}
  • Сгенерируйте вспомогательный код для файла (методы получения и установки, служебные методы, а также код клиента и сервера) в нашем случае в Go (но это может быть легко на любом другом основном языке):
protoc calculatorpb/calculator.proto --go_out=plugins=grpc:.

Затем вы можете запустить сервер gRPC с помощью:

$ go run calculator_server/server.go

который в основном выглядит так (полный код в репо, там тоже читается лучше):

func (*server) Sum(ctx context.Context, req        
                                        *calculatorpb.SumRequest)          
                                 (*calculatorpb.SumResponse, error) {
   fmt.Printf("Sum function was invoked with %v\n", req)
   total := req.GetA() + req.GetB()
   res := &calculatorpb.SumResponse {
       Result: total,
   }
   return res, nil
}
func main() {
   lis, err := net.Listen("tcp", "0.0.0.0:50051")
   .
   .
   .
   s := grpc.NewServer(opts...)        
   calculatorpb.RegisterCalculatorServiceServer(s, &server{})
}

Обратите внимание на функцию Sum, которую клиентский код вызывает удаленно:

func main() {
   fmt.Println("Calculator Client")
   cc, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
   .
   .
   .
   c := calculatorpb.NewCalculatorServiceClient(cc)
   doUnary(c)
}
func doUnary(c calculatorpb.CalculatorServiceClient) {
   req := &calculatorpb.SumRequest{
      A: 3,
      B: 6,
   }
   res, err := c.Sum(context.Background(), req)
   if err != nil {
      log.Fatalf("error calling the Sum service %v", err)
   }
   log.Printf("Result from sum:%d", res.GetResult())
}

Здесь показан простой унарный вариант использования gRPC, который представляет собой обычное взаимодействие клиент-сервер, подобное REST или Интернету в целом, однако gRPC позволяет эти три других типа:

  • Клиентская потоковая передача
  • Серверная потоковая передача
  • Двунаправленная потоковая передача

И именно в них gRPC намного опережает их, однако для целей этой статьи они не будут описаны в этот раз.

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