С чего начать учить пайтон с нуля самоучитель
Изучение Python с нуля. Как? Где? Зачем?
За каждой технологией стоит разработчик, ответственный за ее появление. Например, систему оплаты, которую вы использовали, чтобы оплатить покупку, тоже создал какой-то программист. Разработчики используют компьютеры, чтобы решать проблемы. Часто они делают это в командах, когда проблемы чересчур комплексные.
Для создания программ используют языки программирования. Python — один из самых популярных сегодня, язык общего назначения, используемый в самых разных сценариях. Умение программировать с помощью Python — крайне полезный навык.
В этом руководстве рассмотрим рекомендации относительно того, как учить Python и на чем сделать акценты в процессе.
Зачем учить Python?
Python используется для самых разных сценариев — от создания веб-приложений до анализа данных и решения математических проблем. Его любят как опытные программисты, так и начинающие. И есть масса причин начать учить этот язык.
Зная Python, вы будете востребованным. Умение программировать поможет «оставаться на плаву» по мере того, как мир развивается. Одна только работа в сфере разработки программного обеспечения должна вырасти на 21% за следующие 10 лет.
Бюро статистики труда США оценивает этот показатель как «намного стремительнее среднего». Учитывая количество разработчиков, использующих Python, знание этого языка поможет заложить фундамент в этом направлении.
Python похож на английский. Многие разработчики отмечают, что Python легко учить, потому что он похож на английский. И это правда, ведь язык был спроектирован, чтобы быть лаконичным. Если вы только учитесь программировать, то Python — отличный выбор для старта.
Python широко используется. Такие организации, как Quora, YouTube, Dropbox и IBM всерьез полагаются на Python в своем бизнесе, потому что он гибкий, мощный и простой. Вы также можете использовать язык для решения сложных проблем.
За сколько можно выучить Python?
Скорость изучения Python зависит от вашего расписания и того, что вы понимаете под словом «учить».
Существует не так уж и много людей, о которых можно было бы сказать, что они знают Python всецело. Объем знаний сильно зависит от того, для чего вам нужны эти знания.
Если вы хотите стать специалистом по машинному обучению, то перед вами лежит довольно долгий путь. Но начнем с того, сколько займет знакомство с базовым пониманием языка.
В среднем изучение основ занимает 6-8 недель. Это позволит понимать большую часть строк, написанных с помощью этого языка. Если же у вас в планах data science или любая другая специализированная отрасль, то лучше сразу закладывать месяцы и даже годы.
Можно расписать план обучения приблизительно на 5-6 месяцев. Это подойдет в первую очередь тем, кто работает полный день, и может проводить у компьютера 2-3 часа. Сегодня вы учите что-то, а завтра — практикуетесь.
Однако важно практиковаться каждый день, чтобы быть уверенным в том, что вам удастся получить нужные знания за определенный промежуток времени. В любом случае этот режим легко подстраивать, пожертвовав, например, временем, которое вы тратите на просмотр сериалов.
Для чего нужен Python?
Python — это язык программирования общего назначения, что значит, что он используется в самых разных отраслях. Чаще всего его применяют:
Для Python есть внушительный набор библиотек, которые расширяют язык. Это подразумевает наличие огромного числа сообществ, использующих Python для самых разных целей. matplotlib, например, нужна для data science, а Click — для написания скриптов.
За сколько можно выучить основы?
Изучение основ Python займет как минимум три месяца. При условии уделения минимум 10 часов обучения в неделю.
Но три — это не конкретное число. Если вы захотите погрузиться в какую либо из библиотек, то быстро обнаружите, что общее время увеличивается. Одну только matplotlib можно учить несколько недель, и это всего одна библиотека.
Чем больше времени вы посвящаете обучению, тем быстрее вы будете учиться. Базовые вещи можно разобрать и за несколько дней. Но если вы хотите писать сложные и длинные программы, то сразу ориентируетесь на три месяца.
Лучший способ изучить Python бесплатно
Итак, вы решили изучать Python. Теперь разберемся с тем, как сделать это быстро.
Учитывая количество разработчиков, использующих этот язык, недостатка в обучающих материалах нет. Однако ресурсы — это не главное. Вот что еще вам потребуется.
Шаг 1: определение мотивации
Прежде чем начать изучать программирование на Python, определитесь с мотивацией. Это может показаться не столь важным, однако стоит понимать, с какой целью вы учитесь.
Пусть Python и является относительно легким языком, сам процесс обучения требует времени и энергии. И наличие мотивации поможет оставаться сфокусированным.
Вы хотите начать карьеру разработчика? Или стремитесь разбираться в современных технологиях? Это — хорошие причины, чтобы начать.
Шаг 2: изучите основы Python
Вы можете быть искушены идеей сразу же приступить к разработке сайта, но такой подход не работает. Вы будите тратить часы на устранение ошибок, возможно, разочаруетесь в программировании. Решите — «это не мое».
Для начала лучше изучить основы. А время для собственных проектов всегда будет.
Давайте рассмотрим план изучения Python с нуля:
Онлайн-курсы по Python
Бесплатный доступ к курсам Skillbox
Онлайн-университет Skillbox открывает 7 дней бесплатного доступа к курсам и интенсивам. Я всегда рекомендую попробовать начать программировать бесплатно. Вы будите уверены, что это действительно вам нравится: получается, подходит язык и хочется писать код всю жизнь.
Udemy — глобальная платформа для обучения онлайн
Udemy — глобальная платформа для обучения и преподавания онлайн, где миллионы студентов получают доступ к необходимым знаниям, которые помогают им добиться успеха. Только по теме «python» доступно почти 2000 курсов для начинающих.
Сайты-справочники и ютуб
PythonRu.com
На нашем сайте более 300 статей и уроков по программированию на python. Вы можете узнать что-то конкретное или пройти серию уроков. Например:
Русскоязычные Youtube каналы
Ютуб один из лучших вариантов изучения программирования. Не спешите учить python «за час», лучше посмотрите эти каналы:
Исключительно видео формат не все воспринимают. Если на ютубе не получилось, не опускайте руки, пробуйте текстовые материалы.
Python на Хабре
Множество статей «от разработчиков для разработчиков». Здесь вы найдете последние новости, обзоры и исследования которые касаются Python. Кроме этого, на Хабре есть несколько переводов курсов зарубежных авторов.
Книги по Python
Изучаем Python. Программирование игр, визуализация данных, веб-приложения
Эта книга посвящена основам Python: инструкциям if, кортежам и так далее. Из нее вы также узнаете о том, как использовать сторонние библиотеки.
Python. Книга рецептов
Эта книга содержит набор рецептов для Python-программиста. Из нее вы узнаете о том, как использовать язык в разных сценариях. Она также включает код, который поможет в изучении синтаксиса.
Автоматизация рутинных задач с помощью Python
Эта книга нужна тем, кто хочет расширить свои знания Python и уже знаком с основами. С ее помощью вы научитесь писать простые, но мощные скрипты, которые автоматизируют скучную рутину.
После изучения основных тем можно переходить к изучению машинного обучения и data science.
Большая часть современных руководств посвящена Python 3, потому что Python 2 уже отжил свое.
Шаг 3: создание проектов
Лучший способ научиться программировать — разрабатывать собственные проекты. Это помогает применять полученные знания и учиться, пробуя новое.
Чем больше вы пишите на Python, тем больше учитесь. Вы ставите цель, следуете ей и одновременно получаете новые навыки.
И даже это еще не все. Это также помогает развивать свое портфолио. А с его помощью вы сможете предлагать свои услуги работодателям.
Но прежде чем вы начнете создавать что-то масштабное, попробуйте с чего-нибудь попроще. Главное — создавать что-то, что развивает ваши способности.
Советы по созданию первых проектов
Единственное, что ограничивает вас в отношении собственного проекта — это воображение. Вы можете создать что угодно: сайт, чтобы рассказывать о любимых фильмах, алгоритм для предсказывания цены на авокадо и что-либо еще. Если же придумать что-нибудь не получается, то вот некоторые советы:
Главное — начинать с малого. Например, вы можете создать трекер привычек. Вот еще несколько идей для проекта:
Шаг 4: Развивайтесь для создания более сложных проектов
Создав несколько простых проектов, вы готовы переходить к боле сложным. Это может быть в том числе функциональное расширение уже существующих.
Предположим, ваш первый проект был сайтом со списком фильмов. Следующим шагом может быть добавление возможности пользователям создавать собственные списки.
Вот какие вопросы стоит задавать себе, рассматривая идеи для проектов:
Вам всегда должно быть интересно работать над проектами. Если столкнулись с препятствием, просите о помощи. Для улучшения навыков нужно просто практиковаться.
Выводы
В начале своего пути изучите основы языка. Познакомьтесь с синтаксисом, условными конструкциями, циклами и списками.
После этого переходите к созданию простых проектов. Это поможет развивать навыки на практике и добавлять результаты в свое портфолио.
Изучение Python требует настойчивости, усилий и времени. Однако этот навык вы сможете использовать в самых разных сферах своей жизни.
Топ-7 учебников по Python для начинающих (на русском языке)
Python — это один из языков, популярность которых растет наиболее быстро. И не без причины! Python используется для автоматизации простых и сложных задач, для вычислений, веб-разработки, создания игр. И, конечно, это идеальный язык для анализа данных.
Разработка на языке Python требует в 3-5 раз меньше времени, чем разработка на Java. Кроме того, это отличный строительный блок для изучения концепций как процедурного, так и объектно-ориентированного программирования.
В общем, нет ничего удивительного, что многие люди выбирают именно Python в качестве своего первого языка программирования. И, конечно, его изучают многие программисты, уже владеющие другими языками.
Каким бы ни был ваш бэкграунд, важно изучать Python правильно, по подходящим учебным пособиям. В частности, стоит обращать внимание на рассматриваемую в них версию языка. В этой статье мы собрали для вас самые лучшие книги по Python 3, подходящие для начинающих.
1. Python для чайников
Автор: Джон Пол Мюллер. Год издания: 2019.
Отличная книга для тех, кто решается учить Python с нуля. Для начала автор познакомит вас со средой Python (установка в разных операционных системах, работа с Anaconda). Затем вы перейдете к изучению самых базовых понятий языка (использование переменных, работа со строками, управление данными, функции и т. п.). Конечно же, есть отдельная глава, посвященная циклам.
Помимо чисто образовательного, в этой книге вы найдете и справочный материал, как то: ресурсы для Python-программистов, список утилит для улучшения работы с Python, список самых нужных библиотек.
Автор книги — опытный технический писатель и редактор, выпустивший более 100 книг. Не удивительно, что его произведение так легко читается. Джон Пол Мюллер объясняет сложные концепции на самых простых примерах, а для еще лучшего понимания в книге имеются иллюстрации.
2. Изучаем Python, том 1
Автор: Марк Лутц. Год издания: 2019.
Как отмечают многие читатели, если вы ищете краткое справочное пособие, то учебник Лутца вам не подойдет. Но для начинающих, которые к тому же хотят досконально разобраться в языке, эта книга — самое то!
Книга написана автором на основе его собственного обучающего курса по Python. В пятом издании речь идет уже о Python 3.
Читая эту книгу, вы:
3. Как устроен Python
Автор: Мэтт Харрисон. Год издания: 2019.
Отличный вариант для тех, кто хочет изучить Python, не имея никакого бэкграунда в программировании.
Читая эту книгу, вы усвоите лучшие подходы к написанию кода на Python, не тратя время на поиски нужной информации или попытки заставить Python вести себя подобно другим языкам. Автор книги собрал все самое важное, что узнал об этом языке за годы работы с ним.
Из книги Мэтта Харрисона вы узнаете, как работают интерпретируемые языки программирования. Вы познакомитесь с основными типами (строками, целыми и вещественными числами), разберетесь, чем отличаются изменяемые и неизменяемые данные, научитесь работать со списками, множествами и словарями.
Эта книга поможет вам освоить язык Python максимально быстро.
4. Python 3. Самое необходимое
Авторы: Н. А. Прохоренок, В. А. Дронов. Год издания: 2019
Как и заявлено в названии, в этой книге вы найдете все самое необходимое для начинающего питониста. Базовый синтаксис, основы ООП, стандартная библиотека, работа с модулями — обо всем этом авторы рассказывают в доступной форме. Благодаря хорошей структуре и сжатому изложению в дальнейшем вы сможете пользоваться этой книгой как настольным справочником по Python.
Многочисленные примеры помогут вам разобраться в излагаемых концепциях и немного «набить руку» в написании кода (если, конечно, вы будете испытывать их на практике, а не только читать).
5. Начинаем программировать на Python
Автор: Тони Гэддис. Год издания: 2019.
На различных форумах об этой книге как об учебнике для начинающих отзываются ну очень хорошо. Может, даже лучше, чем обо всех остальных книгах в нашем списке. По мнению читателей, она и не слишком растянута (в этом часто упрекают книгу Лутца), и достаточно глубока. К тому же, в ней используется свежая версия языка (Python 3.6.4).
Книга ориентирована на людей, в принципе не имеющих опыта в программировании (первая глава — «Введение в компьютеры и программирование»). Как и во всех остальных книгах для начинающих, здесь разбирается весь базовый синтаксис языка. Также затрагиваются темы объектно-ориентированного и функционального программирования. Для наглядности материал проиллюстрирован.
6. Легкий способ выучить Python 3
Автор: Зед А. Шоу. Год издания: 2019.
Прекрасная книга для изучения основ языка и (это важно!) отработки знаний на практике. Здесь подход к обучению следующий: вы читаете упражнения, переписываете их (без копипаста!), запускаете, обнаруживаете, что ничего не работает, перечитываете, исправляете ошибки, запускаете… В общем, базовый синтаксис к концу книги у вас перейдет в мышечную память.
Разумеется, в книге Зеда Шоу рассматривается весь набор стандартных тем для начинающих. При этом стоит особенно отметить легкий, юморной стиль изложения. Читать будет интересно!
7. Изучаем Python
Автор: Эрик Мэтиз. Год издания: 2020.
Практическое руководство по Python для начинающих. В первой части рассматриваются все необходимые основы, а во второй вы переходите к созданию проектов (игра, визуализация данных, веб-приложение). В приложениях дается дополнительный материал об установке Python, настройке редактора кода Sublime Text, контроле версий при помощи Git.
Как выучить python || План обучения с нуля
Всем привет. Решил поделиться планом обучения python с нуля и до приемлимого уровня.
Итак рекомендую начать обучение с прочтения книги «Byte of Python « или по русски укус питона. Это очень маленькая книжка, прочтение которой не отнимет у вас много времени, но зато вы уже сможете получить знания об основах языка. Все кратно и по делу.
Ссылка: https://wombat.org.ua/AByteOfPython/#id10
Далее стоит закрепить полученные знания в курсе на сайте stepik : «Поколение Python»: курс для начинающих
После этого курса стоит немного уделить времени алгоритмам, в этом нам поможет книга “Грокаем аглоритмы”, все примеры в этой книге так же разбираются на языке пайтон, что очень удобно для нас.
После книги про алгоритмы возвращаемся на stepik и проходим курс «Python: основы и применение.» Здесь вы еще больше погрузитесь в язык. изучите классы, попробуете поработать с различными АПИ. В общем будет чем заняться.
Ссылка: https://stepik.org/course/512/syllabus
После этого курса я рекомендую пару недель уделить только решению различных задач по программированию. Для этого существует множество сайтов, я оставлю ссылки на все в описании. Выбирайте какой больше понравится. Лично мне больше всех понравился chekio. Знаю, что многие любят codewars.
Сайты где можно порешать задачки Python:
Вне зависимости от того какой сайт вы выберете, рекомендую начинать с более простых задач, и решать по 3-5 штук в день. Так же после решения, вам будет позволено посмотреть как эту задачу решили другие участники. Почти всегда это будет сделано более элегантным или продвинутым способом. В общем на решениях других людей тоже можно многое почерпнуть.
Далее стоит ознакомиться с ютуб каналом Computer science center
Здесь уже рассматривают продвинутые техники языка. Очень рекомендую к ознакомлению.
Если вам больше нравится читать книги, то вместо этого курса могу порекомендовать книгу «Python. К вершинам мастерства.»
Еще есть вот такой курс от Яндекса.
Так же не забываем постоянно решать задачки с сайтов, чтобы постоянно была практика.
При среднем темпе обучения, на все эти шаги у вас уйдет 3-4 месяца.
На этом этапе уже можно спокойно начать погружаться в выбранную Вами сферу, будь это веб программирование с помощью фреймворков джанго, фласк или может быть датасаенс. Это уже выходит за рамки данного поста. То как стать датасаентистом, мы разбирали в этом посте:
Так же эти ресурсы могут быть вам полезны:
Книга: Изучаем Python. Том 1 | Лутц Марк
Книга: Изучаем Python. Том 2 | Лутц Марк
Книга: Доусон М. Программируем на Python.
У нас есть чатик в телеграмме где мы обсуждаем с подписчиками различные вопросы, залетай к нам! https://t.me/DataScienceGuy
Сейчас смотрю лекции Хирьянова.
Салют! Ты с 0 осваивал pyhton? Сколько времени заняло обучение чтобы выйти на начальный уровень?
О, от яндекса курс не видела ещё, спасибо.
Здравствуйте. Есть предложение по сотрудничеству. Если интересно- напишите ermolenko_olja@mail.ru
Давно хотел изучить Питон, но никак не мог найти правильный ракурс подхода к данному вопросу. Опираясь на Ваш пост уже прошел курс «Поколение Python: курс для начинающих» и прочел книги «Byte of Python» и «Грокаем алгоритмы». Так вот, к чему это я. Дойдя до курса «Python: основы и применение.» я познал боль и отчаяние. Был вынужден искать новые пути познания и пока остановился на курсе «Инди-курс программирования на Python от egoroff_channel» от Артема Егорова (ссылку оставлять не стану на случай, если автор против подобных вбросов). Данный курс часто появлялся в комментах на «Основах и применении» как единственная панацея, что я и решил проверить. Не знаю, проблема ли в качестве изложения авторов курса из Вашего списка, либо в резком перепаде сложности курсов, но что-то не пошло. Возможно стоит рассмотреть какую-то прослойку из переходного курса или вспомогательных материалов. Дело Ваше, но я говорю от лица человека, который по данному посту с нуля решил обучиться и встретил определенные сложности.
Во всяком случае, путь мой только начат, и начат он не без Вашей помощи. Спасибо 🙂
Пы.Сы: хотел просто вбросить про возможно неплохое дополнение к программе обучения, но что-то занесло хехехе.
Крутяк! Хотеть подобный пост про Java
А мне кажется, что бесплатно сложно научиться, мотивация не такая сильная, в отличие от того
если ты заплатил за курс. Есть относительно не дорогие, но очень качественные курсы https://codeby.net/threads/kurs-python-dlja-pentestera.70415. Пять месяцев обучения от нуля до продвинутого. Домашнее задание проверяется в ручную, преподаватели всегда на связи.
Спасибо, а почему бы и нет!
А у меня создалось ощущение, что питон просто очередная мода. Может быть востребован ещё долго, а может и отвалится. Помнится на перл поначалу также многие молились.
Сам пользуюсь уже лет 10 для автоматизации и мне в принципе питон очень нравится. Но крупный серьёзный проект скорее всего на нем не стал бы делать.
За сссылки спасибо, сохранил.
Оставлю комент. Завтра почитаю.
ТС скажи как у питона обстоят дела с ООП
На всякий случай изучу. А то в селе скучно стало, клуб и тот закрыли.
найти заказчика, который скажет: Тебе надо закрыть этот проект на питоне «на вчера»
Ну не летом же начинать такое серьезное дело, а вот с числа точно начну!:))
Естественный отбор
Английский для программирования не важен!©
Как я делал систему оптического трекинга
Дело было в далеком 2015 году. В продаже только появились очки виртуальной реальности Oculus DK2, рынок VR игр быстро набирал популярность.
Возможности игрока в таких играх были невелики. Отслеживалось всего 6 степеней свободы движений головы — вращение (инерциалкой в очках) и перемещение в маленьком объеме в зоне видимости инфракрасной камеры, закрепленной на мониторе. Процесс игры представлял собой сидение на стуле с геймпадом в руках, вращение головой в разные стороны и борьбу с тошнотой.
Звучало не очень круто, но я увидел в этом возможность сделать что-то интересное, используя свой опыт в разработке электроники и жажду новых проектов. Как можно было эту систему улучшить?
Конечно, избавиться от геймпада, от проводов, дать возможность игроку свободно перемещаться в пространстве, видеть свои руки и ноги, взаимодействовать с окружением, другими игроками и реальными интерактивными предметами.
1) Берем несколько игроков, надеваем на них VR очки, ноутбук и датчики на руки, ноги и туловище.
2) Берем помещение, состоящее из нескольких комнат, коридоров, дверей, оборудуем его системой трекинга, вешаем датчики и магнитные замки на двери, добавляем несколько интерактивных предметов и создаем игру, в которой геометрия виртуальной локации точно повторяет геометрию реального помещения.
3) Создаем игру. Игра представляет собой многопользовательский квест, в котором несколько игроков надевают на себя оборудование и оказываются в виртуальном мире. В нем они видят себя, видят друг друга, могут ходить по локации, открывать двери и совместно решать игровые задачи.
Эту идею я рассказал своему товарищу, который неожиданно воспринял ее с большим энтузиазмом и предложил взять на себя организационные вопросы. Так мы решили мутить стартап.
Для реализации заявленного функционала, нужно было создать две основные технологии:
1) Костюм, состоящий из датчиков на руках, ногах и торсе, отслеживающий положения частей тела игрока
2) Система трекинга, отслеживающая игроков и интерактивные объекты в 3D пространстве.
Про разработку второй технологии и пойдет речь в этой статье. Может быть, позже напишу и про первую.
Бюджета на все это, конечно, у нас не было, поэтому нужно было сделать все из подручных материалов. Для задачи отслеживания игроков в пространстве я решил использовать оптические камеры и светодиодные маркеры, закрепленные на VR очках. Опыта подобных разработок у меня не было, но я уже что-то слышал про OpenCV, Python, и подумал, что справлюсь.
По задумке, если система знает где расположена камера и как она ориентирована, то по положению изображения маркера на кадре можно определить прямую в 3D пространстве, на которой этот маркер находится. Пересечение двух таких прямых дает итоговое положение маркера.
Соответственно, камеры нужно было закрепить на потолке так, чтобы каждая точка пространства просматривалась минимум двумя камерами (лучше больше, чтобы избежать перекрытия обзора телами игроков). Для покрытия трекингом предполагаемого помещения площадью около 100 кв.м., требовалось около 60 камер. Я выбрал первые попавшиеся дешевые на тот момент usb вебки.
Эти вебки нужно к чему-то подключать. Эксперименты показали, что при использовании usb удлинителей (по крайней мере, дешевых), камеры начинали глючить. Поэтому решил разделить вебки на группы по 8 штук и втыкать их в системники, закрепленные на потолке. На моем домашнем компе как раз было 10 usb портов, так что пришло время начинать разработку тестового стенда.
Архитектуру я придумал следующую:
На каждые очки вешается акриловый матовый шарик от гирлянды с вклеенным внутрь RGB светодиодом. Одновременно в игре предполагалось несколько игроков, так что для идентификации решил разделять их по цвету – R, G, B, RG, RB, GB, RB. Вот так это выглядело:
Первая задача, которую нужно выполнить – написать программу поиска шарика на кадре.
Поиск шарика на кадре
Мне нужно было в каждом кадре, пришедшем с камеры, искать координаты центра шарика и его цвет для идентификации. Звучит несложно. Качаю OpenCV под Python, втыкаю камеру в usb, пишу скрипт. Для минимизации влияния лишних объектов на кадре, выставляю экспозицию и выдержку на камере в самый минимум, а яркость светодиода делаю высокой, чтобы получить яркие пятна на темном фоне. В первой версии алгоритм был следующий:
1) Переводим изображение в градации серого
2) Бинаризуем по порогу (если яркость пикселя больше порога, он становится белым, иначе – черным). При этом размытое пятно от шарика превращается в кластер белых пикселей на черном фоне
3) Находим контуры кластеров и их центры. Это и есть координаты шарика на кадре
4) Определяем усредненный цвет пикселей кластера (на исходном цветном изображении) в окрестности его центра для идентификации
Вроде, работает, но есть нюансы.
Во-первых, на дешевой камере матрица довольно шумная, что приводит к постоянным флуктуациям контуров бинаризованных кластеров и соответственно к дерганью центра. Нельзя, чтобы у игроков дергалась картинка в VR очках, поэтому нужно было эту проблему решать. Попытки применять другие виды адаптивной бинаризации с разными параметрами не давали большого эффекта.
Во-вторых, разрешение камеры всего лишь 640*480, поэтому на некотором расстоянии (не очень большом) шарик виден как пара пикселей на кадре и алгоритм поиска контуров перестает нормально работать.
Пришлось придумывать новый алгоритм. В голову пришла следующая идея:
1) Переводим изображение в градации серого
2) Размываем картинку мощным Gaussian blur –ом так, чтобы изображения светодиодов превратились в размытые пятна с градиентом яркости от центра к периферии
3) Находим самые яркие пиксели на изображении, они должны соответствовать центрам пятен
4) Так же определяем средний цвет кластера в окрестности центра
Так работает гораздо лучше, координаты центра при неподвижном шарике неподвижны, и работает даже при большом расстоянии от камеры.
Чтобы убедиться, что все это будет работать с 8-ю камерами на одном компе, нужно провести нагрузочный тест.
Подключаю 8 камер к своему десктопу, располагаю их так, чтобы каждая видела светящиеся точки и запускаю скрипт, где описанный алгоритм работает в 8-ми независимых процессах (спасибо питонской либе «multiprocessing») и обрабатывает все потоки сразу.
И… сразу натыкаюсь на фейл. Изображения с камер то появляются, то исчезают, framerate скачет от 0 до 100, кошмар. Расследование показало, что часть usb портов на моем компе подключены к одной шине через внутренний хаб, из-за чего скорость шины делится между несколькими портами и ее уже не хватает на битрейт камер. Втыкание камер в разные порты компа в разных комбинациях показало, что у меня всего 4 независимых usb шины. Пришлось найти материнку с 8-ю шинами, что было довольно непростым квестом.
Продолжаю нагрузочный тест. На этот раз все камеры подключились и выдают нормальные потоки, но сразу сталкиваюсь со следующей проблемой – низкий fps. Процессор загружен на 100% и успевает обрабатывать лишь 8-10 кадров в секунду с каждой из восьми вебок.
Похоже, нужно оптимизировать код. Узким местом оказалось Гауссово размытие (оно и не удивительно, ведь нужно на каждый пиксель кадра производить свертку с матрицей 9*9). Уменьшение ядра не спасало ситуацию. Пришлось искать другой метод нахождения центров пятен на кадрах.
Решение удалось найти внезапно во встроенной в OpenCV функции SimpleBlobDetector. Она делает прямо то, что мне нужно и очень быстро. Преимущество достигается благодаря последовательной бинаризации изображения с разными порогами и поиску контуров. Результат – максимальные 30 fps при загрузке процессора меньше 40%. Нагрузочный тест пройден!
Классификация по цвету
Следующая задача – классификация маркера по его цвету. Усредненное значение цвета по пикселям пятна дает RGB компоненты, которые очень нестабильны и сильно меняются в зависимости от расстояния до камеры и яркости светодиода. Но есть отличное решение: перевод из RGB пространства с HSV (hue, saturation, value). В таком представлении пиксель вместо «красный», «синий», «зеленый», раскладывается на компоненты «тон», «насыщенность», «яркость». В этом случае насыщенность и яркость можно просто исключить и классифицировать только по тону.
И так, на данный момент я научился находить и идентифицировать маркеры на кадрах с большого количества камер. Теперь можно перейти к следующему этапу – трекингу в пространстве.
Я использовал pinhole модель камеры, в которой все лучи падают на матрицу через точку, находящуюся на фокусном расстоянии от матрицы.
По этой модели будет происходить преобразование двухмерных координат точки на кадре в трехмерные уравнения прямой в пространстве.
Для отслеживания 3D координат маркера нужно получить минимум две скрещивающиеся прямые в пространстве от разных камер и найти точку их пересечения. Увидеть маркер двумя камерами не сложно, но для построения этих прямых нужно, чтобы система знала все о подключенных камерах: где они висят, под какими углами, фокусное расстояние каждого объектива. Проблема в том, что все это неизвестно. Для вычисления параметров требуется некая процедура калибровки.
В первом варианте решил сделать калибровку трекинга максимально примитивной.
1) Вешаю первый блок из восьми камер на потолок, подключаю их к системнику, висящему там же, направляю камеры так, чтобы ими покрывался максимальный игровой объем.
2) С помощью лазерного нивелира и дальномера измеряю XYZ координаты всех камер в единой системе координат
3) Для вычисления ориентаций и фокусных расстояний камер, измеряю координаты специальных стикеров. Стикеры вешаю следующим образом:
В интерфейсе отображения картинки с камеры рисую две точки. Одну в центре кадра, другую в 200 пикселях справа от центра:
Если смотреть на кадр, эти точки падают куда-то на стену, пол или любой другой объект внутри помещения. Вешаю в соответствующие места бумажные наклейки и рисую на них точки маркером.
Измеряю XYZ координаты этих точек с помощью тех же нивелира и дальномера. Итого для блока из восьми камер нужно измерить координаты самих камер и еще по две точки на каждую. Т.е. 24 тройки координат. А таких блоков должно быть около десяти. Получается долгая муторная работа. Но ничего, позже сделаю калибровку автоматизированной.
Запускаю процесс расчета на основе измеренных данных.
Есть две системы координат: одна глобальная, связанная с помещением, другая локальная для каждой камеры. В моем алгоритме результатом для каждой камеры должна получиться матрица 4*4, содержащая ее местоположение и ориентацию, позволяющая преобразовать координаты из локальной в глобальную систему.
1) Берем исходную матрицу с нулевыми поворотами и смещением.
2) Берем единичный вектор в локальной системе камеры, который смотрит из объектива вперед и преобразуем его в глобальные координаты по исходной матрице.
3) Берем другой вектор в глобальной системе, который из камеры смотрит на центральную точку на стене.
4) С помощью градиентного спуска поворачиваем исходную матрицу так, чтобы после преобразования эти векторы были сонаправлены. Таким образом, мы зафиксировали направление камеры. Осталось зафиксировать вращение вокруг этого направления. Для этого и измерялась вторая точка в 200 пикселях от центра кадра. Поворачиваем матрицу вокруг главной оси, пока два вектора не станут достаточно параллельны.
5) По расстоянию между этими двумя точками вычисляю фокусное расстояния в пикселях (учитывая, что расстояние между проекциями этих точек на кадре составляет 200 пикселей).
Наверняка эту задачу можно было решить аналитически, но для простоты я использовал численное решение на градиентном спуске. Это не страшно, т.к. вычисления будут проводиться один раз после монтажа камер.
Для визуализации результатов калибровки я сделал 2D интерфейс с картой, на которой скрипт рисует метки камер и направления, в которых они видят маркеры. Треугольником обозначаются ориентации камер и углы обзора.
Можно приступать к запуску визуализации, которая покажет правильно ли определились ориентации камер и правильно ли интерпретируются кадры. В идеале, линии, идущие из значков камер должны пересекаться в одной точке.
Похоже на правду, но точность явно могла быть выше. Первая причина несовершенства, которая пришла в голову – искажения в объективах камер. Значит, нужно эти искажения как-то компенсировать.
У идеальной камеры важный для меня параметр только один – фокусное расстояние. У реальной кривой камеры нужно учитывать еще дисторсии объектива и смещение центра матрицы.
Для измерения этих параметров есть стандартная процедура калибровки, в процессе которой измеряемой камерой делают набор фотографий шахматной доски, на которых распознаются углы между квадратами с субпиксельной точностью.
Результатом калибровки является матрица, содержащая фокусные расстояния по двум осям и смещение матрицы относительно оптического центра. Все это измеряется в пикселях.
А также вектор коэффициентов дисторсии, который позволяет компенсировать искажения объектива с помощью преобразований координат пикселей.
Применяя преобразования с этими коэффициентами к координатам маркера на кадре, можно привести систему к модели идеальной pinhole камеры.
Провожу новый тест трекинга:
Уже гораздо лучше! Выглядит настолько хорошо, что даже вроде будет работать.
Вычисление координат маркера
И так, я получил кучу прямых, разбросанных по пространству, на пересечениях которых должны находиться маркеры. Только вот прямые в пространстве на самом деле не пересекаются, а скрещиваются, т.е. проходят на некотором расстоянии друг от друга. Моя задача – найти точку, максимально близкую к обеим прямым. Формально говоря, нужно найти середину отрезка, являющегося перпендикуляром к обеим прямым.
Длина отрезка AB тоже пригодится, т.к. она отражает «качество» полученного результата. Чем он короче, тем ближе друг к другу прямые, тем лучше результат.
Затем я написал алгоритм трекинга, который попарно вычисляет пересечения прямых (внутри одного цвета, от камер, находящихся на достаточном расстоянии друг от друга), ищет лучшее и использует его как координаты маркера. На следующих кадрах старается использовать ту же пару камер, чтобы избежать скачка координат при переходе на трекинг другими камерами.
Параллельно, при разработке костюма с датчиками, я обнаружил странное явление. Все датчики показывали разные значения угла рысканья (направления в горизонтальной плоскости), как будто у каждого был свой север. В первую очередь полез проверять не ошибся ли я в алгоритмах фильтрации данных или в разводке платы, но ничего не нашел. Потом решил посмотреть на сырые данные магнитометра и увидел проблему.
Магнитное поле в нашем помещении было направлено ВЕРТИКАЛЬНО ВНИЗ! Видимо, это связано с железом в конструкции здания.
Но ведь в VR очках тоже используется магнитометр. Почему у них такого эффекта нет? Иду проверять. Оказалось, что в очках он тоже есть… Если сидеть неподвижно, можно заметить, как виртуальный мир медленно, но верно вращается вокруг тебя в рандомную сторону. За минут 10 он уезжает почти на 180 градусов. В нашей игре это неминуемо приведет к рассинхрону виртуальной и реальной реальностей и сломанным об стены очкам.
Похоже, что помимо координат очков, придется определять и их направление в горизонтальной плоскости. Решение напрашивается само – ставить на очки не один, а два одинаковых маркера. Оно позволит определять направление с точностью до разворота на 180 градусов, но с учетом наличия встроенных инерциальных датчиков, этого вполне достаточно.
Система в целом работала, хоть и с небольшими косяками. Но было принято решение запустить квест, который как раз был близок к завершению нашим gamedev разработчиком, присоединившимся к нашей миникоманде. Была затречена вся игровая площадь, установлены двери с датчиками и магнитными замками, изготовлено два интерактивных предмета:
Игроки надевали очки, костюмы и рюкзаки-компьютеры и заходили в игровую зону. Координаты трекинга отсылались им по wi-fi и применялись для позиционирования виртуального персонажа. Все работало достаточно неплохо, посетители довольны. Приятнее всего было наблюдать ужас и крики особо впечатлительных посетителей в моменты, когда на них из темноты нападали виртуальные призраки =)
Внезапно нам прилетел заказ на большой VR шутер на 8 игроков с автоматами в руках. А это 16 объектов, которые нужно тречить. Повезло, что сценарий предполагал возможность разделения трекинга на две зоны по 4 игрока, поэтому я решил, что проблем не будет, можно принимать заказ и ни о чем не волноваться. Протестировать систему в домашних условиях было невозможно, т.к. требовалась большая площадь и много оборудования, которое будет куплено заказчиком, поэтому до монтажа я решил потратить время на автоматизацию калибровки трекинга.
Первым делом нужно было централизовать всю систему. Вместо разделения игровой зоны на блоки по 8 камер, я сделал единый сервер, на который приходили координаты точек на кадрах всех камер сразу.
1) вешаю камеры и на глаз направляю их в игровую область
2) запускаю режим записи на сервере, в котором все приходящие с камер 2D точки сохраняются в файл
3) хожу по темной игровой локации с маркером в руках
4) останавливаю запись и запускаю расчет калибровочных данных, при котором вычисляются расположения, ориентации и фокусные расстояния всех камер.
5) в результате предыдущего пункта получается единое пространство, наполненное камерами. Т.к. это пространство не привязано к реальным координатам, оно имеет случайное смещение и поворот, которое я вычитаю вручную.
Пришлось перелопатить огромное количество материала по линейной алгебре и написать многие сотни строк питонского кода. Настолько много, что я уже почти не помню как оно работает.
Вот так выглядит напечатанная на принтере специальная палка-калибровалка.
Тестирование большого проекта
Проблемы начались во время тестирования на объекте за пару недель до запуска проекта. Идентификация 8-ми разных цветов маркеров работала ужасно, тестовые игроки постоянно телепортировались друг в друга, некоторые цвета вообще не отличались от внешних засветок в помещении торгового комплекса. Тщетные попытки что-то исправить с каждой бессонной ночью все сильнее вгоняли меня в отчаяние. Все это осложнялось нехваткой производительности сервера при расчете десятков тысяч прямых в секунду.
Когда уровень кортизола в крови превысил теоретический максимум, я решил посмотреть на проблему с другой стороны. Как можно сократить количество разноцветных точек, не сокращая количество маркеров? Сделать трекинг активным. Пускай у каждого игрока, например, левый рог всегда корит красным. А второй иногда загорается зеленым по приходу команды с сервера так, что в один момент времени он горит только у одного игрока. Получается, что зеленая лампочка будет как-будто перепрыгивать с одного игрока на другого, обновляя привязку трекинга к красной лампочке и обнуляя ошибку ориентации магнитометра.
Для этого пришлось бежать в ближайший чипидип, покупать светодиоды, провода, транзисторы, паяльник, изоленту и на соплях навешивать функционал управления светодиодами на плату костюма, которая на это рассчитана не была. Хорошо, что при разводке платы я на всякий случай повесил пару свободных ног stm-ки на контактные площадки.
Алгоритмы трекинга пришлось заметно усложнить, но в итоге все заработало! Телепортации игроков друг в друга исчезли, нагрузка на процессор упала, засветки перестали мешать.
Проект был успешно запущен, первым делом я сделал новые платы костюмов с поддержкой активного трекинга, и мы произвели обновление оборудования.
Чем все закончилось?
За 3 года мы открыли множество развлекательных точек по всему миру, но коронавирус внес свои коррективы, что дало нам возможность сменить направление работы в более общественно-полезную сторону. Теперь мы довольно успешно занимаемся разработкой медицинских симуляторов в VR. Команда у нас все еще маленькая и мы активно стремимся расширять штат. Если среди читателей есть опытные разработчики под UE4, ищущие работу, пожалуйста, напишите мне.
Традиционный забавный момент в конце статьи:
Периодически при тестах с большим количеством игроков возникал глюк, при котором игрока внезапно на короткое время телепортировало на высоту несколько метров, что вызывало соответствующую реакцию. Дело оказалось в том, что моя модель камеры предполагала пересечение матрицы с бесконечной прямой, идущей от маркера. Но она не учитывала, что у камеры есть перед и зад, так что система искала пересечение бесконечных прямых, даже если точка находится за камерой. Поэтому возникали ситуации, когда две разные камеры видели два разных маркера, но система думала, что это один маркер на высоте в несколько метров.
Система в прямом смысле работала через задницу =)
Вот уже на протяжении нескольких лет Тимофей, преподаватель кафедры информатики МФТИ, выкладывает свои лекции по программированию на своём Youtube канале с открытым доступом.
Разработка системы заметок с нуля. Часть 2: REST API для RESTful API Service + JWT + Swagger
Продолжаем серию материалов про создание системы заметок. В этой части мы спроектируем и разработаем RESTful API Service на Go cо Swagger и авторизацией. Будет много кода, ещё больше рефакторинга и даже немного интеграционных тестов.
В первой части мы спроектировали систему и посмотрели, какие сервисы требуются для построения микросервисной архитектуры.
Подробности в видео и текстовой расшифровке под ним.
Начнём с макетов интерфейса. Нам нужно понять, какие ручки будут у нашего API и какой состав данных он должен отдавать. Макеты мы будем делать, чтобы понять, какие сущности, поля и эндпоинты нам нужны. Используем для этого онлайн-сервис NinjaMock. Он подходит, если макет надо сделать быстро и без лишних действий.
Страницу регистрации сделаем простую, с четырьмя полями: Name, Email, Password и Repeat Password. Лейблы делать не будем, обойдемся плейсходерами. Авторизацию сделаем по юзернейму и паролю.
После входа в приложение пользователь увидит список заметок, который будет выглядеть примерно так:
Интерфейс, который будет у нашего веб-приложения:
— Слева — список категорий любой вложенности.
— Справа — список заметок в виде карточек, который делится на два списка: прикреплённые и обычные карточки.
— Каждая карточка состоит из заголовка, который урезается, если он очень длинный.
— Справа указано, сколько секунд/минут/часов/дней назад была создана заметка.
— Тело заголовка — отрендеренный Markdown.
— Панель инструментов. Через неё можно изменить цвет, прикрепить или удалить заметку.
Тут важно отметить, что файлы заметки мы не отображаем и не будем запрашивать у API для списка заметок.
Полная карточка открывается по клику на заметку. Тут можно сразу отобразить полностью длинный заголовок. Высота заметки зависит от количества текста. Для файлов появляется отдельная секция. Мы их будем получать отдельным асинхронным запросом, который не помешает пользователю редактировать заметку. Файлы можно скачать по ссылке, также есть отдельная кнопка на добавление файлов.
Так будет выглядеть открытая заметка
В ходе прототипирования стало понятно, что в первой части мы забыли добавить еще один микросервис — TagsService. Он будет управлять тегами.
Для страниц авторизации и регистрации нам нужны эндпоинты аутентификации и регистрации соответственно. В качестве аутентификации и сессий пользователя мы будем использовать JWT. Что это такое и как работает, разберём чуть позднее. Пока просто запомните эти 3 буквы.
Для страницы списка заметок нам нужны эндпоинты /api/categories для получения древовидного списка категорий и /api/notes?category_id=? для получения списка заметок текущей категории. Перемещаясь по другим категориям, мы будем отдельно запрашивать заметки для выбранной категории, а на фронтенде сделаем кэш на клиенте. В ходе работы с заметками нам нужно уметь создавать новую категорию. Это будет метод POST на URL /api/categories. Также мы будем создавать новый тег при помощи метода POST на URL /api/tags.
Чтобы обновить заметку, используем метод PATCH на URL /api/notes/:uuid с измененными полями. Делаем PATCH, а не PUT, потому что PUT требует отправки всех полей сущности по спецификации HTTP, а PATCH как раз нужен для частичного обновления. Для отображения заметки нам ещё нужен эндпоинт /api/notes/:uuid/files с методами POST и GET. Также нам нужно скачивать файл, поэтому сделаем метод GET на URL /api/files/:uuid.
Структура репозитория системы
Ещё немного общей информации. Структура репозитория всей системы будет выглядеть следующим образом:
В директории app будет исходный код сервиса (если он будет). На уровне с app будут другие директории других продуктов, которые используются с этим сервисом, например, MongoDB или ELK. Продукты, которые будут использоваться на уровне всей системы, например, Consul, будут в отдельных директориях на уровне с сервисами.
Писать будем на Go
— Идём на официальный сайт.
— Копируем ссылку до архива, скачиваем, проверяем хеш-сумму.
— Распаковываем и добавляем в переменную PATH путь до бинарников Go
— Пишем небольшой тест проверки работоспособности, собираем бинарник и запускаем.
Установка завершена, всё работает
Теперь создаём проект. Структура стандартная:
— cmd — точка входа в приложение,
— internal — внутренняя бизнес-логика приложения,
— pkg — для кода, который можно переиспользовать из проекта в проект.
Я очень люблю логировать ход работы приложения, поэтому перенесу свою обёртку над логером logrus из другого проекта. Основная функция здесь Init, которая создает логер, папку logs и в ней файл all.log со всеми логами. Кроме файла логи будут выводиться в STDOUT. Также в пакете реализована поддержка логирования в разные файлы с разным уровнем логирования, но в текущем проекте мы это использовать не будем.
APIService будет работать на сокете. Создаём роутер, затем файл с сокетом и начинаем его слушать. Также мы хотим перехватывать от системы сигналы завершения работы. Например, если кто-то пошлёт приложению сигнал SIGHUP, приложение должно корректно завершиться, закрыв все текущие соединения и сессии. Хотел перехватывать все сигналы, но линтер предупреждает, что os.Kill и SIGSTOP перехватить не получится, поэтому их удаляем из этого списка.
Теперь давайте добавим сразу стандартный handler для метрик. Я его копирую в директорию pkg, далее добавляю в роутер. Все последующие роутеры будем добавлять так же.
Далее создаём точку входа в приложение. В директории cmd создаём директорию main, а в ней — файл app.go. В нём мы создаём функцию main, в которой инициализируем и создаём логер. Роутер создаём через ключевое слово defer, чтобы метод Init у роутера вызвался только тогда, когда завершится функция main. Таким образом можно выполнять очистку ресурсов, закрытие контекстов и отложенный запуск методов. Запускаем, проверяем логи и сокет, всё работает.
Но для разработки нам нужно запускать приложение на порту, а не на сокете. Поэтому давайте добавим запуск приложения на порту в наш роутер. Определять, как запускать приложение, мы будем с помощью конфига.
Создадим для приложения контекст. Сделаем его синглтоном при помощи механизма sync.Once. Пока что в нём будет только конфиг. Контекст в виде синглтона создаю исключительно в учебных целях, впоследствии он будет выпилен. В большинстве случаев синглтоны — необходимое зло, в нашем проекте они не нужны. Далее создаём конфиг. Это будет YAML-файл, который мы будем парсить в структуру.
В роутере мы вытаскиваем из контекста конфиг и на основании listen.type либо создаем сокет, либо вешаем приложение на порт. Код graceful shutdown выделяем в отдельный пакет и передаём на вход список сигналов и список интерфейсов io.Close, которые надо закрывать. Запускаем приложение и проверяем наш эндпоинт heartbeat. Всё работает. Давайте и конфиг сделаем синглтоном через механизм sync.Once, чтобы потом безболезненно удалить контекст, который создавался в учебных целях.
Теперь переходим к API. Создаём эндпоинты, полученные при анализе прототипов интерфейса. Тут важно отметить, что у нас все данные привязаны к пользователю. На первый взгляд, все ручки должны начинаться с пользователя и его идентификатора /api/users/:uuid. Но у нас будет авторизация, иначе любой пользователь сможет программно запросить заметки любого другого пользователя. Авторизацию можно сделать следующим образом: Basic Auth, Digest Auth, JSON Web Token, сессии и OAuth2. У всех способов есть свои плюсы и минусы. Для этого проекта мы возьмём JSON Web Token.
Работа с JSON Web Token
JSON Web Token (JWT) — это JSON-объект, который определён в открытом стандарте RFC 7519. Он считается одним из безопасных способов передачи информации между двумя участниками. Для его создания необходимо определить заголовок (header) с общей информацией по токену, полезные данные (payload), такие как id пользователя, его роль и т.д., а также подписи (signature).
JWT использует преимущества подхода цифровой подписи JWS (Signature) и кодирования JWE (Encrypting). Подпись не даёт кому-то подделать токен без информации о секретном ключе, а кодирование защищает от прочтения данных третьими лицами. Давайте разберёмся, как они могут нам помочь для аутентификации и авторизации пользователя.
Аутентификация — процедура проверки подлинности. Мы проверяем, есть ли пользователь с полученной связкой логин-пароль в нашей системе.
Авторизация — предоставление пользователю прав на выполнение определённых действий, а также процесс проверки (подтверждения) данных прав при попытке выполнения этих действий.
Другими словами, аутентификация проверяет легальность пользователя. Пользователь становится авторизированным, если может выполнять разрешённые действия.
Важно понимать, что использование JWT не скрывает и не маскирует данные автоматически. Причина использования JWT — проверка, что отправленные данные были действительно отправлены авторизованным источником. Данные внутри JWT закодированы и подписаны, но не зашифрованы. Цель кодирования данных — преобразование структуры. Подписанные данные позволяют получателю данных проверить аутентификацию источника данных.
Реализация JWT в нашем APIService:
— Создаём директории middleware и jwt, а также файл jwt.go.
— Описываем кастомные UserClaims и сам middlware.
— Получаем заголовок Authorization, оттуда берём токен.
— Берём секрет из конфига.
— Создаём верификатор HMAC.
— Парсим и проверяем токен.
— Анмаршалим полученные данные в модель UserClaims.
— Проверяем, что токен валидный на текущий момент.
При любой ошибке отдаём ответ с кодом 401 Unauthorized. Если ошибок не было, в контекст сохраняем ID пользователя в параметр user_id, чтобы во всех хендлерах его можно было получить. Теперь надо этот токен сгенерировать. Это будет делать хендлер авторизации с методом POST и эндпоинтом /api/auth. Он получает входные данные в виде полей username и password, которые мы описываем отдельной структурой user. Здесь также будет взаимодействие с UserService, нам надо там искать пользователя по полученным данным. Если такой пользователь есть, то создаём для него UserClaims, в которых указываем все нужные для нас данные. Определяем время жизни токена при помощи переменной ExpiresAt — берём текущее время и добавляем 15 секунд. Билдим токен и отдаём в виде JSON в параметре token. Клиента к UserService у нас пока нет, поэтому делаем заглушку.
Добавим в хендлер с heartbeat еще один тестовый хендлер, чтобы проверить работу аутентификации. Пишем небольшой тест. Для этого используем инструмент sketch, встроенный в IDE. Делаем POST-запрос на /api/auth, получаем токен и подставляем его в следующий запрос. Получаем ответ от эндпоинта /api/heartbeat, по истечении 5 секунд мы начнём получать ошибку с кодом 401 Unauthorized.
Наш токен действителен очень ограниченное время. Сейчас это 15 секунд, а будет минут 30. Но этого всё равно мало. Когда токен протухнет, пользователю необходимо будет заново авторизовываться в системе. Это сделано для того, чтобы защитить пользовательские данные. Если злоумышленник украдет токен авторизации, который будет действовать очень большой промежуток времени или вообще бессрочно, то это будет провал.
Чтобы этого избежать, прикрутим refresh-токен. Он позволит пересоздать основной токен доступа без запроса данных авторизации пользователя. Такие токены живут очень долго или вообще бессрочно. После того как только старый JWT истекает мы больше не можем обратиться к API. Тогда отправляем refresh-токен. Нам приходит новая пара токена доступа и refresh-токена.
Хранить refresh-токены на сервере мы будем в кэше. В качестве реализации возьмём FreeCache. Я использую свою обёртку над кэшем из другого проекта, которая позволяет заменить реализацию FreeCache на любую другую, так как отдает интерфейс Repository с методами, которые никак не связаны с библиотекой.
Пока рассуждал про кэш, решил зарефакторить существующий код, чтобы было удобней прокидывать объекты без dependency injection и синглтонов. Обернул хендлеры и роутер в структуры. В хендлерах сделал интерфейс с методом Register, которые регистрируют его в роутере. Все объекты теперь инициализируются в main, весь роутер переехал в мейн. Старт приложения выделили в отдельную функцию также в main-файле. Теперь, если хендлеру нужен какой-то объект, я его просто буду добавлять в конструктор структуры хендлера, а инициализировать в main. Плюс появилась возможность прокидывать всем хендлерам свой логер. Это будет удобно когда надо будет добавлять поле trace_id от Zipkin в строчку лога.
Вернемся к refresh_token. Теперь при создании токена доступа создадим refresh_token и отдадим его вместе с основным. Сделаем обработку метода PUT для эндпоинта /api/auth, а в теле запроса будем ожидать параметр refresh_token, чтобы сгенерировать новую пару токена доступа и refresh-токена. Refresh-токен мы кладём в кэш в качестве ключа. Значением будет user_id, чтобы по нему можно было запросить данные пользователя у UserService и сгенерировать новый токен доступа. Refresh-токен одноразовый, поэтому сразу после получения токена из кэша удаляем его.
Для описания нашего API будем использовать спецификацию OpenAPI 3.0 и Swagger — YAML-файл, который описывает все схемы данных и все эндпоинты. По нему очень легко ориентироваться, у него приятный интерфейс. Но описывать вручную всё очень муторно, поэтому лучше генерировать его кодом.
— Создаём эндпоинты /api/auth с методами POST и PUT для получения токена по юзернейму и паролю и по Refresh-токену соответственно.
— Добавляем схемы объектов Token и User.
— Создаём эндпоинты /api/users с методом POST для регистрации нового пользователя. Для него создаём схему CreateUser.
Понимаем, что забыли сделать хендлер для регистрации пользователя. Создаём метод Signup у хенлера Auth и структуру newUser со всеми полями для регистрации. Генерацию JWT выделяем в отдельный метод, чтобы можно было его вызывать как в Auth, так и в Signup-хендлерах. У нас всё еще нет UserService, поэтому проставляем TODO. Нам надо будет провалидировать полученные данные от пользователя и потом отправить их в UserService, чтобы он уже создал пользователя и ответил нам об успехе. Далее вызываем функцию создания пары токена доступа и refresh-токена и отдаём с кодом 201.
У нас есть подсказка в виде Swagger-файла. На его основе создаём все нужные хендлеры. Там, где вызов микросервисов, будем проставлять комментарий с TODO.
Создаём хендлер для категорий, определяем URL в константах. Далее создаём структуры. Опираемся на Swagger-файл, который создали ранее. Далее создаём сам хендлер и реализуем метод Register, который регистрирует его в роутере. Затем создаём методы с логикой работы и сразу пишем тест API на этот метод. Проверяем, находим ошибки в сваггере. Таким образом мы создаём все методы по работе с категориями: получение и создание.
Далее создаём таким же образом хендлер для заметок. Понимаем, что забыли методы частичного обновления и удаления как для заметок, так и для категорий. Дописываем их в Swagger и реализуем методы в коде. Также обязательно тестируем Swagger в онлайн-редакторе.
Здесь надо обратить внимание на то, что методы создания сущности возвращают код ответа 201 и заголовок Location, в котором находится URL для получения сущности. Оттуда можно вытащить идентификатор созданной сущности.
В третьей части мы познакомимся с графовой базой данных Neo4j, а также будем работать над микросервисами CategoryService и APIService.