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

Otus

Перевод CRLF-инъекции и HTTP Response Splitting

23.07.2020 18:11:16 | Автор: admin

Привет, Хабровчане! В преддверии старта занятий в ближайшей группе профессионального курса Безопасность веб-приложений, мы подготовили для вас еще один полезный перевод.




Что такое CRLF?


Когда браузер отправляет запрос веб-серверу, тот отправляет ответ, который содержит заголовки HTTP-ответа и сам контент сайта, то есть тело ответа. HTTP-заголовки и HTML-ответ (содержимое сайта) разделяются определенной комбинацией специальных символов, а именно возвратом каретки (carriage return) и подачей строки (line feed). Сокращается это как CRLF.


Веб-сервер использует CRLF, чтобы понять, где начинается новый HTTP-заголовок и заканчивается старый. Также CRLF может сообщить веб-приложению или пользователю, что новая строка начинается в файле или в блоке текста. Символы CRLF это стандартное сообщение HTTP/1.1, поэтому оно используется любым типом веб-сервера, включая Apache, Microsoft IIS и другие.



Что такое CRLF-инъекция?


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


CRLF-инъекция в веб-приложениях


Для веб-приложений CRLF-инъекции могут иметь серьезные последствия, которые будут зависеть от того, как приложение работает с данными. Последствия могут варьироваться от раскрытия конфиденциальной информации до выполнения вредоносного кода, что напрямую влияет на уровень безопасности веб-приложения. На самом деле атака типа CRLF-инъекции может нанести серьёзный вред веб-приложению несмотря на то, что она не была включена в список OWASP Top 10. Например, таким образом можно изменять логи в панели администратора, как показано в примере ниже.


Пример CRLF-инъекции в логи


Представьте себе файл с логами в панели администратора с шаблоном выходного потока IP Время Путь, как показано ниже:


123.123.123.123 - 08:15 - /index.php?page=home

Если злоумышленник может сделать инъекцию CRLF-символов в HTTP-запрос, он может изменить выходной поток и фальсифицировать логи. Он может изменить ответ веб-приложения на что-нибудь подобное:


/index.php?page=home&%0d%0a127.0.0.1 - 08:15 - /index.php?page=home&restrictedaction=edit

Здесь %0d и %0a закодированные URL символы CR и LF. Таким образом после того, как злоумышленник вставит эти символы, а приложение выведет их, логи будут выглядеть следующим образом:


IP Время Путь


123.123.123.123 - 08:15 - /index.php?page=home&127.0.0.1 - 08:15 - /index.php?page=home&restrictedaction=edit

Таким образом, используя CRLF-инъекции, злоумышленник может подделать записи в логах, чтобы замести следы. Злоумышленник буквально делает hijacking страницы и изменяет ответ. Например, представим сценарий, в котором у злоумышленника есть пароль администратора и выполняется параметр restrictedaction, который может использовать только администратор.


Проблема заключается в том, что если администратор заметит, что неизвестный IP использует параметр restrictedaction, то поймет, что что-то не так. Однако пока это выглядит так, будто команда была выдана localhost (и, потому, вероятно, кем-то, кто имеет доступ к серверу, например, администратором), это не будет выглядеть подозрительно.


Часть запроса, начинающаяся с %0d%0a будет обработана сервером как один параметр. После этого появится еще один & с параметром restrictedaction, который будет воспринят сервером, как еще один параметр. По сути, это будет тот же самый запрос, что и:


/index.php?page=home&restrictedaction=edit

HTTP Response Splitting


Описание


Поскольку заголовок HTTP-ответа и его тело разделены символами CRLF, злоумышленник может попытаться внедрить их. Комбинация CRLFCRLF скажет браузеру, что заголовок заканчивается и начинается тело. Значит теперь он может записывать данные в тело ответа туда, где лежит HTML-код. Это может привести к уязвимости межсайтового скриптинга.


Пример HTTP Response Splitting, приводящего к XSS


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


X-Your-Name: Bob

Значение заголовка задается с помощью GET-параметра name. Если нет кодировки URL-адреса и значение просто содержится в заголовке, то злоумышленник может вставить вышеупомянутую комбинацию CRLFCRLF, чтобы сообщить браузеру о начале тела запроса. Так он может вставить данные, например, полезную нагрузку XSS:


?name=Bob%0d%0a%0d%0a<script>alert(document.domain)</script>

Вышеуказанное выведет окно оповещения в контексте атакуемого домена.


Инъекция в HTTP-заголовки


Описание


С помощью CRLF-инъекции, злоумышленник также может вставлять HTTP-заголовки, которые могут быть использованы для обхода механизмов безопасности, таких как XSS-фильтр браузера или политики одинакового источника (same-origin-policy). Так злоумышленник может получить конфиденциальную информацию, такую как CSRF-токены. Он даже может установить файлы cookie, которые могут быть эксплуатированы путем входа жертвы в учетную запись злоумышленника или использования уязвимости межсайтового скриптинга (XSS).


Пример инъекции в HTTP-заголовок для извлечения конфиденциальных данных


Если злоумышленник сможет сделать инъекцию в HTTP-заголовок, который активирует CORS (Cross Origin Resource Sharing), то он сможет использовать javascript для доступа к ресурсам, защищенным SOP (Same Origin Policy), которая запрещает сайтам из разных источников получать доступ друг к другу.


Последствия CRLF-инъекции


Последствия CRLF-инъекции варьируются и могут включать в себя все последствия использования XSS и раскрытия конфиденциальной информации. Таким способом можно деактивировать некоторые ограничения системы безопасности, такие как фильтры XSS и Same Origin Policy в браузерах жертвы, делая их уязвимыми к вредоносным атакам.


Как предотвратить CRLF/HTTP-инъекции заголовков в веб-приложениях


Лучший метод предотвращения это не использовать ввод данных пользователем непосредственно в заголовок ответа. Если это невозможно, вы всегда должны использовать функцию для кодирования специальных символов CRLF. Еще одна хорошая практика безопасности это обновление языка программирования до версии, которая не позволяет делать инъекции CR и LF внутри функций, которые устанавливают HTTP-заголовки.


Таблица классификации уязвимостей и их степени тяжести





Подробнее о курсе Безопасность веб-приложений



Подробнее..

Перевод Регрессионная спираль смерти

27.07.2020 20:17:32 | Автор: admin

Перевод статьи подготовлен в преддверии старта курса Автоматизация тестирования на JavaScript




История, которая может показаться вам до боли знакомой:


Вы работаете QA в небольшой команде разработчиков; сегодня утро четверга второй недели вашего двухнедельного спринта. У вашей команды еще несколько историй (story) в разработке, которые должны быть утверждены к утру пятницы, чтобы быть готовыми к демонстрации днем. Ваши планы на вечер четверга начинают таять на ваших глазах.


На утреннем стендапе разработчики говорят, что они уже вот-вот заканчивают. Вы знаете, что они пишут модульные и компонентные тесты в рамках процесса разработки, и вы уже выполнили с ними несколько прогонов фич, так что вы уверены, что получите качественный код. Тем не менее, в дополнение к тестированию истории, вам все равно придется завершать автоматизацией UI и API, проверять некоторые нефункциональные требования и провести специфическое тестирование работоспособности И один день это не так много времени на все это. Вы, вероятно, можете уложиться в это время с тестированием, но успеть с автоматизацией будет нереально. Вы делитесь со скрам-мастером этими неприятными новостями.


ОК, говорит скрам мастер, как насчет, и вы точно знаете, что будет дальше. как насчет того, чтобы мы передвинули автоматизацию на следующий спринт? Автоматизация, стоящая непосредственно перед отметкой выполнено, заложенная в историю и всегда занимающая часть оцененного времени, выталкивается в будущее. В очередной раз. Мне это тоже не нравится, говорит ваш скрам-мастер, видя выражение вашего лица, но сейчас наша скорость на виду, это очень важно, поскольку мы приближаемся к этому гигантскому майлстоуну мы просто не можем не добить наши стори поинты.


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


Конец истории


Держу пари, что эта история, к сожалению, знакома многим из вас. Контекст или причина не имеют значения, мы неизбежно оказываемся в ситуациях, когда нас заставляют откладывать, сокращать или просто пропускать автоматизацию тестирования. К сожалению, важность автоматизации в Agile Delivery и последствия ее отсрочки уходят из виду многих правообладателей, специалистов по планированию, менеджеров и т. д. Для них автоматизация тестирования кажется требованием, которое в общем неплохо было бы иметь, но которым можно пренебречь, когда поджимают дедлайны.


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


Регрессионное тестирование


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


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


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


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


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


Правообладатели или люди, не имеющие опыта разработки, часто упускают риск внесения критических изменений за пределами функции в разработке и необходимости регрессионного тестирования. Я даже слышал, как говорят, что тестирование функции это просто проверка перечисленных критериев приемлемости этой функции(!!!). К сожалению, игнорирование или недооценка реальной цены регрессии является ключевым фактором для входа в регрессионную спираль смерти.


Agile Delivery и бремя регрессии


Agile Delivery вводит две концепции, относящиеся к регрессионной спирали смерти. Во-первых, Agile Delivery делит фичи на небольшие, отдельные пользовательские истории (user stories). Каждая пользовательская история должна быть атомарной, чтобы ее можно было разрабатывать и тестировать как отдельный объект. Во-вторых, Agile Delivery способствует коротким циклам разработки с небольшими итеративными релизами. Все виды и гибриды Agile, Scrum, Kanban, SAFE, LeSS, ScrumBan и т. д. по-своему используют эти концепции. Давайте посмотрим на каждую в отдельности.


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


Использование небольших отдельных историй может создать ложное чувство изоляции между изменениями кода в разных историях, подрывая при этом регрессионное тестирование. Да, пользовательские истории независимы, но под этими историями находится тот же взаимосвязанный код и тот же риск внесения непреднамеренных изменений. Неважно, насколько четко вы разделяете свои истории или насколько хорошо вы определяете критерии приемлемости, регрессионное тестирование так же необходимо в Agile-разработке, как и при разработке одной большой функции в каскадном процессе.



История 13 была внедрена и протестирована. Нам не о чем беспокоиться?
/ НЕТ! Изменения в коде могли повлиять на другие истории (как связанные, так и не связанные с этой историей)


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


Давайте перейдем ко второй концепции что Agile Delivery поощряет небольшую, инкрементную доставку. Если бы необходимо было регрессировать приложение каждые шесть месяцев, стоимость регрессионного тестирования, даже медленного ручного, не была бы ощутимой. Фактически, именно так мы раньше и поставляли программное обеспечение до перехода к Agile разработке у нас был бы план регрессионного тестирования, и каждые шесть месяцев мы выполняли этот план перед релизом. Это часто занимало дни или недели, но в рамках шестимесячного цикла релиза это было не слишком обременительно.


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


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


Предположим, у нас есть недавно созданная Agile-команда, которая только начинает свою первую итерацию разработки (спринт). На первой итерации (или некоторой начальной продолжительности в потоковой модели) у нас может быть три истории для тестирования. Тестирование этих историй довольно простое мы просто тестируем эти три истории. Во второй итерации мы добавляем две дополнительные истории. Сколько усилий будет затрачено на тестирование этих историй? Наивный ответ несколько меньше усилий, чем для первого спринта, так как мы тестируем только две истории. Однако, основываясь на нашем понимании того, что изменения кода могут вызвать регрессию в любой области приложения, мы знаем, что мы должны протестировать обе новые истории, а также регрессировать три ранее завершенные истории.



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


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


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



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


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



Автоматизация в Agile Delivery


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


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


Важно понимать, что автоматизация тестирования не ускоряет первоначальное тестирование. На самом деле, внедрение автоматизации тестирования часто замедляет базовую скорость разработки. Автоматизация это процесс разработки, который требует продумывания и опыта, как и всё остальное! Автоматизация не тестирует это просто запрограммированная проверка того, что уже было протестировано. К счастью, это именно то, что нам нужно для смягчения регрессионной спирали смерти!


Если учесть моменты двух предыдущих разделов то, что изменения в программном обеспечении могут иметь непреднамеренные последствия, и что agile delivery это просто непрерывный поток небольших изменений становится очевидным, что agile delivery не может функционировать без автоматизации тестирования. Попытка итеративной доставки без него это все равно, что отправиться в дальнюю дорогу, но отказаться от остановок, потому что остановки замедляют вас. Топливо фундаментальная часть автомобильного путешествия (на данный момент), так же, как автоматизация тестирования фундаментальная часть Agile Delivery. Автоматизация это единственное, что может смягчить удушающее бремя регрессии и избежать резкого падения скорости разработки.



Зачастую правообладатели или менеджеры утверждают, что автоматизацию тестирования следует полностью игнорировать, перенаправив 100% усилий по тестированию будущих спринтов на ручную регрессию. Гораздо чаще команды вынуждены принимать истории с частичной или достаточно хорошей автоматизацией. Выполнение этого требования все еще передает некоторый процент от общей стоимости регрессии в будущие спринты на ручное неавтоматическое тестирование. К сожалению, любая незаконченная автоматизация, независимо от того, насколько она мала, обязательно увеличивает нагрузку регрессии в будущих спринтах.


Оставляя любое количество регрессии на трудоемкое, ручное тестирование, вы встаете на скользкую дорожку, которая может быстро привести к регрессионной спирали смерти. Если вы тестируете и автоматизируете историю, что произойдет, если из-за нехватки времени мы согласимся с правообладателем и признаем историю законченной с неполной автоматизацией? Некоторое количество бремени регрессии передается на следующую итерацию, которая затем потребляет даже больше времени на тестирование в последующем спринте. Это дает еще меньше времени для автоматизации в этой итерации, в результате чего автоматизируется еще меньше тестов, еще больше спихивая ее в будущее и т. д. Этот шаблон повторяется из итерации в итерацию, создавая цикл положительной обратной связи, пока у QA совсем не останется времени на автоматизацию, из-за долгих часов и вечеров, едва удовлетворяющих потребность в ручном тестировании, результирующих в уходе с работы и поиске компании с более зрелой agile практикой.


Я твердо убежден в том, что без полной автоматизации тестирования не может быть здоровой Agile Delivery. Полная автоматизация историй с набором средств автоматизации, охватывающим все виды тестов (модуль, компонент, интеграция, пользовательский интерфейс и т. д.), абсолютно необходима для поддержания скорости доставки на протяжении всего жизненного цикла. Любое предложение о послаблении этого требования для каждой истории следует отвергать и объяснять, что это такое краткосрочное отвлечение для создания восприятия успеха, в то же время ставящее под угрозу долгосрочные цели развития. Не говоря уже о здравом уме.


Спираль смерти


Вернемся к нашей маленькой истории.


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


Вы собираетесь протестовать, когда ваш скрам-мастер вмешивается: Мы не можем этого сделать! Это первый шаг к регрессионной спирали смерти!


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


Вы хотите, чтобы мы преуспели или просто казались преуспевающими? Эти слова вашего бизнес-аналитика, вызывает улыбку на вашем лице.


Вы решаете отменить собеседование на следующей неделе.


Слова благодарности


Я не изобрел термин регрессионная спираль смерти, но никогда не видел его в письменной форме. Самое раннее использование термина, которое я могу найти, находится в Тестировании экстремального программирования (2002) Лизы Криспин и Тип Хаус.


Несколько других ссылок можно найти с помощью быстрого поиска в Google:


http://ivory.idyll.org/blog/software-quality-death-spiral.html (2008)


https://xebia.com/blog/regression-testing-with-an-agile-mindset (2010)




Узнать всё о курсе Автоматизация тестирования на JavaScript



Подробнее..

Нет времени на обучение. Что за этим стоит и как решить проблему?

01.04.2021 14:11:52 | Автор: admin

Привет, хабровчане! По наблюдениям наших менеджеров, чаще всего человек отказывается от прохождения курсов из-за нехватки времени. На самом деле у всех нас 24 часа в сутках, поэтому с ощущением, что у вас нет времени на желаемое, можно и нужно разбираться. Для этого мы подготовили веселый познавательный спецпроект в честь дня рождения OTUS, а в статье разбираем 3 первопричины, которые мешают расставлять приоритеты в пользу обучения.

1. Сомнения в пользе

Как мы рассуждаем, когда думаем о развитии навыков? К примеру, разработчик может думать так:

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

Но в глубине души может оставаться неуверенность, что все получится.

Что можно сделать: Разобрать для себя, как именно новая специальность или навыки повлияют на жизнь. Нарисовать детальный план действий после обучения, воссоздать реалистичную картинку нового будущего.

Например, человек только собирается перейти в IT-сферу. Ему будет полезно напитаться историями тех, кто в ней уже работает, попросить вместе порешать простенькие задачи, чтобы почувствовать себя в этой роли, сходить на собеседования на стажерские позиции и т.д.

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

Еще действенный способ разобраться в своих приоритетах ответить себе на вопрос Почему важно пройти обучение прямо сейчас?.

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

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

2. Мало свободы действий

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

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

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

3. Беспокойство за отношения с близкими

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

Что можно сделать: Именно решению этой проблемы мы посвятили отдельный спецпроект. Увлекательного приключения!

ПОЛЕТЕЛИ


Напишите в комментариях, какие рекомендации оказались для вас новыми и интересными? Актуальны ли для вас эти проблемы? Поделитесь своими способами с ними справиться!

Подробнее..

Перевод Производительпотребитель на Kafka и Kotlin

13.07.2020 20:06:08 | Автор: admin

Перевод статьи подготовлен в преддверии старта курса Backend-разработка на Kotlin




В этой статье мы поговорим о том, как создать простое приложение на Spring Boot с Kafka и Kotlin.


Введение


Начните с посещения https://start.spring.io и добавьте следующие зависимости:


Groovy


implementation("org.springframework.boot:spring-boot-starter-data-rest")implementation("org.springframework.boot:spring-boot-starter-web")implementation("com.fasterxml.jackson.module:jackson-module-kotlin")implementation("org.apache.kafka:kafka-streams")implementation("org.jetbrains.kotlin:kotlin-reflect")implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")implementation("org.springframework.kafka:spring-kafka")

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


Создайте и загрузите проект. Затем импортируйте его в IntelliJ IDEA.


Скачайте Apache Kafka


Загрузите последнюю версию Apache Kafka с их сайта и распакуйте в папку. Я пользуюсь операционной системой Windows 10. При запуске Kafka вы можете столкнуться с некоторыми проблемами по типу too many lines encountered. Так происходит потому что Kafka добавляет большую структуру папок в свое имя пути. Если эта проблема не будет устранена автоматически, вам придется переименовать структуру папок как-нибудь покороче и запустить приложение из Power Shell.


Чтобы запустить Kafka, воспользуйтесь следующими командами:


Shell


.\zookeeper-server-start.bat ..\..\config\zookeeper.properties.\kafka-server-start.bat ..\..\config\server.properties

Эти две команды вы увидите в папке /bin/windows.


Чтобы запустить Kafka, сначала нужно запустить Zookeeper. Zookeeper это продукт Apache, который предоставляет сервис распределенной конфигурации.


Запуск Spring Boot


Первым шагом создайте в своей IDE класс, который называется KafkaDemoApplication.kt. При создании проекта с сайта Spring, класс будет создан автоматически.


Добавьте следующий код:


Kotlin


import org.springframework.boot.autoconfigure.SpringBootApplicationimport org.springframework.boot.runApplication@SpringBootApplicationclass KafkaDemoApplication fun main(args: Array<String>) {   runApplication<KafkaDemoApplication>(*args)}

Производитель


Мы можем отправлять сообщения в топики двумя способами. Их мы рассмотрим ниже.


Мы разработаем класс-контроллер, который нужен для отправки и получения сообщений. Назовем этот класс KafkaController.kt. И добавим следующий метод:


Kotlin


var kafkaTemplate:KafkaTemplate<String, String>? = null;val topic:String = "test_topic"@GetMapping("/send")fun sendMessage(@RequestParam("message") message : String) : ResponseEntity<String> {    var lf : ListenableFuture<SendResult<String, String>> = kafkaTemplate?.send(topic, message)!!    var sendResult: SendResult<String, String> = lf.get()    return ResponseEntity.ok(sendResult.producerRecord.value() + " sent to topic")}

Для отправки сообщений в топик, который называется test_topic, мы используем KafkaTemplate. Он будет возвращать объект ListenableFuture, из которого мы можем получить результат этого действия. Такой подход является самым простым, если вы просто хотите отправлять сообщение в топик.


Второй способ


Следующий способ отправки сообщения в топик Kafka это использование объекта KafkaProducer. Для этого мы напишем следующий код:


Kotlin


@GetMapping("/produce")fun produceMessage(@RequestParam("message") message : String) : ResponseEntity<String> {    var producerRecord :ProducerRecord<String, String> = ProducerRecord(topic, message)    val map = mutableMapOf<String, String>()    map["key.serializer"]   = "org.apache.kafka.common.serialization.StringSerializer"    map["value.serializer"] = "org.apache.kafka.common.serialization.StringSerializer"    map["bootstrap.servers"] = "localhost:9092"    var producer = KafkaProducer<String, String>(map as Map<String, Any>?)    var future:Future<RecordMetadata> = producer?.send(producerRecord)!!    return ResponseEntity.ok(" message sent to " + future.get().topic());}

И тут нужно сделать небольшое пояснение.


Нам нужно инициализировать объект KafkaProduce с Map, которая будет содержать ключ и значение для сериализации. В нашем примере речь идет о строковом сообщении, поэтому нам нужен только StringSerializer.


В принципе, Serializer это интерфейс Kafka, который преобразует строки в байты. В Apache Kafka есть и другие сериализаторы, такие как ByteArraySerializer, ByteSerializer, FloatSerializer и др.


Для map мы указываем ключ и значение с StringSerializer.


Kotlin


map["key.serializer"]   = "org.apache.kafka.common.serialization.StringSerializer"map["value.serializer"] = "org.apache.kafka.common.serialization.StringSerializer"

Следующее значение это сведения о bootstrap-сервере, необходимые для коммуникации с кластером Kafka.


Kotlin


map["bootstrap.servers"] = "localhost:9092"

Все эти атрибуты нужны, если мы используем KafkaProducer.


Затем нам нужно создать ProducerRecord с именем топика и самим сообщением. Именно это мы и сделаем в следующей строке:


Kotlin


var producerRecord :ProducerRecord<String, String> = ProducerRecord(topic, message)

Теперь мы можем отправить наше сообщение в топик с помощью следующего кода:


Kotlin


var future:Future<RecordMetadata> = producer?.send(producerRecord)!!

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


Потребитель


Мы посмотрели, как отправлять сообщения в топики. Но нам также нужно слушать входящие сообщения. Чтобы это сделать, нужно создать слушателя, который будет потреблять сообщения.
Давайте создадим класс MessageConsumer.kt и пометим его с помощью Service.


Kotlin


@KafkaListener(topics= ["test_topic"], groupId = "test_id")fun consume(message:String) :Unit {    println(" message received from topic : $message");}

Этот метод можно использовать для прослушивания сообщения с помощью аннотации @KafkaListener и вывода сообщения в консоль, как только оно появляется в топике. Только убедитесь, что вы используете то же имя топика, что и для отправки сообщения.
Исходный код вы можете посмотреть в моем репозитории на GitHub.




Узнать подробнее о курсе Backend-разработка на Kotlin



Подробнее..

Конференция Demo Day новое образовательное мероприятие OTUS

08.12.2020 18:19:49 | Автор: admin

11 декабря вас ждет 6 лекций по разным IT-направлениям: программирование, тестирование, Data Science, управление и информационная безопасность. Участие бесплатное. Подробнее о формате рассказываем в статье.

За время существованияOTUSмы успели поэкспериментировать с разными мероприятиями. У нас были митапы в офисе, карьерный интенсив и CTF-соревнования. На регулярной основе проводим практикумы, открытые занятия и дни открытых дверей.

А сейчас у нас родилось желание проводить ивенты, которые бы олицетворялиOTUS. То есть объединяли бы все наши направления, преподавателей и студентов. Как раз в таком фестивальном формате пройдет в пятницу 11 декабря онлайн-конференция Demo Day.


За почти четыре года существования Отус накопил приличный капитал это наши преподаватели и их знания о самых последний кейсах индустрии. Мы хотим начать делиться с сообществом этими знаниями, нам кажется, что это полезный и необходимый вклад в развитие IT-специалистов, в рост их компетентности. Приходите и оцените!

Дмитрий Волошин

основатель OTUS


Что вас ждет в программе Demo Day?

17:30 - 18:00Ведущий мероприятия харизматичныйблогер Лекс АйТиБорода. Он откроет мероприятие, выступит модератором докладов и подведет итоги.

Далее со своими докладами выступят 6 экспертов из разных IT-направлений. Мы постарались подобрать для вас максимально практичные темы.


18:00 - 18:40Тестирование.

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

Вы рассмотрите все альтернативы с их плюсами и минусами, облачные фермы, локальные фермы, реальные устройства, эмуляторы/симуляторы.

Спикер:Наташкин Александр, Head of Mobile QA at ЮMoney (ex Яндекс.Деньги). 12 лет опыта работы в сфере IT, более 7 из них в QA.


18:40 - 19:20Программирование

Тема:Боли и прелести GraphQL на примере реальных проектов

Вы рассмотрите реальные кейсы внедрения GraphQL, возникнувшие в них проблемы и их решения. Также преподаватель расскажет, как с этим работать на NodeJS.

Спикер: Антон Морев, Wormsoft, основатель и IT-директор. За годы работы ему удалось выполнить множество как обычных, так и нестандартных задач, которые мотивируют к постоянному изучению изменяющихся технологий.

Как основатель и IT-директор Антон контролирует все процессы разработки в компании и занимается внедрением решений по оптимизации процессов.


19:20 - 20:00Data Science

Тема:Строим мосты между 2D и 3D данными с помощью Pytorch3D

На докладе вы с экспертом посмотрите на конкретные кейсы применения Pytorch3D для работы с облаками точек и 3d мешами, с обзором доступного функционала и с базовым объяснением терминов. Особое внимание уделим дифференцируемому рендеру. Доклад будет интересен тем кто интересуется работой с 3D данными, но не сталкивался ранее с Pytorch3D.

Спикер: Женя Ческидова, Deep Learning Engineer в Wolf3d, Таллин. Сейчас работает над технологией восстановления 3Д-меша лица по одной фотографии. Главная сфера интересов в глубоком обучении в настоящий момент работа с 3D-данными.


20:00 - 20:40Управление

Тема:Техники продуктивной корпоративной коммуникации

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

Встреча будет полезна тем, кто:

1. Чувствует хаос в поступающей информации

2. Испытывает проблемы с ненужными встречами (где половина сотрудников сидят, уткнувшись в телефоны)

3. Сталкивается с нарушением границ в рабочем общении

4. Хочет наладить свою рабочую коммуникацию так, чтобы она помогала работать, а не мешала

Спикер: Александр Пряхин, технический директор в CityAds Media. В профессиональном программировании прошел долгий путь от Junior Developer до CTO. Разработал различные обучающие курсы: от изучения языка PHP до построения масштабируемых систем и архитектур.


20:40 - 21:20Тестирование, MySQL

Тема:Пример оптимизации производительности в 32 раза

Эксперт познакомит с процессом оптимизации одной системы. В процессе рассказа производительность увеличивается с 5% до 160% от желаемой. Вы узнаете, как найти проблемы в разных узлах и путях и устранить их.

- нагрузочное тестирование

- оптимизация систем

- MS SQL

- Очереди

- .Net

- Анализ производительности

Спикер: Виктор Ганелес, эксперт по тестированию производительности ПромСвязьБанк. Более 6 лет занимается тестированием производительности в крупных банках. Постоянный спикер крупных международных конференций (SQA Days и прочие)


21:20 - 22:00Информационная безопасность

Тема:Методы внедрения вредоносного кода

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

Спикер: Артур Пакулов, ex-вирусный аналитик в Kaspersky Lab. Специалист в области низкоуровневого программирования, обратной разработки и анализа вредоносного программного обеспечения.


Для нас это новый формат, поэтому нам будет очень важно получить от участников обратную связь и идеи. В апреле мы планируем провести более масштабный ивентOTUS Fest, который будет длиться 3 дня и предложит участникам еще больше полезных докладов.

Ждем вас!Регистрация на Demo Day бесплатная. Планируете ли вы посетить конференцию? Какие темы вам интересны?

Подробнее..

Перевод Docker Compose от разработки до продакшена

24.07.2020 20:08:25 | Автор: admin

Перевод транскрипции подкаста подготовлен в преддверии старта курса Администратор Linux





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


С появлением docker compose v3 эти YAML-файлы могут использоваться непосредственно в рабочей среде, при работе с
кластером Docker Swarm.


Но значит ли это, что вы можете использовать один и тот же docker-compose файл в
процессе разработки и в продакшен среде? Или использовать этот же файл для
стейджинга? Ну, в целом да, но для такого функционала нам необходимо следующее:


  • Интерполяция переменных: использование переменных среды для некоторых
    значений, которые изменяются в каждой среде.
  • Переопределение конфигурации: возможность определить второй (или любой
    другой последующий) docker-compose файл, который что-то изменит относительно
    первого, и docker compose позаботится о слиянии обоих файлов.

Различия между файлами для разработки и продакшена


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


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


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


Переопределение конфигурации


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


Docker compose поддерживает объединение различных compose-файлов для
получения окончательной конфигурации. Как это работает можно увидеть на примере:


$ cat docker-compose.ymlversion: "3.2"services:  whale:    image: docker/whalesay    command: ["cowsay", "hello!"]$ docker-compose upCreating network "composeconfigs_default" with the default driverStarting composeconfigs_whale_1Attaching to composeconfigs_whale_1whale_1  |  ________whale_1  | < hello! >whale_1  |  --------whale_1  |     \whale_1  |      \whale_1  |       \whale_1  |                     ##        .whale_1  |               ## ## ##       ==whale_1  |            ## ## ## ##      ===whale_1  |        /""""""""""""""""___/ ===whale_1  |   ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~whale_1  |        \______ o          __/whale_1  |         \    \        __/whale_1  |           \____\______/composeconfigs_whale_1 exited with code 0

Как было сказано, docker compose поддерживает объединение нескольких compose-
файлов, это позволяет переопределять различные параметры во втором файле. Например:


$ cat docker-compose.second.ymlversion: "3.2"services:  whale:    command: ["cowsay", "bye!"]$ docker-compose -f docker-compose.yml -f docker-compose.second.yml upCreating composeconfigs_whale_1Attaching to composeconfigs_whale_1whale_1  |  ______whale_1  | < bye! >whale_1  |  ------whale_1  |     \whale_1  |      \whale_1  |       \whale_1  |                     ##        .whale_1  |               ## ## ##       ==whale_1  |            ## ## ## ##      ===whale_1  |        /""""""""""""""""___/ ===whale_1  |   ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~whale_1  |        \______ o          __/whale_1  |         \    \        __/whale_1  |           \____\______/composeconfigs_whale_1 exited with code 0

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


К счастью, docker compose автоматически ищет специальный файл с именем
docker-compose.override.yml для переопределения значений docker-compose.yml. Если
переименовать второй файл, то получится тот же результат, только с помощью изначальной команды:


$ mv docker-compose.second.yml docker-compose.override.yml$ docker-compose upStarting composeconfigs_whale_1Attaching to composeconfigs_whale_1whale_1  |  ______whale_1  | < bye! >whale_1  |  ------whale_1  |     \whale_1  |      \whale_1  |       \whale_1  |                     ##        .whale_1  |               ## ## ##       ==whale_1  |            ## ## ## ##      ===whale_1  |        /""""""""""""""""___/ ===whale_1  |   ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~whale_1  |        \______ o          __/whale_1  |         \    \        __/whale_1  |           \____\______/composeconfigs_whale_1 exited with code 0

Хорошо, так запомнить проще.


Интерполяция переменных


Файлы конфигурации поддерживают интерполяцию
переменных
и значения по умолчанию. То есть вы можете сделать следующее:


services:  my-service:    build:      context: .    image: private.registry.mine/my-stack/my-service:${MY_SERVICE_VERSION:-latest}...

И если вы выполняете docker-compose build (или push) без переменной окружения
$MY_SERVICE_VERSION, будет использовано значение latest, но если вы установите
значение переменной окружения до сборки, оно будет использовано при сборке или пуше
в регистр private.registry.mine.


Мои принципы


Подходы, которые удобны для меня, могут пригодиться и вам. Я следую этим
простым правилам:


  • Все мои стеки для продакшена, разработки (или других сред) определяются через
    файлы docker-compose.
  • Файлы конфигурации, необходимые для охвата всех моих сред, максимально
    избегают дублирования.
  • Мне нужна одна простая команда для работы в каждой среде.
  • Основная конфигурация определяется в файле docker-compose.yml.
  • Переменные среды используются для определения тегов образов или других
    переменных, которые могут меняться от среды к среде (стейджинг, интеграция,
    продакшен).
  • Значения переменных для продакшена используются в качестве значений по
    умолчанию, это минимизирует риски в случае запуска стека в продакшене без
    установленной переменной окружения.
  • Для запуска сервиса в продакшен-среде используется команда docker stack deploy compose-file docker-compose.yml --with-registry-auth my-stack-name.
  • Рабочее окружение запускается с помощью команды docker-compose up -d.

Давайте посмотрим на простой пример.


# docker-compose.yml...services:  my-service:    build:      context: .    image: private.registry.mine/my-stack/my-service:${MY_SERVICE_VERSION:-latest}    environment:      API_ENDPOINT: ${API_ENDPOINT:-https://production.my-api.com}...

И


# docker-compose.override.yml...services:  my-service:    ports: # This is needed for development!      - 80:80    environment:      API_ENDPOINT: https://devel.my-api.com    volumes:      - ./:/project/src...

Я могу использовать docker-compose (docker-compose up), чтобы запустить стек в
режиме разработки с исходным кодом, смонтированным в /project/src.


Я могу использовать эти же файлы на продакшене! И я мог бы использовать точно
такой же файл docker-compose.yml для стейджинга. Чтобы развернуть это на
продакшен, мне просто нужно собрать и отправить образ с предопределенным тегом
на этапе CI:


export MY_SERVICE_VERSION=1.2.3docker-compose -f docker-compose.yml builddocker-compose -f docker-compose.yml push

На продакшене это можно запустить с помощью следующих команд:


export MY_SERVICE_VERSION=1.2.3docker stack deploy my-stack --compose-file docker-compose.yml --with-registry-auth

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


export MY_SERVICE_VERSION=1.2.3export API_ENDPOINT=http://staging.my-api.comdocker stack deploy my-stack --compose-file docker-compose.yml --with-registry-auth

В итоге мы использовали два разных docker-compose файла, которые без
дублирования конфигураций могут использоваться для любой вашей среды!




Узнать подробнее о курсе Администратор Linux



Подробнее..

Аспекты учета и поиска геоинформационных объектов с задействованием MongoDB

02.04.2021 04:18:46 | Автор: admin

Онлайн-курс:"OTUS.NoSQL".

Проект: https://github.com/BorisPlus/mongodb_geo

Введение

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

Академический подход написания статей подразумевал "сведения, отражающие свойства ... объектов материального мира". Однако на практике имелся факт осуществления энтузиастом накладки поверх Гугл-карт через штатное API рисунков с топографией Средиземья и построение маршрутов героев Дж. Толкина, что не совсем "материально". Другим стыком с нематериальным может служить пример наборов данных по типуGeoIP,E.164,ABC.

Результат исследования представляет собой инструмент отображения хранящихся в MongoDB сведений о геообъектах на карте посредством web-доступа. Клиентская часть реализована с использованиемLeaflet(JavaScript-библиотека с открытым исходным кодом для мобильных интерактивных карт) и набора соответствующих процедур асинхронного получения сведений от серверной части. Сервис разработан на базе созданного ранее на курсе"OTUS.Web-python"конструктора программного обеспечения"Dummy WSGI Framework"(репозиторий) на языке программирования Python3 с задействованием WSGI.

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

Необходимо заметить, что документацияMongoDBявляется достаточно проработанной и удобной по навигации. Исчерпывающие сведения в разделе погеообъектамподтолкнули к изучению изложенных в данной работе возможностей. Кроме того, после осуществленной разработки и достигнутых результатов в ходе написания настоящего текста в документации MongoDB обнаруженпримерработы с геообъектами, что еще раз подтверждает ее высокую проработку.

Гео-объекты MongoDB

В данной статье продемонстрированы базовые возможности оперирования в MongoDB такимигеообъектамикак:точка,линия,полигон.

Более сложные структуры, такие как:набор точек,набор линий,набор полигоновиколлекция геообъектовне рассматриваются.

Представляемое в рамках настоящей работы решение предполагает возможность хранения в обособленных коллекциях одной базы данных сведений о геообъектах различных типов. Тем не менее хранение также возможно в рамках одной коллекции. Названия полей, описывающих геообъекты, могут быть произвольными, за исключением уникально идентифицирующего атрибута документа -ident. Также необходимо соблюдать установленную MongoDBструктуру сведенийо геообъекте:

<field>: { type: <GeoJSON type> , coordinates: <coordinates> }

В рамках получения сведений клиентской стороной от серверной геообъекты искусственно поделены на два вида - статичные и динамичные.

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

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

Предполагается, что инфраструктура MongoDB развернута.
mongo  192.168.102.99  --port 49326 ---> use otus switched to db otus > db.dropDatabase() { "dropped" : "otus", "ok" : 1 } > use otus switched to db otus > db otus > show collections

Точки (статичные)

В качестветочекиспользуютсяметеориты. Необходимые коллекция, поля и индексы:

> db.meteorites.createIndex( { "ident": 1 }, { unique: true } )> db.meteorites.createIndex( { "location" : "2dsphere" } ) 

Изначально сведения о местоположении хранятся в атрибутеgeolocation. Однако оно не имеет необходимой структуры для геообъекта типа "точка". Поэтому в качестве атрибута местоположения метеоритов выступает дополнительное, отсутствующее в демонстрационном наборе, полеlocation, куда перенесены сведения в необходимом формате:

location: { type: 'Point' , coordinates: [ LON, LAT ] }

Загрузка исходных данных о метеоритах:

mongoimport --host 192.168.102.99  --port 49326 \--db otus --collection meteorites --jsonArray \--file ./foreign/meteorites/data.json2021-03-28T10:28:09.443+0300    connected to: mongodb://192.168.102.99:49326/2021-03-28T10:28:12.443+0300    [<span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);"><span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);">#</span>##.....................] otus.meteorites      1.62MB/10.1MB (16.0%)</span>2021-03-28T10:28:15.443+0300    [<span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);"><span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);">#</span>########...............] otus.meteorites      3.97MB/10.1MB (39.4%)</span>2021-03-28T10:28:18.443+0300    [<span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);"><span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);">#</span>###########............] otus.meteorites      5.39MB/10.1MB (53.4%)</span>2021-03-28T10:28:21.443+0300    [<span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);"><span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);">#</span>################.......] otus.meteorites      7.23MB/10.1MB (71.6%)</span>2021-03-28T10:28:24.443+0300    [<span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);"><span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);">#</span>####################...] otus.meteorites      8.83MB/10.1MB (87.5%)</span>2021-03-28T10:28:27.443+0300    [<span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);"><span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);">#</span>######################.] otus.meteorites      9.71MB/10.1MB (96.3%)</span>2021-03-28T10:28:28.453+0300    [<span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);"><span class="pl-c" style="box-sizing: border-box; color: var(--color-prettylights-syntax-comment);">#</span>#######################] otus.meteorites      10.1MB/10.1MB (100.0%)</span>2021-03-28T10:28:28.454+0300    45716 document(s) imported successfully. 0 document(s) failed to import.

Исходя из уже имеющегося опыта, из набора 45716 объектов необходимо удалить метеорит, который не относится к земной поверхности (марсианский метеоритMeridiani Planum), так как его координаты не соответствуют стандарту земного геопозиционирования и не могут быть помещены в индекс (индексирование приводит к ошибкеCan't extract geo keys: ... longitude/latitude is out of bounds, ..., равно как и вставка таких данных в индекс).

db.meteorites.remove({"ident" : "32789"});

Кроме этого в наборе имеется 7315 метеоритов, чье местоположение не известно. Данный факт также не позволит включить их в гео-индекс и приведет к ошибке. Поэтому в индексе учтены только те метеориты, чье местоположение известно.

db.meteorites.updateMany(     {"geolocation":{$exists:true}},    [{        $set: {            "location" : {                "type": "Point",                "coordinates" : [                     { $toDouble: "$geolocation.longitude" } ,                     { $toDouble: "$geolocation.latitude" }                 ]            }        }    }]);

В результате в MongoDB в коллекцииmeteoritesв атрибутахlocationсодержится информация о местоположении 38400 метеоритах из их общего числа 45716.

Важное замечание:согласнодокументацииданный порядок следования координат{ долгота, широта }является единственно верным с точки зрения MongoDB (If specifying latitude and longitude coordinates, list the longitude first and then latitude). Необходимо заострить внимание на этом обстоятельстве, так как в последующем при отображении информации на карте Leaflet нужен будет другойпорядокабсолютно для всех координат любых геообъектов -{ широта, долгота }. Указанное приводит к тому, что после получения сведений из MongoDB требуется произвести перестановку в парах координат или представить их в виде "словаря"{ lon: долгота, lat: широта }. Если для точки это выражается в одной перестановке, то для полигона это происходит в рамках итерации по точкам границы. Было бы хорошо, если бы MongoDB поддерживала хранение и в формате{ широта, долгота }.

Полигоны (статичные)

В качествеполигоновиспользуются здания и сооружения, сведения о которых единоразово получены для отдельного района г. Санкт-Петербурга с использованием API сервисаWikiMapia.

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

Необходимые коллекция, поля и индексы:

db.geo_wikimapia_polygons.createIndex( { "ident": 1 }, { unique: true } )db.geo_wikimapia_polygons.createIndex( { "area" : "2dsphere" } ) 

Сбордемонстрационных данных реализован на языке программирования Python3 с использованием библиотекиpymongo.

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

Изначально сведения о местоположении полигона разнесены по полям записи, то есть фактически отсутствует атрибут необходимой для MongoDB структуры для геообъекта типа "полигон". Поэтому в его качестве выступает дополнительное полеarea, куда перенесены сведения в необходимом формате (происходит сразу привставкесведений в рамках их онлайн-получения). Структура геообъекта типаполигонимеет вид:

area: { type: 'Polygon' , coordinates: [[ [LON_1, LAT_1], [LON_2, LAT_2], ..., [LON_1, LAT_1] ]] }

В результате запросов к WikiMapia:

python3 ./foreign/onetime_static_load_polygons_wikimapia.py Page 1 has docs count 50Page 2 has docs count 50...Page 37 has docs count 35Max page 37 with some data

в MongoDB накоплена информация в отношении:

> db.geo_wikimapia_polygons.count()

1832 зданий и сооружений.

Важное замечание: в MongoDB сведения о полигоне должны удовлетворять спецификации (пункт 3.1.6 RFC 7946 "GeoJSON" August 2016). В частности, полигон, имеющий пересечение граней, не может быть добавлен (иначе в MongoDB возникает ошибкаEdges <number K> and <number M> cross. Edge locations in degrees: [Kx1, Ky1]-[Kx2, Ky2] and [Mx1, My1]-[Mx2,My2]). Кроме того, важно, чтобы полигон был "замкнут", то есть крайняя точка должна совпадать и первоначальной (иначе в MongoDB возникает ошибкаLoop is not closed). WikiMapia же иным образом подходит к требованиям достоверности координат. Поэтому из 1835 полигонов, полученных в описанном ранее абзаце (36 страниц * 50 полигонов + 35 полигонов = 1835 полигонов), сохранены 1832 объекта.

Линии (динамичные)

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

Важное замечание: сведения о линии должны удовлетворять спецификации (пункт 3.1.4 RFC 7946 "GeoJSON" August 2016), то есть линия должна содержать две разные точки (при этом она может также содержать и одинаковые).

Сбор демонстрационных данныхреализованна языке программирования Python3 с использованием библиотекrequests,pymongoи задействованием пакета многопроцессорной обработкиmultiprocessing. Необходимость крайнего обусловлена требованиями увеличения скорости получения актуальных сведений о местоположении и пути следования автомобилей с целью повышения эффективности частоты прорисовки маршрутов на карте (максимизации интерактивности). Сведения получаются в отношении заранее определенных точек района г. Санкт-Петербурга. Точки сбора данной информации располагаются на определенном коротком расстоянии друг от друга и образуют заранее рассчитанную в проекте "ячеистую" структуру. Данный подход отличается оталгоритма "заливки", применявшегося иным разработчиком, исследовавшим подобную информацию ранее.

Вниманию Python-разработчиков: невозможно организовать пул процессов, к которым применены декораторы. Необходимо переписать код таких функций с условием внесения в их содержание "оберточной" логики.

Изначально сведения о маршруте не имеют необходимой MongoDB структуры для геообъекта типа "линия". Поэтому в качестве данного атрибута выступает дополнительное полеpath, куда перенесены сведения в необходимом формате. Структура геообъекта типалинияимеет вид:

path: { type: 'LineString' , coordinates: [ [LON_1, LAT_1], [LON_2, LAT_2], ..., [LON_N, LAT_N] ] }

Операции с геообъектами

В рамках работы рассматриваются такиеоперациикак:$geoIntersectsи$nearSphere.

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

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

Использование иной операции$nearSphereпродемонстрировано на примере выборки из MongoDb полигонов и метеоритов с целью отображения их на карте только при условии присутствия их в "круговой" окрестности наблюдения (для полигона - пересечение с окружностью).

Операции$geoWithin(выборка геообъектов, имеющих полное включение в заданную область) и$near(выборка геообъектов в окрестности точки) в рамках настоящей работы не рассматриваются.

Операции$nearи$nearSphereшире по возможности, чем просто "нахождение в круговой окрестности", так как описывают не только максимальное удаление ($maxDistance) от точки наблюдения, но и минимальное ($minDistance). Данное обстоятельство может быть использовано при работе с секторами, учитывающими "примерное" удаление объектов от исходной точки наблюдения: секторное поле зрения на панораме "Яндекс.Карты", "классические" области действия сотовых вышек, углы обзора видеокамер городского наружного наблюдения и иное.

Сервис демонстрации геообъектов

Сервис реализован на базе авторского конструктора программного Web-обеспечения"Dummy WSGI Framework"(репозиторий), является легковесным (около 45 килобайт кода).

pip3 install -r ./service/requirements.txtuwsgi --http 127.0.0.1:8080 --wsgi-file ./service/application.py

Обстановка в области карты

По адресуhttp://127.0.0.1:8080вниманию представлен район г. Санкт-Петербурга, где отображены полигоны зданий. По мере перемещения по карте или изменении масштаба карты в асинхронном режиме подгружаются иные полигоны.

Разработчикам на заметку: для записи GIF-анимации использовалось программное обеспечениеPeek.

При перемещении в определенные районы (достаточно в исходной точке г. Санкт-Петербурга уменьшить масштаб до размера "7") подгружаются указатели на конкретные места падения метеоритов.

С целью демонстрации в текущем приложении нет ограничения на степень масштабирования, при которой запросы к серверной части не происходят. Тем самым представляется возможным увидеть скопления метеоритов на карте достаточно малого масштаба. Однако в качестве рекомендаций при работе с данными, имеющими значительный "мировой" объем, можно посоветовать использование имеющегося в Leaflet параметра минимально допустимого уровня масштабирования карты.

Очередным открытием в данных NASA явилось наличие 64 метеоритов, местоположения падения которых имеют абсолютно одинаковые координаты. Указанное обнаружено в результате визуального изучения метеоритов на карте (выделяющаяся темная тень).

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

> db.meteorites.find({"location.coordinates": [13.43333,58.58333] }).count()    64> db.meteorites.find({"location.coordinates": [13.43333,58.58333] }, {name: 1, _id: 0})    { "name" : "Osterplana" }    { "name" : "sterplana 002" }    { "name" : "sterplana 003" }    ...    { "name" : "sterplana 064" }

Данные "необычные" сведения соответствуют метеориту "sterplana", имеющему удивительнуюисторию(рус.).

Для интерактивной демонстрации динамичных геообъектов необходима следующая коллекция:

db.geo_yandex_taxi.deleteMany({})db.geo_yandex_taxi.createIndex( { "ident": 1 }, { unique: true } )db.geo_yandex_taxi.createIndex( { "last_point" : "2dsphere" } )db.geo_yandex_taxi.createIndex( { "path" : "2dsphere" } )

и фоновый сбор актуальных данных:

python3 ./foreign/upsert_yandex_taxi_loop.py 9       2.61409401893615729       2.4818162918090829       2.5282382965087899       2.3746058940887459       2.53371548652648939       2.72976160049438489       2.605773925781259       2.5869448184967049       2.5660433769226074

Исходные сведения о перемещении таксопарка "Яндекс" обезличены и содержат крайние точки пройденных маршрутов:

{'id': 'bcc095db8e3b56e057caebdb97af5694', 'display_tariff': 'business', 'free': True, 'static_icon': False, 'positions': [{'lon': 30.326291, 'lat': 59.974395, 'direction': 50.0, 'timestamp': '2021-03-24T23:49:01.000000+0000'}, {'lon': 30.326291, 'lat': 59.974395, 'direction': 50.0, 'timestamp': '2021-03-24T23:48:52.000000+0000'}, {'lon': 30.326291, 'lat': 59.974395, 'direction': 50.0, 'timestamp': '2021-03-24T23:48:43.000000+0000'}, {'lon': 30.326291, 'lat': 59.974395, 'direction': 50.0, 'timestamp': '2021-03-24T23:48:34.000000+0000'}]}

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

На их основании сформированы линия перемещения и точка крайнего местоположения.

Помимо мониторинга на предмет появления нового динамичного объекта в текущей области карты системой осуществляется проверка покидания объектами ее границ. В ином бы случае движущиеся объекты останавливались и скапливались по краям карты (в случае работы подобного сервиса с "подконтрольными" сведениями необходимость в данной особенности полностью отсутствует).

Обстановка в области круговой окрестности

По адресуhttp://localhost:8080/circle/продемонстрирована выборка только тех геообъектов, которые попадают в круговую окрестность, располагаемую по центру карты.

Важное замечание: с целью использованияnearиnearSphereтребуется создать индекс2dили2dsphere, иначе их вызов приведет к ошибке исполнения:

error processing query: ns=otus.geo_wikimapia_polygonsTree: GEONEAR  field=area maxdist=500 isNearSphere=0Sort: {}Proj: { _id: 0 } planner returned error :: caused by :: unable to find index for $geoNear query, full error: {'ok': 0.0, 'errmsg': 'error processing query: ns=otus.geo_wikimapia_polygonsTree: GEONEAR  field=area maxdist=500 isNearSphere=0\nSort: {}\nProj: { _id: 0 }\n planner returned error :: caused by :: unable to find index for $geoNear query', 'code': 291, 'codeName': 'NoQueryExecutionPlans'}

Технические особенности реализации

При использовании данных наработок помимо перечисленных аспектов необходимо учитывать следующее.

Конфигурационный файл

Параметр конфигурации сервисаbase_config.pyсодержит отсылку на вид ("статичный", "динамичный"), название коллекции базы MongoDB ("meteorites", "geo_wikimapia_polygons", "geo_yandex_taxi") и атрибуты ("location", "area", "last_point", "path"), содержащие сведения о геообъектах с указанием их GeoJSON-типа ("Point", "LineString", "Polygon"), а именно:

...MONGODB_DB_COLLECTIONS = dict(    static={        "meteorites": {            "location": POINT_OBJECT,        },        "geo_wikimapia_polygons": {            "area": POLYGON_OBJECT,        },    },    dynamic={        "geo_yandex_taxi": {            "last_point": POINT_OBJECT,            "path": LINE_STRING_OBJECT,        },    },)...

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

При отсутствии в конфигурации динамичной коллекции, генерируемая сервисом HTML-страница не содержит инструкцию на JavaScript, осуществляющую периодический запрос сведений при неизменной локации. Указанное устраняет излишнюю нагрузку на серверную часть сервиса.

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

"Ненагружающий" запоздалый AJAX запрос

Получение сведений клиентской частью от серверной реализовано с использованием асинхронных запросов на JavaScript (AJAX). При этом с целью недопущения исполнения сервисом запросов, являющихся "промежуточными", запросы происходят с некоторой малой задержкой, во время которой данный запрос может быть отменен последующим обращением. То есть результативная выборка данных происходит только в конечном положении наблюдения. Данный подход снижает нагрузку на серверную часть при последовательном переходе от участка карты к участку или при работе с ее масштабированием за счет исключения к ней AJAX-запросов, фактическое исполнение которых не требуется.

 function get_data(...){    ...    if (xhr && !(xhr.readyState === 4)) {        xhr.abort();        console.log('Previous AJAX #' + xhr.__dt + ' was aborted');    }    clearTimeout(timer);    xhr = new XMLHttpRequest();    xhr.responseType = 'json';    xhr.__dt = Date.now();    console.log('Start AJAX #' + xhr.__dt);    timer = setTimeout(function() {        // find objects in area.    }}

Демонстрация работы по старту и прерыванию AJAX-запросов доступна в консоли web-браузера при включении в нем режима "отладки" (F12).

Направления работы

Соответствие координат

MongoDB использует систему координат WGS84 (MongoDB geospatial queries on GeoJSON objects calculate on a sphere; MongoDB uses the WGS84 reference system for geospatial queries on GeoJSON objects) (поиск вглоссариислова "wgs84").

При этом Leaflet по-умолчанию использует систему координат EPSG 3857.

Исходя из описания,EPSG 3857допустима для координат между85.06Sи85.06N.

То есть в рамках рабочей эксплуатации необходимо в Leaflet установить параметр CRS равным"L.CRS.EPSG4326", посколькуонне имеет таких ограничений и целиком соответствует системе геокодирования MongoDB.

Запредельные координаты

К сожалению в данной работе не решен нюанс запроса сведений в области карты, находящейся за пределами стандартных для MongoDB широты и долготы. Например, после изначальной загрузки карты возможно осуществить значительное ее перетаскивание в сторону, и тем самым Leaflet станет запрашивать сведения, подобные этим:

pymongo.errors.OperationFailure: longitude/latitude is out of bounds, lng: 561.213 lat: 89.9823 ... Valid longitude values are between -180 and 180, both inclusive.Valid latitude values are between -90 and 90, both inclusive.

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

-тестирование

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

Расширение числа источников

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

Выводы

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

Вместо заключения

Лучше один раз увидеть, чем сто раз услышать.

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

Спасибо курсам"OTUS" ("OTUS.Web-python" 2018 и "OTUS.NoSQL" 2020)за приобретенный опыт Fullstack-разработки (в частности интеграции Python, Javascript и MongoDB).

Подробнее..
Категории: Python , Otus , Mongodb , Maps api , Leaflet

MongoDB базовые возможности

13.10.2020 00:06:12 | Автор: admin
Цель:
освоить базовые возможности mongodb

Необходимо:
  • установить MongoDB одним из способов: ВМ, докер;
  • заполнить данными;
  • написать несколько запросов на выборку и обновление данных
  • создать индексы и сравнить производительность.


Решение

Установка хранилища

MongoDB было развернуто на машине в локальной сети с использованием Docker-контейнеризации и оболочки www.portainer.io.

Заполнение хранилища данными

Я в поисках данных, достаточных для изучения базовых возможностей MongoDB, остановился на наборе данных NASA Earth Meteorite Landings (1000 строк со сведениями об упавших на Землю метеоритах, из репозитория https://github.com/jdorfman/awesome-json-datasets.

Замечание: нашел более полные (45.7K) сведения https://data.nasa.gov/Space-Science/Meteorite-Landings/gh4g-9sfh, но экспорт в JSON через их API дает только 1000 записей (непонятно), надо перелопатить полные данные экспортируемого CSV файла https://data.nasa.gov/api/views/gh4g-9sfh/rows.csv?accessType=DOWNLOAD?

Замечание: упс, можно получить полные данные в JSON, но это хак. Искренне надеюсь, что в этом нет SQL-инъекции

https://data.nasa.gov/api/id/gh4g-9sfh.json?$select=`name`,`id`,`nametype`,`recclass`,`mass`,`fall`,`year`,`reclat`,`reclong`,`geolocation`&$order=`:id`+ASC&$limit=46000&$offset=0

wc ./gh4g-9sfh.json     45716   128491 10441343 ./gh4g-9sfh.json


Для взаимодействия с сервером в сети поставил на локальной машине только клиента и инструменты:

wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -echo "deb http://repo.mongodb.org/apt/debian buster/mongodb-org/4.4 main" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.listsudo apt-get updatesudo apt-get install -y mongodb-org-shellsudo apt-get install -y mongodb-org-tools


Проверка соединения:

mongo nosql-2020.otus --port 32789    MongoDB shell version v4.4.1    connecting to: mongodb://nosql-2020.otus:32789/test?compressors=disabled&gssapiServiceName=mongodb    Implicit session: session { "id" : UUID("5ff24788-0710-4a1a-821f-7acb2eddfb4f") }    MongoDB server version: 4.4.1    Welcome to the MongoDB shell.    For interactive help, type "help".    For more comprehensive documentation, see            https://docs.mongodb.com/    Questions? Try the MongoDB Developer Community Forums            https://community.mongodb.com


Импорт данных с локальной машины на удаленную:

mongoimport --host nosql-2020.otus  --db "otus_003" --port 32789 --collection eml45k --jsonArray --file ./003_MONGODB.files/gh4g-9sfh.json        2020-10-12T01:01:13.826+0100    connected to: mongodb://nosql-2020.otus:32789/    2020-10-12T01:01:16.827+0100    [#######.................] otus_003.eml45k      2.99MB/9.96MB (30.0%)    2020-10-12T01:01:19.827+0100    [###############.........] otus_003.eml45k      6.44MB/9.96MB (64.6%)    2020-10-12T01:01:22.827+0100    [#######################.] otus_003.eml45k      9.81MB/9.96MB (98.5%)    2020-10-12T01:01:23.035+0100    [########################] otus_003.eml45k      9.96MB/9.96MB (100.0%)    2020-10-12T01:01:23.035+0100    45716 document(s) imported successfully. 0 document(s) failed to import.


Замечание: 10 секунд на все про все, забавненько

Выборка данных

> show databasesadmin     0.000GBconfig    0.000GBlocal     0.000GBotus_003  0.000GBtest      0.000GB> use otus_003switched to db otus_003> show collectionseml


Ищем метеорит по заведомо известному имени:

> db.eml45k.find({name:"Bjelaja Zerkov"})    { "_id" : ObjectId("5f8380a91c0ab84b54bfe394"), "name" : "Bjelaja Zerkov", "id" : "5063", "nametype" : "Valid", "recclass" : "H6", "mass" : "1850", "fall" : "Fell", "year" : "1796-01-01T00:00:00.000", "reclat" : "49.783330", "reclong" : "30.166670", "geolocation" : { "latitude" : "49.78333", "longitude" : "30.16667" } }


Ищем метеорит по заведомо известным координатам:

> db.eml45k.find({ "geolocation" : { "latitude" : "44.83333" , "longitude" : "95.16667" } }){ "_id" : ObjectId("5f8380a91c0ab84b54bfe322"), "name" : "Adzhi-Bogdo (stone)", "id" : "390", "nametype" : "Valid", "recclass" : "LL3-6", "mass" : "910", "fall" : "Fell", "year" : "1949-01-01T00:00:00.000", "reclat" : "44.833330", "reclong" : "95.166670", "geolocation" : { "latitude" : "44.83333", "longitude" : "95.16667" } }


Выборка списка упавших метеоритов с сортировкой по году падения (интересно, почему у NASA нет конкретного времени падения по Гринвичу) и c ограничением списка выбираемых полей:
> db.eml45k.find( { }, {year: 1, id: 1, name: 1, _id: 0 }).sort( { year: -1 } ){ "name" : "Northwest Africa 7701", "id" : "57150", "year" : "2101-01-01T00:00:00.000" }{ "name" : "Chelyabinsk", "id" : "57165", "year" : "2013-01-01T00:00:00.000" }{ "name" : "Northwest Africa 7755", "id" : "57166", "year" : "2013-01-01T00:00:00.000" }{ "name" : "Northwest Africa 7812", "id" : "57258", "year" : "2013-01-01T00:00:00.000" }{ "name" : "Northwest Africa 7822", "id" : "57268", "year" : "2013-01-01T00:00:00.000" }{ "name" : "Northwest Africa 7856", "id" : "57421", "year" : "2013-01-01T00:00:00.000" }{ "name" : "Northwest Africa 7855", "id" : "57420", "year" : "2013-01-01T00:00:00.000" }{ "name" : "Northwest Africa 7857", "id" : "57422", "year" : "2013-01-01T00:00:00.000" }{ "name" : "Northwest Africa 7858", "id" : "57423", "year" : "2013-01-01T00:00:00.000" }{ "name" : "Northwest Africa 7861", "id" : "57425", "year" : "2013-01-01T00:00:00.000" }{ "name" : "Northwest Africa 7862", "id" : "57426", "year" : "2013-01-01T00:00:00.000" }{ "name" : "Northwest Africa 7863", "id" : "57427", "year" : "2013-01-01T00:00:00.000" }{ "name" : "Battle Mountain", "id" : "56133", "year" : "2012-01-01T00:00:00.000" }{ "name" : "Sutter's Mill", "id" : "55529", "year" : "2012-01-01T00:00:00.000" }{ "name" : "Antelope", "id" : "57455", "year" : "2012-01-01T00:00:00.000" }{ "name" : "Catalina 009", "id" : "57173", "year" : "2012-01-01T00:00:00.000" }{ "name" : "Jiddat al Harasis 799", "id" : "57428", "year" : "2012-01-01T00:00:00.000" }{ "name" : "Johannesburg", "id" : "55765", "year" : "2012-01-01T00:00:00.000" }{ "name" : "Ksar Ghilane 011", "id" : "55606", "year" : "2012-01-01T00:00:00.000" }{ "name" : "Ksar Ghilane 010", "id" : "55605", "year" : "2012-01-01T00:00:00.000" }Type "it" for more>


Что-то я не нашел? как объединить два поля непосредственно при выборке:

"geolocation" : { "latitude" : "49.78333", "longitude" : "30.16667" } 


в точку (необходимо заметить изменение порядка следования произведено в соответствии с документацией (https://docs.mongodb.com/manual/geospatial-queries/#geospatial-legacy, то есть

< field >: [< longitude >, < latitude >]):


"geolocation" : { "type" : "Point", "coordinates" : [  30.16667 , 49.78333 ] } }


непосредственно при запросе, чтобы сделать (кто знает?) что-то по типу этого:

> db.eml45k.find({     [         {$toDouble: "$geolocation.longitude"} ,        {$toDouble: "$geolocation.latitude"}     ] : {        $geoWithin: {            $geometry: {                type : "Polygon" ,                coordinates: [[                    ... ,                ]]            }        }    }}) 


Поэтому создал искусственное поле в коллекции:

db.eml45k.updateMany(     {},    [{        $set: {            "pointed_geolocation.type" : "Point",            "pointed_geolocation.coordinates" : [                 { $toDouble : "$geolocation.longitude" } ,                 { $toDouble: "$geolocation.latitude" }             ]        }    }]);{ "acknowledged" : true, "matchedCount" : 45716, "modifiedCount" : 45716 }


и мы наконец-то можем отправиться на поиски необнаруженных метеоритов, упавших в определенном районе:

> db.eml45k.find({     "pointed_geolocation.coordinates" : {        $geoWithin: {            $geometry: {                type : "Polygon" ,                coordinates: [[                    [ 47.0 , 33.0  ],                     [ 47.0 , 65.0 ],                     [ 169.0 , 65.0 ],                    [ 169.0 ,  33.0 ],                    [ 47.0 , 33.0 ]                ]]            }        }    },    'fall': 'Fell'},{     year: {$year: { "$toDate": "$year"}},     "pointed_geolocation.coordinates": 1,     name: 1,     _id: 0 }).sort( { year: -1 } )


Выборка
{ name: Chelyabinsk, pointed_geolocation: { coordinates: [ 61.11667, 54.81667 ] }, year: 2013 }
{ name: Dashoguz, pointed_geolocation: { coordinates: [ 59.685, 41.98444 ] }, year: 1998 }
{ name: Kunya-Urgench, pointed_geolocation: { coordinates: [ 59.2, 42.25 ] }, year: 1998 }
{ name: Sterlitamak, pointed_geolocation: { coordinates: [ 55.98333, 53.66667 ] }, year: 1990 }
{ name: Undulung, pointed_geolocation: { coordinates: [ 124.76667, 66.13889 ] }, year: 1986 }
{ name: Omolon, pointed_geolocation: { coordinates: [ 161.80833, 64.02 ] }, year: 1981 }
{ name: Yardymly, pointed_geolocation: { coordinates: [ 48.25, 38.93333 ] }, year: 1959 }
{ name: Vengerovo, pointed_geolocation: { coordinates: [ 77.26667, 56.13333 ] }, year: 1950 }
{ name: Kunashak, pointed_geolocation: { coordinates: [ 61.36667, 55.78333 ] }, year: 1949 }
{ name: Krasnyi Klyuch, pointed_geolocation: { coordinates: [ 56.08333, 54.33333 ] }, year: 1946 }
{ name: Lavrentievka, pointed_geolocation: { coordinates: [ 51.56667, 52.45 ] }, year: 1938 }
{ name: Pavlodar (stone), pointed_geolocation: { coordinates: [ 77.03333, 52.3 ] }, year: 1938 }
{ name: Kainsaz, pointed_geolocation: { coordinates: [ 53.25, 55.43333 ] }, year: 1937 }
{ name: Ichkala, pointed_geolocation: { coordinates: [ 82.93333, 58.2 ] }, year: 1936 }
{ name: Nikolaevka, pointed_geolocation: { coordinates: [ 78.63333, 52.45 ] }, year: 1935 }
{ name: Brient, pointed_geolocation: { coordinates: [ 59.31667, 52.13333 ] }, year: 1933 }
{ name: Pesyanoe, pointed_geolocation: { coordinates: [ 66.08333, 55.5 ] }, year: 1933 }
{ name: Kuznetzovo, pointed_geolocation: { coordinates: [ 75.33333, 55.2 ] }, year: 1932 }
{ name: Boriskino, pointed_geolocation: { coordinates: [ 52.48333, 54.23333 ] }, year: 1930 }
{ name: Khmelevka, pointed_geolocation: { coordinates: [ 75.33333, 56.75 ] }, year: 1929 }
Type it for more
> it
{ name: Mamra Springs, pointed_geolocation: { coordinates: [ 62.08333, 45.21667 ] }, year: 1927 }
{ name: Demina, pointed_geolocation: { coordinates: [ 84.76667, 51.46667 ] }, year: 1911 }
{ name: Krutikha, pointed_geolocation: { coordinates: [ 77, 56.8 ] }, year: 1906 }
{ name: Barnaul, pointed_geolocation: { coordinates: [ 84.08333, 52.73333 ] }, year: 1904 }
{ name: Tyumen, pointed_geolocation: { coordinates: [ 65.53333, 57.16667 ] }, year: 1903 }
{ name: Ochansk, pointed_geolocation: { coordinates: [ 55.26667, 57.78333 ] }, year: 1887 }



Странно, я не знал, что Челябинский не значится в категории найден.

Агрегируем и найдем сколько найдено и сколько нет:

db.eml45k.aggregate([{ $match: {     "pointed_geolocation.coordinates" : {        $geoWithin: {            $geometry: {                type : "Polygon" ,                coordinates: [[                    [ 47.0 , 33.0  ],                     [ 47.0 , 65.0 ],                     [ 169.0 , 65.0 ],                    [ 169.0 ,  33.0 ],                    [ 47.0 , 33.0 ]                ]]            }        }    }} },    {"$group" : {_id: "$fall", count: { $sum: 1 }}}]){ "_id" : "Fell", "count" : 26 }{ "_id" : "Found", "count" : 63 }


Итого найдено 63 из 89, а __26__ __не__ нашли, так что есть шанс :)

Использование индексов

Удалим все индексы в коллекции от прошлых экспериментов:

db.eml45k.dropIndexes(){        "nIndexesWas" : 1,        "msg" : "non-_id indexes dropped for collection",        "ok" : 1}


Попробуем посмотреть оценочное время исполнения запроса:

db.eml45k.find({     "pointed_geolocation.coordinates" : {        $geoWithin: {            $geometry: {                type : "Polygon" ,                coordinates: [[                    [ 47.0 , 33.0  ],                     [ 47.0 , 65.0 ],                     [ 169.0 , 65.0 ],                    [ 169.0 ,  33.0 ],                    [ 47.0 , 33.0 ]                ]]            }        }    }}).explain("executionStats").executionStats.executionTimeMillis...110...110...109


Итог примерно в среднем 110 секунд.

Проиндексируем:
db.eml45k.createIndex( { "pointed_geolocation" : "2dsphere" } )    {        "ok" : 0,        "errmsg" : "Index build failed: 98b9ead2-c156-4312-81af-1adf5896e3c9: Collection otus_003.eml45k ( 6db2d178-61b5-4627-8512-fcf919fe596f ) :: caused by :: Can't extract geo keys: { _id: ObjectId('5f838e30fb89bd9d553ae27f'), name: \"Bulls Run\", id: \"5163\", nametype: \"Valid\", recclass: \"Iron?\", mass: \"2250\", fall: \"Fell\", year: \"1964-01-01T00:00:00.000\", pointed_geolocation: { type: \"Point\", coordinates: [ null, null ] } }  Point must only contain numeric elements",        "code" : 16755,        "codeName" : "Location16755"    }


Ошибка из-за NULL-значений, я что-то не нашел сходу как (кто знает?) ее при индексировании исключить из индекса, поэтому удалю ключи эти:

db.eml45k.updateMany(    { "pointed_geolocation.coordinates" : [ null , null ] },         [{                 $set: { "pointed_geolocation": null }    }] );


Пробуем опять индекс

db.eml45k.createIndex( { "pointed_geolocation" : "2dsphere" } )        {        "ok" : 0,        "errmsg" : "Index build failed: d33b31d4-4778-4537-a087-58b7bd1968f3: Collection otus_003.eml45k ( 6db2d178-61b5-4627-8512-fcf919fe596f ) :: caused by :: Can't extract geo keys: { _id: ObjectId('5f838e35fb89bd9d553b3b8f'), name: \"Meridiani Planum\", id: \"32789\", nametype: \"Valid\", recclass: \"Iron, IAB complex\", fall: \"Found\", year: \"2005-01-01T00:00:00.000\", reclat: \"-1.946170\", reclong: \"354.473330\", geolocation: { latitude: \"-1.94617\", longitude: \"354.47333\" }, pointed_geolocation: { type: \"Point\", coordinates: [ 354.47333, -1.94617 ] } }  longitude/latitude is out of bounds, lng: 354.473 lat: -1.94617",        "code" : 16755,        "codeName" : "Location16755"    }


Ошибка __longitude/latitude is out of bounds, lng: 354.473 lat: -1.94617__ и в документации https://docs.mongodb.com/manual/geospatial-queries/#geospatial-legacy

    Valid longitude values are between -180 and 180, both inclusive.    Valid latitude values are between -90 and 90, both inclusive.


и 354.47333 не входит в диапазон от -180 до 180.

Очень странно, я сначала думал там нужно поправку везде на минус 180 сделать

(`$subtract: [{ $toDouble : "$geolocation.longitude" }, 180.0]`)


, но в итоге не все так просто.

Какие долготы не в диапазоне:

db.eml45k.find({"pointed_geolocation.coordinates.0": {$lt: -180}} ) # нет таких, так и должно бытьdb.eml45k.find({"pointed_geolocation.coordinates.0": {$lt: 0}} ) #  такие есть, так и должно бытьdb.eml45k.find({"pointed_geolocation.coordinates.0": {$gt: 180}} ) # всего один, и так не должно быть    { "_id" : ObjectId("5f838e35fb89bd9d553b3b8f"), "name" : "Meridiani Planum", "id" : "32789", "nametype" : "Valid", "recclass" : "Iron, IAB complex", "fall" : "Found", "year" : "2005-01-01T00:00:00.000", "reclat" : "-1.946170", "reclong" : "354.473330", "geolocation" : { "latitude" : "-1.94617", "longitude" : "354.47333" }, "pointed_geolocation" : { "type" : "Point", "coordinates" : [ 354.47333, -1.94617 ] } }


В итоге только один метеорит имеет странные координаты. Поискав, выяснил, что этот метеорит Meridiani Planum был случайно найден марсоходом Opportunity в 2005 году
(http://old.mirf.ru/Articles/art2427_2.htm). Это (ВНИМАНИЕ) марсианский метеорит, найден (ВНИМАНИЕ) на Марсе. Вот NASA шутники.

Удалим его из коллекции.

db.eml45k.remove({"id" : "32789"})WriteResult({ "nRemoved" : 1 })


Индексируем
> db.eml45k.createIndex( { "pointed_geolocation" : "2dsphere" } ){        "createdCollectionAutomatically" : false,        "numIndexesBefore" : 1,        "numIndexesAfter" : 2,        "ok" : 1}


Замеряем

db.eml45k.find({     "pointed_geolocation.coordinates" : {        $geoWithin: {            $geometry: {                type : "Polygon" ,                coordinates: [[                    [ 47.0 , 33.0  ],                     [ 47.0 , 65.0 ],                     [ 169.0 , 65.0 ],                    [ 169.0 ,  33.0 ],                    [ 47.0 , 33.0 ]                ]]            }        }    }}).explain("executionStats").executionStats.executionTimeMillis


В итоге при перепроверках 104 107 106

Как-то странно, не очень-то и шустрее.

Удалил индекс, проверил.

Без индекса и с индексом одинаково.

Пробую отдельно для Челябинского:

db.eml45k.find(    {"pointed_geolocation.coordinates" : [ 61.11667, 54.81667 ]}).explain("executionStats").executionStats.executionTimeMillis


без индекса и с индексом одинаково.

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

В итоге, запрос

db.eml45k.find({     "pointed_geolocation" : {        $geoWithin: {            $geometry: {                type : "Polygon" ,                coordinates: [[                    [ 47.0 , 33.0  ],                     [ 47.0 , 65.0 ],                     [ 169.0 , 65.0 ],                    [ 169.0 ,  33.0 ],                    [ 47.0 , 33.0 ]                ]]            }        }    }}).explain("executionStats").executionStats.executionTimeMillis


без индекса 125, 123, 119, 123 миллисекунды, а с индексом 7, 4, 4, 5.

Всё получилось.
Подробнее..
Категории: Nosql , Otus , Mongodb

Перевод Новые Property Wrappers в SwiftUI

27.07.2020 20:17:32 | Автор: admin

Привет, Хабровчане! В конце августа в OTUS стартует новая группа профессионального базового курса Разработчик iOS. Как всегда делимся полезным переводом и приглашаем на бесплатные онлайн-мероприятия: День Открытых Дверей и Быстрый старт в IOS-разработку.




WWDC20 привнес в SwiftUI много новых функций, о которых на протяжении следующих недель я собираюсь рассказывать вам в своем блоге. Начать же сегодня я хочу с главных дополнений к потокам данных SwiftUI в виде новых оберток свойств (Property Wrappers) @StateObject, @AppStorage, @SceneStorage и @ScaledMetric.


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

StateObject


Как вы помните, SwiftUI предоставляет нам обертку свойства @ObservedObject, которая позволяет нам наблюдать за изменениями в модели данных, которая находится за пределами инфраструктуры SwiftUI. Например, это могут быть данные, которые вы извлекаете из веб-службы или локальной базы данных. Основной проблемой @ObservedObject был жизненный цикл. Вы должны хранить его где-то за пределами SwiftUI, чтобы сохранить его во время обновлений представления, например, в SceneDelegate или AppDelegate. Иначе при определенных обстоятельствах вы можете потерять данные, за которые отвечает @ObservedObject.


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


struct CalendarContainerView: View {    @StateObject var viewModel = ViewModel()    var body: some View {        CalendarView(viewModel.dates)            .onAppear(perform: viewModel.fetch)    }}

AppStorage


AppStorage еще одна новая обертка свойства, которую мы получили в этом году. Это идеальный способ хранения небольшого количества данных вида ключ-значение связанных с UserDefaults. AppStorage DynamicProperty, что означает, что SwiftUI обновит ваши представления, как только значение отдельно взятого ключа обновится в UserDefaults. AppStorage идеально подходит для хранения настроек приложения. Давайте посмотрим, как мы можем ее использовать.


enum Settings {    static let notifications = "notifications"    static let sleepGoal = "sleepGoal"}struct SettingsView: View {    @AppStorage(Settings.notifications) var notifications: Bool = false    @AppStorage(Settings.sleepGoal) var sleepGoal: Double = 8.0    var body: some View {        Form {            Section {                Toggle("Notifications", isOn: $notifications)            }            Section {                Stepper(value: $sleepGoal, in: 6...12) {                    Text("Sleep goal is \(sleepGoal, specifier: "%.f") hr")                }            }        }    }}

В приведенном выше примере мы создаем экран настроек, используя обертку свойства AppStorage и представление Form. Теперь мы можем получить доступ к нашим настройкам в любом месте приложения с помощью AppStorage, и как только мы изменим там какие-либо значения, SwiftUI обновит представления.


struct ContentView: View {    @AppStorage(Settings.sleepGoal) var sleepGoal = 8    @StateObject var store = SleepStore()    var body: some View {        WeeklySleepChart(store.sleeps, goal: sleepGoal)            .onAppear(perform: store.fetch)    }}

SceneStorage


В этом году мы получили множество возможностей для управления сценами в SwiftUI без UIKit. В результате у нас появилась новая обертка свойства SceneStorage, которая позволяет нам реализовать правильное восстановление состояния для наших сцен. SceneStorage работает аналогично AppStorage, но вместо UserDefaults использует отдельное хранилище для каждой сцены. Это означает, что каждая сцена имеет свое собственное хранилище, к которому не могут получить доступ другие сцены. Система полностью отвечает за управление хранилищем для каждой сцены, и у вас нет доступа к данным без обертки свойства SceneStorage.


struct ContentView: View {    @SceneStorage("selectedTab") var selection = 0    var body: some View {        TabView(selection: $selection) {            Text("Tab 1").tag(0)            Text("Tab 2").tag(1)        }    }}

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


ScaledMetric


Еще одна новая обертка свойства ScaledMetric. ScaledMetric позволяет нам масштабировать любое двоичное плавающее значение относительно категории размера Dynamic Type. Например, в вашем приложении можно очень легко изменить интервалы в соответствии с категорией размера Dynamic Type. Давайте посмотрим на небольшой пример.


struct ContentView: View {    @ScaledMetric(relativeTo: .body) var spacing: CGFloat = 8    var body: some View {        VStack(spacing: spacing) {            ForEach(0...10, id: \.self) { number in                Text(String(number))            }        }    }}

Как только пользователь изменит настройки Dynamic Type, SwiftUI масштабирует значение интервала и обновит представление.


Заключение


Сегодня мы узнали о новых обертках свойств в SwiftUI. Я считаю, что теперь мы имеем достаточно оберток свойств потоков данных, чтобы охватить любую логику, которая нам потребуется при реализации наших приложений. Не стесняйтесь подписываться на меня в Twitter и задавать свои вопросы, связанные с этой статьей. Спасибо за внимание и хорошей вам недели!




Узнать подробнее о базовом курсе Разработчик iOS



Подробнее..

Перевод REST API в Symfony (без FosRestBundle) с использованием JWT аутентификации. Часть 1

06.07.2020 18:16:50 | Автор: admin

Перевод статьи подготовлен в преддверии старта курса Symfony Framework.





В первой части статьи мы рассмотрим самый простой способ реализации REST API в проекте Symfony без использования FosRestBundle. Во второй части, которую я опубликую следом, мы рассмотрим JWT аутентификацию. Прежде чем мы начнем, сперва мы должны понять, что на самом деле означает REST.


Что означает Rest?


REST (Representational State Transfer передача состояния представления) это архитектурный стиль разработки веб-сервисов, который невозможно игнорировать, потому что в современной экосистеме существует большая потребность в создании Restful-приложений. Это может быть связано с уверенным подъемом позиций JavaScript и связанных фреймворков.


REST API использует протокол HTTP. Это означает, что когда клиент делает какой-либо запрос к такому веб-сервису, он может использовать любой из стандартных HTTP-глаголов: GET, POST, DELETE и PUT. Ниже описано, что произойдет, если клиент укажет соответствующий глагол.


  • GET: будет использоваться для получения списка ресурсов или сведений о них.
  • POST: будет использоваться для создания нового ресурса.
  • PUT: будет использоваться для обновления существующего ресурса.
  • DELETE: будет использоваться для удаления существующего ресурса.

REST не имеет состояний (state), и это означает, что на стороне сервера тоже нет никаких состояний запроса. Состояния остаются на стороне клиента (пример использование JWT для аутентификации, с помощью которого мы собираемся засекьюрить наш REST API). Таким образом, при использовании аутентификации в REST API нам нужно отправить аутентификационный заголовок, чтобы получить правильный ответ без хранения состояния.


Создание проекта Symfony:


Во-первых, мы предполагаем, что вы уже установили PHP и менеджер пакетов Сomposer для создания нового проекта Symfony. С этим всем в наличии создайте новый проект с помощью следующей команды в терминале:


composer create-project symfony/skeleton demo_rest_api


Создание проекта Symfony


Мы используем базовый скелет Symfony, который рекомендуется для микросервисов и API. Вот как выглядит структура каталогов:



Структура проекта


Config: содержит все настройки бандла и список бандлов в bundle.php.
Public: предоставляет доступ к приложению через index.php.
Src: содержит все контроллеры, модели и сервисы
Var: содержит системные логи и файлы кэша.
Vendor: содержит все внешние пакеты.


Теперь давайте установим некоторые необходимые пакеты с помощью Сomposer:


composer require symfony/orm-packcomposer require sensio/framework-extra-bundle

Мы установили sensio/framework-extra-bundle, который поможет нам упростить код, используя аннотации для определения наших маршрутов.


Нам также необходимо установить symphony/orm-pack для интеграции с Doctrine ORM, чтобы соединиться с базой данных. Ниже приведена конфигурация созданной мной базы данных, которая может быть задана в файле .env.



.env файл конфигурации


Теперь давайте создадим нашу первую сущность. Создайте новый файл с именем Post.php в папке src/Entity.


<?php namespace App\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; /**  * @ORM\Entity  * @ORM\Table(name="post")  * @ORM\HasLifecycleCallbacks()  */ class Post implements \JsonSerializable {  /**   * @ORM\Column(type="integer")   * @ORM\Id   * @ORM\GeneratedValue(strategy="AUTO")   */  private $id;  /**   * @ORM\Column(type="string", length=100   *   */  private $name;  /**   * @ORM\Column(type="text")   */  private $description;  /**   * @ORM\Column(type="datetime")   */  private $create_date;  /**   * @return mixed   */  public function getId()  {   return $this->id;  }  /**   * @param mixed $id   */  public function setId($id)  {   $this->id = $id;  }  /**   * @return mixed   */  public function getName()  {   return $this->name;  }  /**   * @param mixed $name   */  public function setName($name)  {   $this->name = $name;  }  /**   * @return mixed   */  public function getDescription()  {   return $this->description;  }  /**   * @param mixed $description   */  public function setDescription($description)  {   $this->description = $description;  }  /**   * @return mixed   */  public function getCreateDate(): ?\DateTime  {   return $this->create_date;  }  /**   * @param \DateTime $create_date   * @return Post   */  public function setCreateDate(\DateTime $create_date): self  {   $this->create_date = $create_date;   return $this;  }  /**   * @throws \Exception   * @ORM\PrePersist()   */  public function beforeSave(){   $this->create_date = new \DateTime('now', new \DateTimeZone('Africa/Casablanca'));  }  /**   * Specify data which should be serialized to JSON   * @link https://php.net/manual/en/jsonserializable.jsonserialize.php   * @return mixed data which can be serialized by <b>json_encode</b>,   * which is a value of any type other than a resource.   * @since 5.4.0   */  public function jsonSerialize()  {   return [    "name" => $this->getName(),    "description" => $this->getDescription()   ];  } }

И после этого выполните команду: php bin/console doctrine:schema:create для создания таблицы базы данных в соответствии с нашей сущностью Post.


Теперь давайте создадим PostController.php, куда мы добавим все методы, взаимодействующие с API. Он должен быть помещен в папку src/Controller.


<?php /**  * Created by PhpStorm.  * User: hicham benkachoud  * Date: 02/01/2020  * Time: 22:44  */ namespace App\Controller; use App\Entity\Post; use App\Repository\PostRepository; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; /**  * Class PostController  * @package App\Controller  * @Route("/api", name="post_api")  */ class PostController extends AbstractController {  /**   * @param PostRepository $postRepository   * @return JsonResponse   * @Route("/posts", name="posts", methods={"GET"})   */  public function getPosts(PostRepository $postRepository){   $data = $postRepository->findAll();   return $this->response($data);  }  /**   * @param Request $request   * @param EntityManagerInterface $entityManager   * @param PostRepository $postRepository   * @return JsonResponse   * @throws \Exception   * @Route("/posts", name="posts_add", methods={"POST"})   */  public function addPost(Request $request, EntityManagerInterface $entityManager, PostRepository $postRepository){   try{    $request = $this->transformJsonBody($request);    if (!$request || !$request->get('name') || !$request->request->get('description')){     throw new \Exception();    }    $post = new Post();    $post->setName($request->get('name'));    $post->setDescription($request->get('description'));    $entityManager->persist($post);    $entityManager->flush();    $data = [     'status' => 200,     'success' => "Post added successfully",    ];    return $this->response($data);   }catch (\Exception $e){    $data = [     'status' => 422,     'errors' => "Data no valid",    ];    return $this->response($data, 422);   }  }  /**   * @param PostRepository $postRepository   * @param $id   * @return JsonResponse   * @Route("/posts/{id}", name="posts_get", methods={"GET"})   */  public function getPost(PostRepository $postRepository, $id){   $post = $postRepository->find($id);   if (!$post){    $data = [     'status' => 404,     'errors' => "Post not found",    ];    return $this->response($data, 404);   }   return $this->response($post);  }  /**   * @param Request $request   * @param EntityManagerInterface $entityManager   * @param PostRepository $postRepository   * @param $id   * @return JsonResponse   * @Route("/posts/{id}", name="posts_put", methods={"PUT"})   */  public function updatePost(Request $request, EntityManagerInterface $entityManager, PostRepository $postRepository, $id){   try{    $post = $postRepository->find($id);    if (!$post){     $data = [      'status' => 404,      'errors' => "Post not found",     ];     return $this->response($data, 404);    }    $request = $this->transformJsonBody($request);    if (!$request || !$request->get('name') || !$request->request->get('description')){     throw new \Exception();    }    $post->setName($request->get('name'));    $post->setDescription($request->get('description'));    $entityManager->flush();    $data = [     'status' => 200,     'errors' => "Post updated successfully",    ];    return $this->response($data);   }catch (\Exception $e){    $data = [     'status' => 422,     'errors' => "Data no valid",    ];    return $this->response($data, 422);   }  }  /**   * @param PostRepository $postRepository   * @param $id   * @return JsonResponse   * @Route("/posts/{id}", name="posts_delete", methods={"DELETE"})   */  public function deletePost(EntityManagerInterface $entityManager, PostRepository $postRepository, $id){   $post = $postRepository->find($id);   if (!$post){    $data = [     'status' => 404,     'errors' => "Post not found",    ];    return $this->response($data, 404);   }   $entityManager->remove($post);   $entityManager->flush();   $data = [    'status' => 200,    'errors' => "Post deleted successfully",   ];   return $this->response($data);  }  /**   * Returns a JSON response   *   * @param array $data   * @param $status   * @param array $headers   * @return JsonResponse   */  public function response($data, $status = 200, $headers = [])  {   return new JsonResponse($data, $status, $headers);  }  protected function transformJsonBody(\Symfony\Component\HttpFoundation\Request $request)  {   $data = json_decode($request->getContent(), true);   if ($data === null) {    return $request;   }   $request->request->replace($data);   return $request;  } }

Здесь мы определили пять маршрутов:


GET /api/posts: вернет список постов.



api получения всех постов


POST /api/posts: создаст новый пост.



api добавления нового поста


GET /api/posts/id: вернет пост, соответствующий определенному идентификатору,



получение конкретного поста


PUT /api/posts/id: обновит пост.



обновление поста


Это результат после обновления:



пост после обновления


DELETE /api/posts/id: удалит пост.



удаление поста


Это результат получения всех постов после удаления поста с идентификатором 3:



все посты после удаления


Исходный код можно найти здесь


Заключение


Итак, теперь мы понимаем, что такое REST и Restful. Restful API должен быть без состояний. Мы знаем, как создать Restful-приложение, используя HTTP-глаголы. В общем, теперь мы хорошо понимаем REST и готовы профессионально создавать Restful-приложения.


В следующей статье мы рассмотрим, как обеспечить секьюрность API с помощью JWT аутентификации.




Узнать подробнее о курсе Symfony Framework



Подробнее..

Перевод Управление секретами в Symfony

13.07.2020 18:21:24 | Автор: admin

Перевод статьи подготовлен в преддверии старта курса Symfony Framework.




Не надоело ли вам хранить файл с секретами вашего приложения в менеджере паролей и копировать его в свою среду CI/CD каждый раз, когда вы изменяете его для развертывания приложения в соответствии с требованиями безопасности?


Запускаем Symfony


Создайте docker-compose.yml в корневом каталоге вашего проекта и добавьте следующее:


(См. знакомство с PHP Docker разработкой с XDEBUG здесь)


version: '3'services:  php:    image: webdevops/php-nginx-dev:7.4    working_dir: /app    environment:      - WEB_DOCUMENT_ROOT=/app/public      - PHP_DISPLAY_ERRORS=1      - PHP_MEMORY_LIMIT=2048M      - PHP_MAX_EXECUTION_TIME=-1      - XDEBUG_REMOTE_AUTOSTART=1      - XDEBUG_REMOTE_PORT=9000      - XDEBUG_PROFILER_ENABLE=0      - XDEBUG_REMOTE_CONNECT_BACK=0      - XDEBUG_REMOTE_HOST=docker.for.mac.localhost      - php.xdebug.idekey=PHPSTORM      - php.xdebug.remote_enable=1      - php.xdebug.max_nesting_level=1000    ports:      - "8080:80"    volumes:      - ./:/app:rw,cached    depends_on:      - mysql  mysql:    image: mysql:5.7    ports:      - "3306:3306"    environment:      MYSQL_ROOT_PASSWORD: root      MYSQL_DATABASE: test      MYSQL_USER: test      MYSQL_PASSWORD: test

Запустите сервис с помощью docker-compose up и установите Symfony 5 для запуска приложения:


docker-compose exec php bash -c 'composer create-project symfony/website-skeleton project && mv project/* . && rm -rf project'

Теперь вы можете перейти на http://localhost:8080 и увидеть следующее:



Дефолтный экран Symfony


Храним секреты в хранилище


Начиная с Symfony 4.4, вы имеете доступ к встроенному управление секретами, которое позволяет хранить все секреты в хранилище (vault) в зашифрованном виде. Вы также имеете возможность расшифровывать свои секреты с помощью приватного ключа или ключевой фразы. С помощью этой команды мы можем безопасно сохранить нашу строку подключения к БД, которая в настоящее время находится в файле .env:


php bin/console secrets:set DATABASE_URL


Как мы видим, команда создала хранилище, пару ключей и сообщает вам, что не следует выполнять расшифровку ключа. Мы можем проверить файлы, которые были сгенерированы в config/secrets/dev:



Мы видим наш ключ, приватный (закрытый) ключ дешифрования и публичный (открытый) ключ шифрования, которые можно коммитить. Еще есть листинговый php-файл, который содержит следующее содержимое для перечисления всех зашифрованных ключей:



Он используется для секретов php bin/console:list и показывает следующее:



Как вы могли заметить, Symfony хранит секреты, разделенные по средам. Поэтому, чтобы не блокировать работу других разработчиков в вашей команде, просто закомментируйте dev-файлы и все готово.Вы не должны коммитить приватный ключ дешифрования для файлов производственного хранилища. Добавление секретов разработчиков на данный момент работает следующим образом:


git add config/secrets

Когда мы пытаемся выполнить запрос к базе данных, он должен просто сработать, потому что Symfony извлекает параметры %env%, если они не предоставлены в качестве переменных среды из хранилища. В файле конфигурации пакета doctrine вы видите следующее:



config/packages/doctrine.yaml


Мы можем проверить, работает ли соединение:


php bin/console doctrine:query:sql "SHOW VARIABLES LIKE 'max_join_size'"

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


Производственное хранилище


Чтобы сохранить параметр DATABASE_URL в производственном хранилище (production vault), мы запускаем команду:


php bin/console secrets:set --env=prod DATABASE_URL

Она создает производственное хранилище со всеми необходимыми ключами и устанавливает DATABASE_URL с указанным значением. Нужно следить за тем, чтобы приватный ключ дешифрования не был добавлен систему контроля версий. Этот момент уже был учтен при установке Symfony с помощью composer, поэтому мы видим следующую строку в файле .gitignore:


/config/secrets/prod/prod.decrypt.private.php

Symfony такой умный!


Извлекаем секретные значения


Чтобы перечислить все значения, которые мы добавили в хранилище, нам нужно использовать команду secrets:list. Чтобы увидеть значение секретов, вы можете передать в команду опцию под названием reveal:



Для производственных значений используйте:


php bin/console secrets:list --env=prod --reveal

Теперь у нас есть секреты в системе контроля версий для нескольких сред. Это довольно круто!


Развертывание приложения


У нас есть только одно конфиденциальное значение приватный ключ дешифрования. Поэтому во время развертывания нам нужно добавить в проект приватный файл дешифровки или разворачивать с переменной среды с именем SYMFONY_DECRYPTION_SECRET.


Когда мы хотим использовать переменную SYMFONY_DECRYPTION_SECRET, нам нужно предоставлять значение приватного ключа дешифрования, закодированное с помощью base64, например, как здесь:


php -r "echo base64_encode(require 'config/secrets/prod/prod.decrypt.private.php');"

Когда вы используете систему сборки, такую как Jenkins или Gitlab, вы можете внедрить это как переменную среды и передать ее в вашу докерезированную сборку или добавить весь файл в файлы сборки и разворачивать их. Для этого Jenkins предлагает функцию работы с секретными файлами:



функция работы с секретными файлами Jenkins


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


php bin/console secrets:decrypt-to-local --force --env=prod

Эта команда генерирует .env.prod.local файл. Таким образом, ваше приложение Symfony будет использовать переменные env из этого файла, и не будет потери производительности из-за расшифровки секретов во время выполнения. Это великолепная возможность, которая не влияет на безопасность. Если кто-то имеет доступ к вашему серверу, он может скопировать приватный ключ дешифрования или просто файл .env.prod.local.


Локальное хранилище для переопределений во время разработки


Как переопределить секреты во время разработки? Вот локальное хранилище, которое мы можем использовать для переопределения DATABASE_URL для проверки чего-либо в другой БД. Добавьте локальное значение как здесь:


php bin/console secrets:set DATABASE_URL --local


Но что произойдет теперь? Локальные переопределения учетных данных хранятся в .env.dev.local! Так что не нужно никакого дополнительного хранилища и, конечно, Symfony уже не добавил строку в файл .gitignore, чтобы не дать вам ее закоммитить. Кроме того, записи в .env файле переопределяются, так что вы можете безопасно так делать. Единственный важный момент, который требует внимания, это то, что Symfony сначала ищет переменные env, а затем секреты. Так что с переменными env вы всегда можете переопределить секретные значения.


Подытожим


  • Env переменные в первую очередь, а секреты во вторую
  • Команда secret:set автоматически разрешает обработку dev, local и prod env
  • Symfony предупреждает возможные проблемы с git с помощью записей в .gitignore



Узнать подробнее о курсе Symfony Framework.



Подробнее..

Перевод PHP Internals News Эпизод 38 предзагрузка и WeakMaps

24.07.2020 20:08:25 | Автор: admin

Перевод транскрипции подкаста подготовлен в преддверии старта курса Backend-разработчик на PHP





Описание


В этом эпизоде PHP Internals News я беседую с Никитой Поповым (Twitter, GitHub, Сайт) о проблемах предзагрузки PHP 7.4 и его RFC WeakMaps.


СКАЧАТЬ MP3


Стенограмма


Дерик Ретанс 0:16
Привет, я Дерик. И это PHP internals news еженедельный подкаст, посвященный демистификации развития языка PHP. Это 38-й эпизод. Я собираюсь обсудить с Никитой Поповым несколько вещей, которые произошли за время праздников. Никита, как прошли твои праздники?


Никита Попов 0:34
Мои праздники прошли замечательно.


Дерик Ретанс 0:36
Я подумал начать беседу не так, как в прошлом году. В любом случае, сегодня утром я хочу поговорить с тобой о том, что произошло с PHP 7.4 в эти праздничные дни. А именно о проблемах с предзагрузкой в PHP 7.4 на Windows. Я понятия не имею, в чем собственно проблема. Не мог ли бы ты мне это объяснить?


Никита Попов 0:56
На самом деле в ранних версиях PHP 7.4 было довольно много проблем с предзагрузкой. Эта фича определенно была не достаточно протестирована. Большинство проблем было исправлено в 7.4.2. Но если вы используете preload-user (собственно то, что вы должны использовать, если вы работаете в руте), то вы, вероятно, все равно столкнетесь с крашами, и это будет исправлено только в следующем релизе.


Дерик Ретанс 1:20
В 7.4.3.


Никита Попов 1:22
Да. Но вернемся к Windows. Windows имеет совершенно другую архитектуру процессов, нежели Linux. В частности, на Linux или BSD у нас есть fork, который в целом просто берет процесс и копирует все его состояние в памяти, чтобы создать новый процесс. Это намного дешевле, чем кажется, потому что это повторное использование памяти до тех пор, пока она на фактически не изменится.


Дерик Ретанс 1:48
Это копирование при записи.


Никита Попов 1:49
Именно, копирование при записи. Эта же функциональность не существует в Windows, или, по крайней мере, она не доступна для широкого применения. Таким образом, в Windows вы можете создавать новые процессы только с нуля без повторного использования памяти из предыдущих. А для OPcache это проблема, потому что OPcache хотел бы ссылаться на внутренние классы, как определяет PHP. Но поскольку мы храним объекты в общей памяти, которая совместно используется несколькими процессами, у нас возникает проблема, заключающаяся в том, что эти внутренние классы могут находиться по разным адресам в разных процессах. В Linux мы всегда будем иметь один и тот же адрес, потому что мы используем fork, и адрес сохраняется. В Windows у каждого процесса может быть свой адрес. И особенно потому, что Windows, насколько я помню, начиная с Windows Vista, использует рандомизацию адресного пространства. Скорее даже почти всегда это будет другой адрес.


Дерик Ретанс 2:51
Из соображений безопасности?


Никита Попов 2:52
Именно. Из соображений безопасности.


Дерик Ретанс 2:54
Если вместо форка вы будете запускать новый процесс, будет ли это проблемой для Linux?


Никита Попов 2:59
Да, это будет проблемой. Разница в том, что в Unix мы так не делаем. OPcache в Windows имеет совершенно другую архитектуру. В Linux мы не разрешаем подключаться к существующему OPcache из отдельного процесса. Таким образом, единственный способ разделить OPcache это использовать fork. В Windows из-за ограничения в виде отсутствия fork, мы разрешаем такие подключения, и именно здесь нам приходится иметь дело с целым рядом проблем. Так что на самом деле это общая проблема, а не только предзагрузки, разница только в том, что обычно мы можем просто сказать: что ж, ладно, мы не разрешаем никаких ссылок на внутренние классы из общей памяти в Windows. Это небольшой недостаток со стороны оптимизации, но это не супер важно. Но что до предварительной загрузки, мы должны связать весь граф классов во время ее выполнения. И если у вас есть классы, которые расширяются из внутреннего класса, например, из Exception. Или, в некоторых случаях, вы можете просто использовать внутренний класс в качестве подсказки типа, тогда вы не сможете хранить ссылки такого рода в общей памяти в Windows. И так как для предварительной загрузки почти неизбежно, что вы попадаете в подобную ситуацию, получается, что вы просто не можете использовать предзагрузку в Windows.


Дерик Ретанс 4:18
Следовательно, напрашивается решение отключить ее, вместо того, чтобы пытаться заставить ее работать и практически всегда терпеть неудачу.


Никита Попов 4:24
Ага, я имею в виду, раньше она с горем пополам работала, просто вы получили кучу предупреждений, что такие классы не были предварительно загружены. И люди могут попасть в ситуацию, когда они попробуют ее на простых примерах и по результатам приходят к выводу, что предварительная загрузка отлично работает. Но как только они переходят к своему действительно сложному приложению, которое использует внутренние классы в различных местах, оказывается, что на самом деле, нет, на практике это не работает. И поэтому единственный способ избежать проблем отключит ее.


Дерик Ретанс 4:51
Кажется, пока это самое разумное решение, но как ты думаешь, в какой-то момент это можно будет исправить другим умным способом?


Никита Попов 4:58
Основной способ, с помощью которого с этим можно бороться, это избегать многопроцессных вложений в Windows. Альтернативой наличию нескольких процессов является наличие нескольких потоков, которые совместно используют адресное пространство. По сути, то же самое, что fork, только с потоками. Но это, конечно, зависит от того, какой веб-сервер и SAPI вы используете. И я думаю, что в настоящее время в Windows многопоточные веб-серверы несколько популярнее, чем в Linux, но это все еще не главный тренд в разработке.


Дерик Ретанс 5:34
Я думаю, что раньше модели потоковых процессов в Windows были гораздо более распространены, когда PHP только вышел для Windows, потому что это был модуль ISAPI, который всегда был потоковым. Насколько я помню, это основная причина, из-за которой у нас был ZTS, в первую очередь. Да, в какой-то момент они начали переходить на модели PHP FPM, потому что они не использовали многопоточность, и были таким образом гораздо безопаснее в использовании.


Никита Попов 5:57
Верно. Я имею в виду, что у потоков есть проблемы, в частности, потому что такие вещи, как локали, относятся к процессу, а не к потоку. Таким образом, процессы, как правило, безопаснее использовать.


Дерик Ретанс 6:08
Произошло ли что-нибудь еще интересное, что пошло не так с предзагрузкой, или вы не хотите об этом рассказывать?


Никита Попов 6:12
Остальное проистекает в основном из того, что у нас есть два разных способа реализовать предварительную загрузку. Один использует файл компиляции OPcache, а другой используют require или include, и разница между ними заключается в том, что файл компиляции OPcache объединяет файл, но не выполняется. В этом случае способ, которым мы выполняем предварительную загрузку, заключается в том, что мы сначала собираем все классы, а затем постепенно их связываем, фактически регистрируем их, всегда следя за тем, чтобы все зависимости уже были связаны. И это тот способ, который, как мне кажется, в целом хорошо работал с релиза PHP 7.4. И другой, require подход, в котором, собственно, require непосредственно выполняет код и регистрирует классы. И в этом случае, если оказывается, что какая-то зависимость не может быть предварительно загружена по какой-либо причине, мы просто должны прервать предзагрузку, потому что мы не сможем восстановиться после этого. Это прерывание отсутствовало. И как оказалось, в конце концов, люди на практике используют предзагрузку, используя require подход, а не подход с использованием файла компиляции OPcache.


Дерик Ретанс 7:26
Хотя это тот пример, который можно наблюдать в большинстве примеров, которые я видел, и в документации.


Никита Попов 7:30
Да, у него есть некоторые преимущества по сравнению с require.


Дерик Ретанс 7:34
Что еще произошло за праздники, так это то, что вы работали над несколькими RFC, о которых можно говорить слишком долго, чтобы это поместилось в этом эпизоде. Но одним из более ранних был WeakMap или WeakMaps RFC, который был построен на основе слабых ссылок, которые мы уже имеем в PHP 7.4. Что не так со слабыми ссылками, и зачем нам понадобились слабые ассоциативные массивы?


Никита Попов 7:58
Со слабыми ссылками все в порядке. Просто напомню, что представляют из себя слабые ссылки они позволяют ссылаться на объект, не исключая его из цикла сборки мусора. Так что, если объект будет уничтожен, то у вас останется висячая ссылка. Если вы попытаетесь получить к нему доступ, вы получите некоторую информацию об объекте. На сегодняшний день, вероятно, наиболее распространенным вариантом использования любой слабой структуры данных является ассоциативный массив (map), где у вас есть объекты и вы хотите связать с ними какие-то данные. Типичными вариантами использования являются кэши и другие мемоизирующие структуры данных. И причина, по которой важно, чтобы эта структура была слабой, заключается в том, что вы бы не хотели хммм, скажем, если вы хотите кэшировать некоторые данные связанные с объектом, но никто другой в итоге так и не использует этот объект, вы бы не хотели продолжать хранить эти данные в кэше, потому что никто никогда не будет использовать их снова. Они будут только бессмысленно занимать память. И здесь выходит на поле WeakMap. Здесь вы используете объекты в качестве ключей, а в качестве значения какие-то данные. И если объект больше не используется за пределами этого ассоциативного массива, он также удаляется из него.


Дерик Ретанс 9:16
Итак, вы упомянули объекты в качестве ключей. Это что-то новенькое? Потому что я не думаю, что в настоящее время PHP поддерживает это.


Никита Попов 9:22
Да, вы не можете использовать объекты в качестве ключей в обычных массивах. Это не сработает. Но, например, интерфейсу ArrayAccess и интерфейсу Traversable все равно, какие у вас типы. Так что вы можете использовать что угодно в качестве ключей.


Дерик Ретанс 9:37
Я освежил это в памяти, да. Но weak map это то, что затем реализует ArrayAccess.


Никита Попов 9:44
Правильно.


Дерик Ретанс 9:45
Как выглядит интерфейс Weak Map? Как бы вы с ним взаимодействовали?


Никита Попов 9:49
Ну, на самом деле, он просто реализует все магические интерфейсы из PHP. Таким образом, ArrayAccess вы можете получить доступ к weak map по ключу, где ключ это объект. Traversable то есть вы можете перебирать weak map и получать ключи и значения, и, конечно, Countable, так что вы можете подсчитать, сколько там элементов. Вот и все.


Дерик Ретанс 10:12
Все эти методы, их там предостаточно, должно быть девять или десять, или около того, верно?


Никита Попов 10:17
Пять.


Дерик Ретанс 10:18
Нет, там еще шестерка итераторов.


Никита Попов 10:20
Правильно, да, есть маленькая деталь, когда при реализации внутренних классов Traversable вам на самом деле не нужно реализовывать методы итератора. Вот почему их там несколько меньше.


Дерик Ретанс 10:33
Кто получит выгоду от этой новой фичи?


Никита Попов 10:35
Один из пользователей weak map, такие вещи как ORM. Где, ну, записи базы данных представлены как объекты, и есть хранилище данных, связанных с этими объектами. И я думаю, что это хорошо известная проблема, что если вы используете ORM, вы можете иногда сталкиваться с Memory Usage проблемами. И отсутствие слабых структур является одной из причин, почему это может произойти. Так как они просто продолжают держаться за информацию, хотя приложение на самом деле больше не использует ее.


Дерик Ретанс 11:12
Запрашивал ли какой-то конкретный ORM эту функцию?


Никита Попов 11:15
Я так не думаю.


Дерик Ретанс 11:16
Поскольку weak maps это что-то вроде внутреннего класса в PHP, как эти вещи реализованы? Есть ли что-то интересное? Потому что я помню, как говорил с Джо о слабых ссылках в прошлом году есть некоторая функциональность, когда она автоматически что-то делает с деструктором или, скорее, с объектами. Это то, что также происходит с weak maps?


Никита Попов 11:37
Итак, механизм работы слабых ссылок и ассоциативных массивов практически одинаков. Таким образом, на каждом объекте есть флаг, который можно установить, чтобы указать, что на него ссылается слабая ссылка или слабый ассоциативный массив. Если объект уничтожен и имеет этот прекрасный флаг, то мы выполняем колбек, который собирается удалить объект из Weak Reference или Weak Map, или сразу из нескольких ассоциативных массивов.


Дерик Ретанс 12:05
Это потому, что существует какой-то реестр, который связывает объект?


Никита Попов 12:08
Потому что мы храним объекты как часть слабые ссылок, и слабых ассоциативных массивов, мы можем эффективно удалить его.


Дерик Ретанс 12:16
Когда я читал RFC, я увидел упоминание чего-то вроде SPL ID объекта, что является способом, как в основном идентифицировать конкретный объект. Это связано со слабыми ссылками или слабыми ассоциативными массивами? Или это что-то, что больше не используется, или люди больше не должны использовать это в значительной степени, потому что, я предполагаю, что ранее это был способ идентифицировать объект и затем связывать с ним дополнительные данные. Как ты упоминал, что ORM должны были делать для кэширования.


Никита Попов 12:44
Верно. Это как-то связано, и в то же время нет. Одно не является заменой другого, это просто разные варианты использования. Раньше у нас очень долгое время был SPL хеш объекта. И я думаю, где-то в PHP 7.0, или, может быть, позже был введен SPL ID объекта, который является целым числом и потому более эффективен. Но, в конце концов, эти функции возвращают уникальный идентификатор объекта. Но этот идентификатор уникален только до тех пор, пока объект жив. Эти ID объектов используются повторно при уничтожении объектов.


Дерик Ретанс 13:30
И это делает их непригодными для связи данных кэша с конкретным объектом?


Никита Попов 13:35
Это делает их пригодными для связывания данных кэша. Но вы также должны хранить объект, чтобы быть уверенным, что он не будет уничтожен. Так вы обходите ограничение, что вы не можете использовать объекты в качестве ключей массива. Вот для чего вам нужен ID. Но вы все равно должны хранить сильную ссылку на объект, чтобы убедиться, что он не мусор. Ведь этот ID начинает ссылаться на какие-то другие объекты.


Дерик Ретанс 14:04
Когда вы говорите Сильная ссылка, это то, чем традиционно являются ссылки в PHP?


Никита Попов 14:08
Это обычная ссылка.


Дерик Ретанс 14:10
Ну, потому что прошло довольно много времени с тех пор, как RFC было представлено до того, как оно было принято, насколько я понял?


Никита Попов 14:16
Оно принято: 25, ноль


Дерик Ретанс 14:18
25, ноль. Это случается не очень часто.


Никита Попов 14:22
Большинство RFC, возможно, не являются анонимными, но, как правило, они либо приняты на 95%, либо строго отвергнуты. Бывает не много промежуточных решений.


Дерик Ретанс 14:34
Это очень хорошо. В любом случае, мы увидим это в PHP 8, как я полагаю, в конце года.


Никита Попов 14:39
Все верно. Да.


Дерик Ретанс 14:41
Ну, спасибо, что нашли время поговорить со мной о слабых ссылках и предварительной загрузке, особенно в Windows. Спасибо, что нашли время.


Никита Попов 14:50
Спасибо за то, что пригласили меня Дерик.


Дерик Ретанс 14:52
Спасибо за прослушивание этой серии PHP internals news, еженедельного подкаста, посвященного демистификации развития языка PHP. Я веду учетную запись Patreon для поклонников этого подкаста, а также для инструмента отладки Xdebug. Вы можете подписаться на Patreon по адресу. Если у вас есть комментарии или предложения, не стесняйтесь присылать их по электронной почте derick@phpinternals.news. Спасибо, что слушали, и увидимся на следующей неделе.


Заметки к подкасту


RFC: WeakMaps




Подробнее о курсе Backend-разработчик на PHP



Подробнее..

Прозрачные процессы тестирования на удалёнке

15.07.2020 18:16:18 | Автор: admin

Публикуем статью Анастасии Шариковой QA Lead в Bookmate и преподавателя профессионального курса QA Lead, с программой которого мы приглашаем вас ознакомиться!


Также приглашаем на бесплатный пробный открытый урок Тестовое покрытие по Бейзеру, где Анастасия Асеева-Нгуен (эксперт по инженерным практикам в Tinkoff Group) рассмотрит основные подходы для построения тестовой модели, расскажет, что такое test coverage и code coverage, покажет способы подсчета тестового покрытия, а также подробно раскроет темы: цикломатическая сложность, использование статических анализаторов для расчета тестового покрытия, диаграммы для подсчета тестового покрытия.




Всем привет!
Меня зовут Анастасия Шарикова, я руковожу отделом тестирования в Bookmate и веду Telegram-канал Yet another QA.


В последнее время все больше компаний переводят свои команды тестирования на удаленную работу, и, скорее всего, после карантина многие так и останутся работать на дому. Бесспорно, в этом есть и свои плюсы, но есть и довольно большое количество сложностей, с которыми придется столкнуться как руководителям, так и самим сотрудникам. Поговорим сегодня о них и о некоторых способах, которые могут улучшить положение.


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


Одна из основных проблем сохранение продуктивности при прежнем или изменившемся объеме работы. Тут дело и в стрессе, и в новой обстановке, и в окружении. При этом сложности могут возникнуть как в рамках работы внутри команды, так и в общении с заказчиками/коллегами по командам.


Внятное рабочее время


Организуйте внятное понимание рабочего времени! Соблазн работать с утра до ночи в половину силы вместо нормальных рабочих 8 часов велик, да и вероятность выгорания от работы по 12 активных часов тоже. Простые практики, которые помогут всем:


  • заранее прописанное расписание доступности команды,
  • пометки статусов работа/отдых,
  • расшаренный календарь доступности

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


Старайтесь больше общаться в мессенджерах


Вариант не совсем актуален для тех, чья работа и так там проходила, но более важный для тех, кто до этого больше контактировал с коллегами вживую или через письма. Зачем? И для продуктивности, и для более быстрого решения проблем. Также не стоит забывать про психическое здоровье коллег от затворничества можно еще как устать.


Пример: если вы работаете в продуктовой команде, можно создавать чаты в Slack или подобных мессенджерах под конкретные фичи, так вы быстрее сможете актуализировать статусы по задачам и находить последнюю информацию.

Не забывайте про видео и аудио


Нет, конечно карантин не повод писать коллегам войсы в Whatsapp. Но вот договориться о том, что раз в неделю все встречаются с видео на, например, планирование полезно. Например, многие лучше настраиваются на работу, если им с утра пришлось все-таки вылезти из кровати и одеться. А еще прекрасный повод не забыть, как выглядят ваши коллеги онлайн корпоративы по пятницам и совместные видеообеды.


Используйте больше технических возможностей ваших инструментов


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


А еще это прекрасный шанс наконец-то попробовать возможности таких инструментов, как, например, Miro, Notion или Trello для визуализации и совместной работы. Например, для работы над большой задачей или для создания чек-листов для командной работы.


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


Внедряйте демо в процессы


Несмотря на то, что эта практика довольно распространена в разработке, в вынужденной удаленке ее актуальность все выше. Проведение демо позволит синхронизироваться команде на более ранних этапах и, опять же, поможет всем не просто прочитать задачу, но и увидеть результаты работы коллег. Где проводить? Вариантов множество, от Hangouts до Slackа.


Сделайте более прозрачным доступ к устройствам


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


Ищите баланс


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


И в качестве вывода


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


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




Узнать подробнее о курсе QA Lead



Подробнее..

Перевод Используем nftables в Red Hat Enterprise Linux 8

15.07.2020 18:16:18 | Автор: admin

Статья подготовлена в преддверии старта курса Администратор Linux




В Red Hat Enterprise Linux 8 приоритетным низкоуровневым решением является nftables. В этой статье мы поговорим о том, как начать использовать nftables. Наиболее актуальной она будет для системных администраторов и DevOps-специалистов. Там, где это имеет смысл, мы поговорим о различиях между nftables и его предшественником iptables.


Для начала необходимо отметить, что nftables это userland-утилита, nft и подсистема ядра. Внутри ядра она строится на базе подсистемы netfilter. В этой статье мы будем говорить о взаимодействии пользователя с утилитой nft.


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


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

Начнем


Итак, как будет выглядеть настройка nftables по-умолчанию? Давайте выясним, выведя весь набор правил.


# nft list ruleset

В результате мы получим ничего. Ладно, это не очень интересно. О чем это говорит?


По умолчанию nftables не создает таблицы и цепочки, в отличие от своего предшественника iptables. Пустой набор правил имеет нулевую стоимость ресурсов. Для сравнения, вспомните iptables, где каждая предварительно созданная таблица и цепочка должны были учитываться, а базовые счетчики пакетов увеличивались, даже если в них было пусто.


Создание таблиц


В nftables вам понадобится создавать таблицы вручную. Таблицы должны определять семейство: ip, ip6, inet, arp, bridge или netdev. Здесь inet означает, что таблица будет обрабатывать пакеты ipv4 и ipv6. Именно это семейство мы и будем использовать в статье.


Примечание: Для тех, кто переходит с iptables, термин таблица может звучать неоднозначно. В nftables таблица просто пространство имен, ни больше, ни меньше. По сути, она представляет из себя набор цепочек, правил, наборов и иных объектов.

Давайте создадим нашу первую таблицу и перечислим набор правил.


# nft add table inet my_table# nft list rulesettable inet my_table {}

Отлично, теперь у нас есть таблица, но сама по себе она мало что дает. Давайте перейдем к цепочкам.


Создание цепочек


Цепочки объекты, которые будут содержать правила брандмауэра.


По аналогии с таблицами, цепочки также нужно создавать вручную. При создании цепочки необходимо указать, к какой таблице она относится, а также тип, хук и приоритет. В этом руководстве мы не будем ничего усложнять и используем filter, input, и priority 0 для фильтрации пакетов, предназначенных для хоста.


# nft add chain inet my_table my_filter_chain { type filter hook input priority 0 \; }

Примечание: Обратный слэш () нужен, чтобы shell не интерпретировал скобку как конец команды.

Цепочки также могут быть созданы без указания хука. Созданные таким образом цепочки эквиваленты цепочкам, созданным пользователями в iptables. Правила могут использовать операторы jump или goto для выполнения правил в цепочке. Эта механика полезна для логического разделения правил или для того, чтобы делиться подмножеством правил, которые в противном случае продублировались бы.


# nft add chain inet my_table my_utility_chain

Создание правил


Теперь, когда вы создали таблицу и цепочку, вы можете, наконец, добавить правила для брандмауэра. Давайте добавим правило для разрешения SSH.


# nft add rule inet my_table my_filter_chain tcp dport ssh accept

Здесь следует отметить, что как только мы добавили его в таблицу семейства inet, это единственное правило будет обрабатывать как пакеты IPv4, так и пакеты IPv6.


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


# nft insert rule inet my_table my_filter_chain tcp dport http accept

Мы добавили два правила, а теперь давайте посмотрим, как будет выглядеть полный набор правил.


# nft list rulesettable inet my_table {    chain my_filter_chain {    type filter hook input priority 0; policy accept;    tcp dport http accept    tcp dport ssh accept    }}

Обратите внимание, что правило http должно отрабатывать раньше правила ssh, поскольку мы использовали выше глагол insert.


Также вы можете добавить правило в произвольное место в цепочке. Есть два способа сделать это.


  1. Используйте index, чтобы указать на индекс в списке правил. Использование add добавит новое правило после указанного индекса. Если же вы используете insert, то новое правило будет добавлено перед правилом с заданным индексом. Значения индексов начинаются с 0.

# nft insert rule inet my_table my_filter_chain index 1 tcp dport nfs accept# nft list rulesettable inet my_table {    chain my_filter_chain {    type filter hook input priority 0; policy accept;    tcp dport http accept    tcp dport nfs accept    tcp dport ssh accept    }}# nft add rule inet my_table my_filter_chain index 0 tcp dport 1234 accept# nft list rulesettable inet my_table {    chain my_filter_chain {    type filter hook input priority 0; policy accept;    tcp dport http accept    tcp dport 1234 accept    tcp dport nfs accept    tcp dport ssh accept    }}

Примечание: Использование index с insert по факту эквивалентно опции iptables -I с индексом. Первое, о чем нужно помнить, так это о том, что индексы в nftables начинаются с 0. Во-вторых, индекс должен указывать на существующее правило. То есть писать "nft insert rule index 0" в пустой цепочке недопустимо.

  1. Используйте handle, чтобы указать правило, до или после которого нужно вставлять другое правило. Для вставки после используйте глагол add. Чтобы вставить до правила, используйте insert. Вы можете получить handle правил, добавив флаг handle при выводе правил.

# nft --handle list rulesettable inet my_table { # handle 21    chain my_filter_chain { # handle 1    type filter hook input priority 0; policy accept;    tcp dport http accept # handle 3    tcp dport ssh accept # handle 2    }}# nft add rule inet my_table my_filter_chain handle 3 tcp dport 1234 accept# nft insert rule inet my_table my_filter_chain handle 2 tcp dport nfs accept# nft --handle list rulesettable inet my_table { # handle 21    chain my_filter_chain { # handle 1    type filter hook input priority 0; policy accept;    tcp dport http accept # handle 3    tcp dport 1234 accept # handle 8    tcp dport nfs accept # handle 7    tcp dport ssh accept # handle 2    }}

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


Также вы можете получить handle правила во время создания, используя сразу два флага echo и handle. Правило будет выведено в CLI вместе с его handle.


# nft --echo --handle add rule inet my_table my_filter_chain udp dport 3333 acceptadd rule inet my_table my_filter_chain udp dport 3333 accept # handle 4

Примечание: старые версии nftables использовали позицию ключевого слова. С тех пор механика ключевых слов устарела и появился handle.

Удаление правил


Удаление правил выполняется с помощью handle правила по аналогии с командами add и insert выше.


Сначала нужно найти handle правила, которое вы хотите удалить.


# nft --handle list rulesettable inet my_table { # handle 21    chain my_filter_chain { # handle 1    type filter hook input priority 0; policy accept;    tcp dport http accept # handle 3    tcp dport 1234 accept # handle 8    tcp dport nfs accept # handle 7    tcp dport ssh accept # handle 2    }}

Затем используйте этот handle для удаления правила.


# nft delete rule inet my_table my_filter_chain handle 8# nft --handle list rulesettable inet my_table { # handle 21    chain my_filter_chain { # handle 1    type filter hook input priority 0; policy accept;    tcp dport http accept # handle 3    tcp dport nfs accept # handle 7    tcp dport ssh accept # handle 2    }}

Листинг правил


В примерах выше мы выводили весь список правил. Существует много других способов вывести подмножество правил.


Вывести все правила в заданной таблице.


# nft list table inet my_tabletable inet my_table {    chain my_filter_chain {        type filter hook input priority 0; policy accept;        tcp dport http accept        tcp dport nfs accept        tcp dport ssh accept    }}

Вывести правила в заданной цепочке.


# nft list chain inet my_table my_other_chaintable inet my_table {    chain my_other_chain {        udp dport 12345 log prefix "UDP-12345"    }}

Наборы


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


Анонимные наборы


Любое правило может иметь inline-наборы. Эта механика полезна для наборов, которые вы не собираетесь изменять.


Например, следующая команда будет разрешать весь трафик с 10.10.10.123 и 10.10.10.231.


# nft add rule inet my_table my_filter_chain ip saddr { 10.10.10.123, 10.10.10.231 } accept# nft list rulesettable inet my_table {    chain my_filter_chain {        type filter hook input priority 0; policy accept;        tcp dport http accept        tcp dport nfs accept        tcp dport ssh accept        ip saddr { 10.10.10.123, 10.10.10.231 } accept    }

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


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


# nft add rule inet my_table my_filter_chain tcp dport { http, nfs, ssh } accept

Примечание: пользователи iptables скорее всего привыкли к использованию ipset. Поскольку в nftables есть собственная нативная поддержка наборов, ipset использовать не нужно.

Именованные наборы


Nftables также поддерживает мутабельные именованные наборы. Для их создания необходимо указать тип элементов, которые будут в них содержаться. Например, типы могут быть такими: ipv4_addr, inet_service, ether_addr.


Давайте создадим пустой набор.


# nft add set inet my_table my_set { type ipv4_addr \; }# nft list setstable inet my_table {    set my_set {    type ipv4_addr    }}

Чтобы сослаться на набор в правиле используйте символ @ и имя набора после него. Следующее правило будет работать как черный список для IP-адресов в нашем наборе.


# nft insert rule inet my_table my_filter_chain ip saddr @my_set drop# nft list chain inet my_table my_filter_chaintable inet my_table {    chain my_filter_chain {    type filter hook input priority 0; policy accept;    ip saddr @my_set drop    tcp dport http accept    tcp dport nfs accept    tcp dport ssh accept    ip saddr { 10.10.10.123, 10.10.10.231 } accept    }}

Конечно, это не особо эффективно, так как в наборе пусто. Давайте добавим в него несколько элементов.


# nft add element inet my_table my_set { 10.10.10.22, 10.10.10.33 }# nft list set inet my_table my_settable inet my_table {    set my_set {    type ipv4_addr    elements = { 10.10.10.22, 10.10.10.33 }    }}

Однако попытка добавить диапазон значений приведет к ошибке.


# nft add element inet my_table my_set { 10.20.20.0-10.20.20.255 }Error: Set member cannot be range, missing interval flag on declarationadd element inet my_table my_set { 10.20.20.0-10.20.20.255 }

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


Интервалы в наборах


В наборах также могут использовать диапазоны. Для IP-адресов это может быть крайне полезно. Для использования диапазонов, набор должен быть создан с использованием флагов интервалов.


# nft add set inet my_table my_range_set { type ipv4_addr \; flags interval \; }# nft add element inet my_table my_range_set  { 10.20.20.0/24 }# nft list set inet my_table my_range_settable inet my_table {    set my_range_set {    type ipv4_addr    flags interval    elements = { 10.20.20.0/24 }    }}

Примечание: Нотация маски сети была неявно преобразована в диапазон IP-адресов. Аналогично, мы могли бы написать 10.20.20.0-10.20.20.255 и получить тот же эффект.

Конкатенации наборов


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


В этом примере мы сможем сопоставлять IPv4-адреса, IP-протоколы и номера портов одновременно.


# nft add set inet my_table my_concat_set  { type ipv4_addr . inet_proto . inet_service \; }# nft list set inet my_table my_concat_settable inet my_table {    set my_concat_set {    type ipv4_addr . inet_proto . inet_service    }}

Теперь мы можем добавить элементы к списку.


# nft add element inet my_table my_concat_set { 10.30.30.30 . tcp . telnet }

Как видите, символьные имена (tcp, telnet) также можно использовать при добавлении элементов набора.


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


# nft add rule inet my_table my_filter_chain ip saddr . meta l4proto . tcp dport @my_concat_set accept# nft list chain inet my_table my_filter_chaintable inet my_table {    chain my_filter_chain {    ...    ip saddr { 10.10.10.123, 10.10.10.231 } accept    meta nfproto ipv4 ip saddr . meta l4proto . tcp dport @my_concat_set accept    }}

Также стоит отметить, что конкатенация может использоваться с inline-наборами. Вот последний пример, отражающий это.


# nft add rule inet my_table my_filter_chain ip saddr . meta l4proto . udp dport { 10.30.30.30 . udp . bootps } accept

Надеюсь, теперь вы понимаете всю силу наборов nftables.


Примечание: конкатенации наборов nftables аналогичны агрегатным типам ipset, например, hash:ip,port.

Verdict Map


Verdict map это интересная функция в nftables, которая позволит вам выполнить действие, основываясь на информации в пакете. Проще говоря, они сопоставляют критерии с действиями.


Например, чтобы логически разделить набор правил, вам нужны отдельные цепочки для обработки TCP и UDP пакетов. Вы можете использовать verdict map, чтобы направлять пакеты в эти цепочки с помощью одного правила.


# nft add chain inet my_table my_tcp_chain# nft add chain inet my_table my_udp_chain# nft add rule inet my_table my_filter_chain meta l4proto vmap { tcp : jump my_tcp_chain, udp : jump my_udp_chain }# nft list chain inet my_table my_filter_chaintable inet my_table {    chain my_filter_chain {    ...    meta nfproto ipv4 ip saddr . meta l4proto . udp dport { 10.30.30.30 . udp . bootps } accept    meta l4proto vmap { tcp : jump my_tcp_chain, udp : jump my_udp_chain }    }}

Конечно, по аналогии с наборами вы можете создавать мутабельные verdict map.


# nft add map inet my_table my_vmap { type inet_proto : verdict \; }

Ваши глаза вас не обманывают. Синтаксис с наборами и вправду очень схож. По факту, под капотом наборы и verdict map строятся на одном и том же типе данных.


Теперь вы можете использовать мутабельную verdict map в правиле.


# nft add rule inet my_table my_filter_chain meta l4proto vmap @my_vmap

Таблицы как пространства имен


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


# nft add table inet table_one# nft add chain inet table_one my_chain# nft add table inet table_two# nft add chain inet table_two my_chain# nft list ruleset...table inet table_one {    chain my_chain {    }}table inet table_two {    chain my_chain {    }}

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


Однако и тут есть один нюанс. Каждый хук таблицы или цепочки можно рассматривать как независимый и отдельный брандмауэр. То есть пакет должен пройти через все, чтобы в итоге быть принятым. Если table_one принимает пакет, его все еще может отклонить table_two. Именно здесь в игру вступает приоритезация хуков. Цепочка с более низким приоритетом гарантированно будет выполнена перед цепочкой с более высоким приоритетом. Если приоритеты равны, то поведение не определено.


Сохранение и восстановление набора правил


Правила nftables можно с легкостью сохранить и восстановить. Вывод list в nft можно использовать в инструменте, чтобы провести восстановление. Именно так работает служба nftables systemd.


Сохранить набор правил


# nft list ruleset > /root/nftables.conf

Восстановить набор правил


# nft -f /root/nftables.conf

Конечно же, вы можете включить службу systemd и восстановить правила при перезагрузке. Служба читает правила из /etc/sysconfig/nftables.conf.


# systemctl enable nftables# nft list ruleset > /etc/sysconfig/nftables.conf

Примечание: некоторые дистрибутивы, включая RHEL-8, отправляют предопределенную конфигурацию nftables в /etc/nftables. Эти сэмплы часто включают в себя конфигурацию таблиц и цепочек в стиле iptables. Они часто указаны в файле /etc/sysconfig/nftables.conf, но могут быть закомментированы.

Заключение


Надеюсь, эта статья стала хорошим введением и демонстрацией возможностей nftables. Однако мы только коснулись поверхности nftables. Есть множество особенностей, которые здесь не затрагивались. Вы можете найти более подробную информацию на странице документации nft и в вики. Помимо этого, вы можете прочитать несколько следующих статей в этом блоге, в которых мы будем говорить о более продвинутых аспектах nftables.




Узнать подробнее о курсе Администратор Linux



Подробнее..

Перевод Сравниваем лучшее программное обеспечение для виртуализации в 2020 году Hyper-V, KVM, vSphere и XenServer

13.07.2020 12:11:27 | Автор: admin

Перевод статьи подготовлен в преддверии старта курса Администратор Linux. Виртуализация и кластеризация





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


Что такое виртуализация серверов?


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


Лучшее программное обеспечение/инструменты и поставщики для виртуализации серверов Hyper-V vs KVM vs vSphere vs XenServer


Citrix XenServer, Microsoft Hyper-V, Red Hat KVM и VMware vSphere являются крупнейшими игроками на рынке виртуализации серверов. Зачастую предприятия испытывают затруднения в принятии решения, какой гипервизор лучше всего подойдет их бизнесу.


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


Примечание: Инструменты расположены в алфавитном порядке по их названиям.


1. Hyper-V


Запущенный в 2008 году Microsoft Hyper-V помогает в расширении или создании приватной облачной среды. Он способствует эффективному использованию оборудования, улучшает непрерывность бизнес-процессов, а также повышает эффективность разработки и тестирования.



Функционал Microsoft Hyper-V для Windows Server 2019:


  • Поддержка постоянной памяти.
  • Обновления экранированных VM.
  • Простые двухузловые кластеры.
  • Дедупликация ReFS.
  • Оптимизация локальных дисковых пространств (Storage Spaces Direct)
  • Центр администрирования Windows.
  • Зашифрованные подсети.

Подробнее о виртуализации серверов с Microsoft вы можете прочитать в этом PDF.


2. KVM


KVM (Kernel-based Virtual Machine), входящая в состав Red Hat Virtualization Suite, представляет собой комплексное решение для инфраструктуры виртуализации. KVM превращает ядро Linux в гипервизор. Он был введен в основную ветку ядра Linux с версии ядра 2.6.20.



Функционал Red Hat KVM:


  • Поддержка контейнеров
  • Масштабируемость
  • Overcommit ресурсов
  • Disk I/O throttling
  • Горячая замена виртуальных ресурсов
  • Недорогое решение для виртуализации
  • Red Hat Enterprise Virtualization программирование и API
  • Живая миграция и миграция хранилища
  • Назначение любых PCI устройств виртуальным машинам
  • Интеграция Red Hat Satellite
  • Поддержка восстановления после сбоя (Disaster Recovery)

Для получения более подробной информации прочтите это руководство по функционалу KVM.


3. vSphere


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


Она предоставляет ряд ключевых компонентов, включая инфраструктурные сервисы (vCompute, vStorage и vNetwork), сервисы приложений, vCenter Server, vSphere Client и т. д.



Функционал VMware vSphere:


  • vCenter Server: инструмент централизованного управления, используемый для настройки, предоставления и управления виртуальными IT средами.
  • vSphere Clien: vSphere 6.7 имеет финальную версию vSphere Web Client на основе Flash. Новые рабочие процессы в обновленном выпуске vSphere Client включают vSphere Update Manager, библиотеку контента, vSAN, политики хранения, профили хостов, схему топологии VMware vSphere Distributed Switch и лицензирование.
  • vSphere SDK: предоставляет интерфейсы для сторонних решений для доступа к vSphere.
  • VM File System: кластерная файловая система для VM.
  • Virtual SMP: позволяет одной виртуальной машине одновременно использовать несколько физических процессоров.
  • vMotion: активная миграция с целостностью транзакций.
  • Storage vMotion: обеспечивает миграцию файлов виртуальных машин из одного места в другое без прерывания обслуживания.
  • Высокая доступность: в случае сбоя одного сервера виртуальная машина перемещается на другой сервер с резервной емкостью для обеспечения непрерывности бизнес-процессов.
    Планировщик распределенных ресурсов (DRS): автоматически назначает и балансирует вычисления по аппаратным ресурсам, доступным для виртуальных машин.
  • Отказоустойчивость: создает копию основной виртуальной машины, чтобы обеспечить ее постоянную доступность.
  • Распределенный коммутатор (VDS): охватывает несколько хостов ESXi и позволяет значительно сократить объем работ по обслуживанию сети.

Для получения дополнительной информации о виртуализации серверов с помощью VMware прочтите этот PDF-файл.


4. XenServer


Основанный на Xen Project Hypervisor, XenServer является платформой виртуализации серверов с открытым исходным кодом для платформ без операционной системы. Он состоит из функций корпоративного уровня, которые помогают предприятиям легко справляться с рабочими нагрузками, комбинированными ОС и сетевыми конфигурациями.


XenServer обеспечивает улучшенную виртуализированную графику с NIVIDA и Intel и позволяет запускать несколько компьютерных операционных систем на одном оборудовании.



Функционал Citrix XenServer:


  • Восстановление узла
  • Защита хоста от сбоев
  • Мультисерверное управление
  • Управление динамической памятью
  • Интеграция Active Directory
  • Администрирование и контроль на основе ролей (RBAC)
  • Пулы смешанных ресурсов с маскированием ЦП
  • Контроллер распределенного виртуального коммутатора
  • Встроенное в память кэширование операций чтения
  • Живая миграция виртуальных машин и хранилище XenMotion
  • Если вас интересуют подробности, вы можете прочитать этот PDF.

Сводка по vSphere, XenServer, Hyper-V и KVM



Помогите нам улучшить эту статью. Поделитесь своим мнением с нами в комментариях ниже!


Дисклеймер: последний раз эта статья была обновлена 11 января 2020 года информацией, доступной на веб-сайтах поставщиков и ресурсов в открытом доступе. Целью данной статьи является предоставление информации о гипервизорах разных поставщиков только в общих информационных целях. Поставщики могут время от времени менять характеристики своего продукта. Хотя мы прилагаем все усилия, чтобы информация была точной и актуальной, мы не можем гарантировать ее 100% точность или актуальность.



Узнать подробнее о курсе Администратор Linux. Виртуализация и кластеризация



Подробнее..

Перевод Распределенное обучение с Apache MXNet и Horovod

13.07.2020 20:06:08 | Автор: admin

Перевод статьи подготовлен в преддверии старта курса Промышленный ML на больших данных




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


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


Что такое Apache MXNet


Apache MXNet открытый фреймворк для глубокого обучения, который используется для создания, обучения и развертывания глубоких нейронных сетей. MXNet абстрагирует сложности, связанные с реализацией нейронных сетей, обладает высокой производительностью и масштабируемостью, а также предлагает API для популярных языков программирования, таких как Python, C++, Clojure, Java, Julia, R, Scala и других.


Распределенное обучение в MXNet с сервером параметров


Стандартный модуль распределенного обучения в MXNet использует подход сервера параметров (parameter server). Он использует набор серверов параметров для сбора градиентов с каждого воркера, выполнения агрегации и отправки обновленных градиентов обратно ворекрам для следующей итерации оптимизации. Определение правильного соотношения серверов к воркерам залог эффективного масштабирования. Если сервер параметров всего один, он может оказаться бутылочным горлышком в проведении вычислений. И наоборот, если используется слишком много серверов, то связь многие-ко-многим может забить все сетевые соединения.


Что такое Horovod


Horovod открытый фреймворк распределенного глубокого обучения, разработанный в Uber. Он использует эффективные технологии взаимодействия между несколькими GPU и узлами, такие как NVIDIA Collective Communications Library (NCCL) и Message Passing Interface (MPI) для распределения и агрегирования параметров модели между ворекрами. Он оптимизирует использование пропускной способности сети и хорошо масштабируется при работе с моделями глубоких нейронных сетей. В настоящее время он поддерживает несколько популярных фреймворков машинного обучения, а именно MXNet, Tensorflow, Keras, и PyTorch.


Интеграция MXNet и Horovod


MXNet интегрируется с Horovod через API распределённого обучения, определенные в Horovod. В Horovod коммуникационные API horovod.broadcast(), horovod.allgather() и horovod.allreduce() реализованы с помощью асинхронных коллбэков движка MXNet, как часть его графа задач. Таким образом, зависимости данных между коммуникацией и вычислениями с легкостью обрабатываются движком MXNet, чтобы избежать потерь производительности из-за синхронизации. Объект distributed optimizer, определенный в Horovod horovod.DistributedOptimizer расширяет Optimizer в MXNet таким образом, чтобы он вызывал соответствующие API Horovod для распределенного обновления параметров. Все эти детали реализации прозрачны для конечных пользователей.


Быстрый старт


Вы можете быстро начать обучать небольшую сверточную нейронную сеть на наборе данных MNIST с помощью MXNet и Horovod на вашем MacBook.
Для начала установите mxnet и horovod из PyPI:


pip install mxnetpip install horovod

Примечание: Если у вас возникает ошибка во время pip install horovod, возможно, вам нужно добавить переменную MACOSX_DEPLOYMENT_TARGET=10.vv, где vv это версия вашей версии MacOS, например, для MacOSX Sierra нужно будет написать MACOSX_DEPLOYMENT_TARGET=10.12 pip install horovod

Затем установите OpenMPI отсюда.


В конце загрузите тестовый скрипт mxnet_mnist.py отсюда и выполните следующие команды в терминале MacBook в рабочей директории:


mpirun -np 2 -H localhost:2 -bind-to none -map-by slot python mxnet_mnist.py

Так вы запустите обучение на двух ядрах вашего процессора. На выходе будет следующее:


INFO:root:Epoch[0] Batch [0-50] Speed: 2248.71 samples/sec      accuracy=0.583640INFO:root:Epoch[0] Batch [50-100] Speed: 2273.89 samples/sec      accuracy=0.882812INFO:root:Epoch[0] Batch [50-100] Speed: 2273.39 samples/sec      accuracy=0.870000

Демонстрация производительности


При обучении модели ResNet50-v1 на наборе данных ImageNet на 64 GPU с восемью экземплярами p3.16xlarge EC2, каждый из которых содержит 8 GPU NVIDIA Tesla V100 на AWS cloud, мы достигли пропускной способности обучения 45000 изображений/сек (т.е. количество обученных сэмплов в секунду). Обучение завершалось за 44 минуты после 90 эпох с лучшей точностью в 75.7%.


Мы сравнили это с распределенным обучением MXNet с подходом использования серверов параметров на 8, 16, 32 и 64 GPU с сервером с одним параметром и отношением серверов к воркерам 1 к 1 и 2 к 1 соответственно. Результат вы можете увидеть на Рисунке 1 ниже. По оси y слева столбцы отражают количество изображений для обучения в секунду, линии отражают эффективность масштабирования (то есть отношение фактической пропускной способности к идеальной) на оси y справа. Как видите выбор количества серверов оказывает влияние на эффективность масштабирования. Если сервер параметров один, эффективность масштабирования падает до 38% на 64 GPU. Для достижения такой же эффективности масштабирования как с Horovod нужно увеличить количество серверов в два раза по отношению к количеству воркеров.



Рисунок 1. Сравнение распределённого обучения с использованием MXNet с Horovod и с сервером параметров


В Таблице 1 ниже мы сравнили итоговую стоимость экземпляра при выполнении экспериментов на 64 GPU. Использование MXNet вместе с Horovod обеспечивает наилучшую пропускную способность при наименьших затратах.



Таблица 1. Сравнение затрат между Horovod и сервером параметров с соотношением серверов к воркерам 2 к 1.


Шаги для воспроизведения


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


Шаг 1


Создайте кластер однородных экземпляров с MXNet версии 1.4.0 или выше и Horovod версии 0.16.0 или выше, чтобы использовать распределенное обучение. Вам также нужно будет установить библиотеки для обучения на GPU. Для наших экземпляров мы выбрали Ubuntu 16.04 Linux, с GPU Driver 396.44, CUDA 9.2, библиотеку cuDNN 7.2.1, коммуникатор NCCL 2.2.13 и OpenMPI 3.1.1. Также вы можете использовать Amazon Deep Learning AMI, где эти библиотеки уже предустановлены.


Шаг 2


Добавьте в свой обучающий скрипт MXNet возможность работы с API Horovod. Приведенный ниже скрипт на основе MXNet Gluon API можно использовать как простой шаблон. Строки, выделенные жирным, нужны в том случае, если у вас уже есть соответствующий обучающий скрипт. Вот несколько критических изменений, которые необходимо внести для обучения с Horovod:


  • Установите контекст в соответствии с локальным рангом Horovod (строка 8), чтобы понимать, что обучение выполняется на правильном графическом ядре.
  • Передайте начальные параметры от одного воркера ко всем (строка 18), чтобы убедиться, что все воркеры начинают работу с одинаковыми начальными параметрами.
  • Создайте Horovod DistributedOptimizer (строка 25), чтобы обновлять параметры распределенно.

Чтобы получить полный скрипт, обратитесь к примерами Horovod-MXNet MNIST и ImageNet.


1  import mxnet as mx2  import horovod.mxnet as hvd34  # Horovod: initialize Horovod5  hvd.init()67  # Horovod: pin a GPU to be used to local rank8  context = mx.gpu(hvd.local_rank())910 # Build model11 model = ...1213 # Initialize parameters14 model.initialize(initializer, ctx=context)15 params = model.collect_params()1617 # Horovod: broadcast parameters18 hvd.broadcast_parameters(params, root_rank=0)1920 # Create optimizer21 optimizer_params = ...22 opt = mx.optimizer.create('sgd', **optimizer_params)2324 # Horovod: wrap optimizer with DistributedOptimizer25 opt = hvd.DistributedOptimizer(opt)2627 # Create trainer and loss function28 trainer = mx.gluon.Trainer(params, opt, kvstore=None)29 loss_fn = ...3031 # Train model32 for epoch in range(num_epoch):33    ...

Шаг 3


Войдите в один из воркеров для запуска распределённого обучения с помощью директивы MPI. В данном примере, распределенное обучение запускается на четырех экземплярах с 4 GPU в каждом, и с 16 GPU в кластере по итогу. Будет использоваться оптимизатор стохастического градиентного спуска (SGD) со следующими гиперпараметрами:


  • mini-batch size: 256
  • learning rate: 0.1
  • momentum: 0.9
  • weight decay: 0.0001

По мере масштабирования от одного GPU к 64 GPU мы линейно масштабировали скорость обучения в соответствии с количеством GPU (от 0,1 для 1 GPU до 6,4 для 64 GPU), сохраняя при этом количество изображений, приходящихся на один GPU, равным 256 (от пакета в 256 изображений для 1 GPU до 16 384 для 64 GPU). Параметры weight decay и momentum изменялись по мере увеличения числа GPU. Мы использовали смешанную точность обучения с типом данных float16 при прямом проходе и float32 для градиентов, чтобы ускорить вычисления float16, поддерживаемые GPU NVIDIA Tesla.


$ mpirun -np 16 \    -H server1:4,server2:4,server3:4,server4:4 \    -bind-to none -map-by slot \    -mca pml ob1 -mca btl ^openib \    python mxnet_imagenet_resnet50.py

Заключение


В этой статье мы рассмотрели масштабируемый подход к распределенному обучению модели с использованием Apache MXNet и Horovod. Мы показали эффективность масштабирования и экономическую эффективность по сравнению с подходом с использованием сервера параметров на наборе данных ImageNet, на котором обучалась модель ResNet50-v1. Также мы отразили шаги, с помощью которых вы можете изменить уже существующий скрипт, чтобы запустить обучение на нескольких экземплярах с помощью Horovod.


Если вы только начинаете работать с MXNet и глубоким обучением, перейдите на страницу установки MXNe, чтобы сначала собрать MXNet. Также настоятельно рекомендуем почитать статью MXNet in 60 minutes, чтобы начать работу.


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


*стоимость рассчитывается на основании почасовой ставки AWS для экземпляров EC2




Узнать подробнее о курсе Промышленный ML на больших данных



Подробнее..

Выгорание сотрудников основные принципы борьбы, если вы тимлид

11.07.2020 10:23:53 | Автор: admin

Перевод статьи подготовлен в преддверии старта курса Team Lead 2.0.





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


Исследования показывают, что около 23% сотрудников испытывают ощущение выгорания практически все время, и еще около 44% процентов ощущают выгорание время от времени. Это означает, что где-то 2/3 сотрудников подвержены этой проблеме. И к огромному сожалению, выгорание оказывает влияние не только на работу, но и на личную жизнь. Все это вместе ничего хорошего не предвещает. Понятное дело, что все в руководстве уверены, что выгорание зло, с которым нужно бороться. Но в тоже время начальство хочет, чтобы сотрудники работали с высокой производительностью и были все время вдохновлены и счастливы от того, что они работают именно здесь.


Так что же нужно делать?


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


Чтобы помочь человеку c выгоранием, необходимо давать отпуск, менять сферу деятельности с текущей на более интересную, частично разгружать задачи, но всего этого может не хватить.
Давайте ниже я приведу основные признаки, что ваша команда сейчас испытывает выгорание:


  • Задачи, которые хорошо выполнялись раньше, теперь требуют куда больше времени и усилий, чем до этого.
  • Учащение жалоб и плохое взаимодействие с клиентами.
  • Плохое взаимодействие команды. До этого ваша команда работала как часы, и все были рады взаимодействовать друг с другом по рабочим вопросам. Теперь каждый член вашей команды только думает, как сбросить с себя свои задачи побыстрее, неважно, в решенном виде, или нет.

Какие же методы борьбы мы можем выбрать, чтобы помочь сотрудникам справиться с выгоранием и ввести превентивные меры, чтобы выгорание больше не посещало наш дружный коллектив? Дать всем отпуск на Бали на несколько месяцев


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


У каждого бывают проблемы и ошибки на работе. Вопрос только в том, как начальство должно в своих комментариях и обратной связи связывать это лично с работником. Он снова допустил ошибку в том месте, в котором уже допускал? Да, это очень плохой маркер. Но вам не нужно винить сотрудника в этом как личность, нужно вместе с ним найти, что же ему мешает работать, как надо. Не нужно чрезмерно давить и постоянно угрожать увольнением это тоже не придаст сотрудникам особой мотивации. Негативная и токсичная среда через некоторое время


  • либо сломает сотрудника, и он станет "зомби", который вроде что-то делает, но совсем без души, и только то, что обязан, не пытаясь потратить на работу хоть на секунду больше,
  • либо заставит сотрудника уволиться с неприятными ощущениями от работы и от компании в целом.

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


Вам нужно выстроить процесс коммуникации таким образом, чтобы сотрудники доверяли вам, как старшему товарищу и лидеру, к которому можно обратиться по любому вопросу, по личному или по рабочему. Сотрудники доверительно общаются как по горизонтали (т.е. с сотрудниками, которые занимают примерно такую же должность), так и по вертикали (готовы обратиться и к вышестоящему сотруднику). И, что более важно, они готовы не только рассказать о своих проблемах, но и услышать проблемы других и приложить те усилия к их решению, насколько это в их силах.


Тщательно планируйте этапы вашей работы


Ни для кого в IT не секрет, что хорошо спланированный проект можно сделать быстро и хорошо, а плохо спланированный можно делать либо долго и хорошо, либо плохо и быстро. С другой стороны, постоянно возникает потребность в новых фичах и правках со стороны пользователя, который только смутно понимает, какие потребности ему нужны от бизнеса. Ваша же задача состоит в том, чтобы сотрудники не демотивировались постоянными переделками уже практически готового проекта. Что делать? Обратитесь к хорошему Scrum-мастеру, подумайте, насколько правильно вы выдаете ваши задачи. Вы cможете больше, если рассчитаете нагрузку правильно.


Какие рекомендации вы можете дать сотрудникам, если подозреваете у них выгорание и хотите им помочь?


  • Посоветуйте соблюдать режим. Тривиальный совет, но тоже может оказаться важной деталью. Если режим совсем сбился, можно поработать какое-то время из дома по меньшему пулу задач, зато восстановить силы/выполнить свои задачи более эффективно.


  • Посоветуйте соблюдать моцион и физические нагрузки. Немногие разработчики соблюдают моцион, но он нужен, чтобы ваше тело легче переносило и интеллектуальные нагрузки. Тело человека не приспособлено к тому, чтобы сидеть много часов в день в кресле и заниматься сложным интеллектуальным трудом. Так что стоит беречь себя, занявшись бегом/записавшись в бассейн/тренажерный зал (хотя, конечно, так лень). Если вы не понимаете, зачем вам заниматься спортом, пока ваше тело не болит, вам стоит почитать статью вроде этой. Да, она больше похожа на рекламную, но здесь все правда вашему мозгу нужна смена гормонов и нужен дофамин, а для этого стоит заниматься физическими нагрузками.


  • Постарайтесь найти хобби и выделять на него достаточно времени, чтобы достигнуть результата. Лучше всего, если хобби не будет связано именно с программированием, играми или другими видами виртуальной реальности. Потому что большей эффективности мы можем достигнуть, только если соблюдать некоторый баланс. Лучше всего найти хобби, которое будет связано с работой руками (микросхемы, столярное дело, спортивные увлечения). Посмотрите на столь популярное в Долине скалолазание! Это же классно, вам стоит попробовать! Это позволит вашему мозгу лучше адаптироваться, потому его разные отделы будут нагружаться соответствующе, и вы сразу ощутите, что можете лучше и в программирование, и в хобби



Еще раз повторим, что нужно помнить, если вы тимлид у команды:


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


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


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



Что НЕ нужно делать, если ваш сотрудник выгорает и начинает работать хуже, чем раньше?


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

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


  • Магия утра. Как первый час дня определяет ваш успех под авторством Хэл Элрод. Многие знакомые программисты советовали мне эту книгу после того, как сами воспользовались ее советами и изменили свою жизнь к лучшему. Прочитайте, вредных советов там нет.
  • Мотивация на 100%. А где же у него кнопка под авторством Ивановой Светланы. Классика литературы по мотивации персонала, её должен прочитать каждый руководитель.
  • Эффективные коммуникации сборник статей от Harvard Business Review. Не самая простая литература, больше профессиональная. Но если все остальное кажется простым и очевидным, вам стоит попробовать эту серию.



Узнать подробнее о курсе Team Lead 2.0.



Подробнее..

Что должен уметь программист 1C?

13.07.2020 20:06:08 | Автор: admin

Здравствуйте! Публикуем авторскую статью Дмитрия Котлова сертифицированного 1С-специалиста, руководителя проектов, эксперта по технологическим вопросам. Также Дмитрий является автором и руководителем нового профессионального курса OTUS Программист 1С, с программой которого мы приглашаем вас познакомиться.




Привет! В своей статье я расскажу о том, какие специалисты нужны для внедрения 1С, какие задачи они выполняют, какие компетенции нужны для качественного выполнения проекта. Отдельно и подробнее поговорим про требования к программистам.


Для начала перечислим позиции специалистов:


  • Руководитель проекта
  • Архитектор
  • Консультант
  • Программист
  • Специалист по качеству (по тестированию)

Чем занимаются перечисленные специалисты?


Руководитель проекта


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


  • Составление плана проекта и контроль его реализации, могут быть различные планы: по срокам, по качеству, по финансам(бюджет)
  • Взаимодействие с заказчиком по плану планам проекта
  • Участие в продаже проекта
  • Взаимодействие с командой проекта на предмет выполнения проекта и решения административных вопросов
  • Координация выполнения работы и их приёмки
  • Выбор оптимальных конфигураций 1С для решения задач клиента

Архитектор


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


  • Разработка и описание архитектуры 1С
  • Участие в пресейлах
  • Техническое руководство проектом
  • Контроль качества разработки
  • Выявление и управление техническими рисками проекта
  • Оценка объёма работ
  • Участие в разработке ТЗ, ЧТЗ, ТП, требований к архитектуре
  • Организация процесса разработки
  • Анализ качества продукта

Консультант


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


  • Консультирование по функционалу
  • Участие в пресейлах
  • Определение бизнес-требований, планирование подхода к работе с требованиями
  • Выявлять, анализировать и документировать требования
  • Доводить требования до заинтересованных лиц, управлять проверкой требований
  • Обеспечивать расстановку приоритетов требований
  • Ставить задачи программистам и принимать результат выполнения
  • Проведение обучения
  • Проведение приёмо-сдаточных испытаний, демонстрация продукта заказчику
  • Сдача и согласование документации с заказчиком

Программист


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


  • Обновление информационных баз
  • Реализация доработок в соответствии со стандартами разработки
  • Участие в совещаниях

Специалист по качеству (по тестированию)


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


  • Ввод тестовых данных в систему
  • Написание сценария тестирования
  • Фиксация результатов тестирования
  • Анализ результатов тестирования, формулирование выявленных проблем
  • Обсуждение сценариев и результатов тестирования, изменение сценариев по результатам обсуждений
  • Написание документации по результатам тестирования
  • Развитие инфраструктуры для обеспечения качества
  • Выполнение ручных и автоматических тестов
  • Участие в сборе и анализе бизнес-требований к продукту

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


Далее подробнее разберём позицию Программист


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


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


Обязанности:


  • Установка программного обеспечения
  • Обучение клиентов
  • Участие в тестировании
  • Участие в качестве ассистента во внедрении
  • Программирование
  • Прохождение обучения
  • Сдача тестов и экзаменов на сертификацию

Требования:


  • Желание развиваться
  • Общительность
  • Инициативность
  • Умение излагать свои мысли, грамотная речь
  • Желателен опыт с 1С
  • Желательно знание бухгалтерского учёта

Программист 1С позиция подразумевает определённый опыт работы и отсутствие необходимости обучать специалиста программированию, т.е. на неё ведётся поиск людей, которые уже умеют программировать и могут самостоятельно решать задачи.


Обязанности:


  • Доработка конфигураций
  • Разработка конфигураций под задачи компании
  • Написание новых отчётов, обработок
  • Интеграция 1С со внешними системами
  • Обновление доработанных конфигураций

Требования


  • Опыт работы от года
  • Высшее образование
  • Знание типовых конфигураций (конфигурации те, которые есть в компании или с которыми предстоит работать)
  • Знание языка запросов
  • Знание СКД
  • Умение писать правила обмен с помощью конфигураций 1С: КД 2.0/3.0
  • Знание универсальных механизмов обмена данными
  • Понимание REST запросов и HTTP сервисов.
  • Умение программировать на управляемых формах
  • Понимание клиент-серверного взаимодействия
  • Знание основных бизнес-процессов предметных областях (в зависимости от того чем занимается компания)
  • Опыт работы с БСП
  • Умение работать с чужим кодом
  • Умение работать с хранилищем конфигураций
  • Знание методик разработки
  • Опыт реализации функционала на мобильной платформе 1С
  • Способность работать в команде
  • Навыки оптимизации кода

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


Обязанности:


  • Разработка нового функционала
  • Подготовка сборок и релизов по выпаленным задачам
  • Настройка сервера 1С Предприятие
  • Декомпозиция, распределение и постановка задач разработчикам
  • Обновление не типовых конфигураций
  • Оптимизация производительности 1С
  • Разработка обменов данными между 1С и внешним ПО

Требования:


  • Знание типовых конфигураций (тех что есть в компании)
  • Опыт работ от 3-х лет
  • Высшее образование
  • Понимание клиент-серверной архитектуры
  • Опыт написания обменов посредством HTTP, web сервисов, FTP
  • Опыт разработки мобильных приложений на мобильной платформе 1С
  • Умение переключаться между задачами
  • Умение работать в команде
  • Знание СКД, КД, БСП, XDTO
  • Опыт работы с технической документацией и её написания
  • Опыт руководства программистами
  • Опыт ревью чужого кода
  • Опыт работ с системами учёта турдозатрат
  • Отличное знание языка программирования 1С и встроенного языка запросов
  • Опыт работы с хранилищем конфигурации 1С
  • Опыт работы с расширениями
  • Опыт оптимизации запросов

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


Также, если вам интересно развиваться в данной сфере, не пропустите прямую трансляцию мастер-класса Разбор стандартов и методик разработки на платформе 1С. Я расскажу о стандартах и методиках разработки 1С и покажу, зачем они нужны. А также вы сможете самостоятельно привести код в соответствии со стандартами и методиками 1С!

Подробнее..

Категории

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

  • Имя: Макс
    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