Русский
Русский
English
Статистика
Реклама

Job шаблон проектирования для новичков и опытных Go программистов

Я начал программировать на Go после достаточно продолжительного периода программирования на PHP. Полагаю судя по последним тенденциям, мой случай далеко не единичный. Go в целом набирает популярность среди Web разработчиков.

Итак, я в мире сусликов. А что делает прожженный до мозга костей PHP программист оказавшись там? Правильно он продолжает "пыхтеть" - в силу своей профессиональной деформации но уже на Go, со всеми вытекающими отсюда последствиями.

Признаюсь я никогда не был на собеседовании, устраиваясь на вакансию Go разработчика, но подозреваю, что там не задают вопросов про SOLID, Dependency Injection и различные прелести ООП. Там другой мир, другая парадигма (в целом разумеется не экстраординарная), связанная с параллельным программированием, строгой типизацией и отсутствием полноценного ООП в строгом его смысле - к тому ООП к которому мы привыкли по PHP, С++ или Java.

Поэтому в начале, разумеется, был определенный дискомфорт и ломка шаблонов. Я не понимал как использовать тот же content.Context, зачем он? Мои шаблоны готовы уже были треснуть, но матерого PHP программиста без хрена не съешь. Пора что-то с этим делать и писать свой велосипед! А именно то решение, которое мне, как PHP программисту, казалось достаточно очевидным для решения задача связанных с параллельным программирование, краеугольным камнем Go. Усевшись за рабочий ноутбук я почти месяц корпел над работой. Я хотел не просто сделать реализацию сферического коня в вакууме, а показать на конкретном примере возможность использования данного подхода. Так сказать, proof of concept. Ну и заодно набить руку на Go, чего греха таить.

После того как велосипед был готов, я выкатил его, осмотрел и, выдохнув, сказал сам себе: "ну, блин, вроде неплохо получилось. Нужно срочно рассказать об этом сообществу, пускай оценят". Сказано сделано. На Reddit была опубликована небольшая заметка про новый шаблон проектирования Go и, судя по реакции я понял что люди ничего не поняли. "Зачем? Как это использовать? Так это же over-engineering" - в целом так можно охарактеризовать реакцию. Я не сдавался: "сейчас я вам нафотошоплю свой контраргумент. Ага, добавить это в README.md - пусть знают эти суслики".

Как видят решение программистыКак видят решение программисты

А если без шуток, то это, наверное, тема для отдельной статьи.

В целом о чем я? Много воды - ноль конкретики. Чтобы было реализовано:

  1. Шаблон проектирования Job - это поведенческий шаблон проектирования, который инкапсулирует всю необходимую информацию для параллельного выполнения задач (task).

  2. В качестве примера использования данного шаблона был реализован простой прокси-сервер, выполняющий роль балансировщика уровня L4; клиент, который сканируют указанную директорию на наличие изображений и отправляет все найденные изображения на backend сервер для изменения их размера; backend сервер, который обрабатывает запросы по изменению размера изображений. В основе всех трех приложений лежит компонент Job. Код так же доступен в репозитории на Github.

В целом сам шаблон Job - это аналог хорошо известного Command pattern, но с прицелом на параллельное выполнение. В той же статье есть и конкретный пример его использования и для нашего случая:

Parallel Processing Where the commands are written as tasks to a shared resource and executed by many threads in parallel (possibly on remote machines; this variant is often referred to as the Master/Worker pattern)

Выполнение задач зависит друг от друга, если одна задача прерывает свое выполнение из-за ошибки все остальные задачи останавливаются тоже. Это все это, безусловно, можно реализовать с помощью content.Context, но реализация на более высоком уровне позволяет избавиться от рутинных действий, связанных с организацией параллельного выполнения задач и сконцентрироваться на реализации самой бизнес логики. Ну а сами ошибки очень легко отлавливать вызовами специальных методов вроде task.Assert заменяя ими более емкие конструкции if err != nil { panic(err) }.

Задачи разделают данные и оркестрируют свое выполнение с помощью так называемой ping/pong синхронизации. Я приведу здесь лишь небольшой кускок кода, чтобы дать общее представление полностью библиотека доступна в репозитории на Github по ссылке.

// Saves resized image to the output dirfunc (s *ImageResizer) SaveResizedImageTask(j job.Job) (job.Init, job.Run, job.Finalize) {// Do some initialization hereinit := func(t job.Task) {if _, err := os.Stat(s.inputDir); os.IsNotExist(err) {t.Assert(err)}if _, err := os.Stat(s.outputDir); os.IsNotExist(err) {err := os.Mkdir(s.outputDir, 755)t.Assert(err)}}run := func(task job.Task) {stream := j.GetValue().(netmanager.Stream)select {case finishedTask := <- j.TaskDoneNotify(): // Wait for the scanner task to be doneif finishedTask.GetIndex() == s.scanneridx {s.scandone = true}task.Tick()case frame := <-stream.RecvDataFrame(): // Process response from the backend servertask.AssertNotNil(frame)res := &imgresize.Response{}err := frame.Decode(res)task.Assert(err)baseName := fmt.Sprintf("%s-%dx%d%s",res.OriginalName, res.ResizedWidth, res.ResizedHeight, res.Typ.ToFileExt())filename := s.outputDir + string(os.PathSeparator) + baseNameif ! s.dryRun {ioutil.WriteFile(filename, res.ImgData, 0775)}j.Log(1) <- fmt.Sprintf("file %s has been saved", filename)stream.RecvDataFrameSync() // Tell netmanager.ReadTask that we are done processing the frames.recvx++task.Tick()default:switch {case s.scandone && s.recvx == s.sentx: // Check if all found images were processedtask.FinishJob()default:task.Idle() // Do nothing}}}return init, run, nil}

Это одна из задач клиента, которая обрабатывает приходящий ответ от сервера и сохраняет полученное изображение. Задача оркестирует свое выполнение используя вышеупомянутую технику ping/pong синхронизации - с задачей, которая занимается файловым сканированием. Также она определяет, когда пришел последний ответ от сервера и когда нужно завершить выполнение всей работы (Job).

Насколько это решение over-engineered и насколько его использование вместо content.Context оправдано - пусть это решает читатель, я своё мнение выразил в виде сарказма на изображении выше.

Всем хороших выходных и да прибудет с нами сила пэхэпе в мире сусликов.

Источник: habr.com
К списку статей
Опубликовано: 16.01.2021 02:07:06
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Php

Go

Design patterns

Software

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru