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

Блог компании маклауд

Перевод Разработчик популярного веб-фреймворка FastAPI об истории его создания и перспективах аннотаций типов Python

16.06.2021 12:16:05 | Автор: admin


Python-девелопер и писатель Рики Уайт взял интервью у Себастьяна Рамиреса, разработчика из Explosion AI. Но Себастьян не просто разработчик, это заметная фигура в open source сообществе, создатель популярных фреймворков FastAPI и Typer. В основном речь шла про широкие возможности применения аннотаций типов Python, историю создания фреймворка FastAPI и его дальнейшее развитие. Кроме того, Себастьян рассказал о своих планах по работе над другими open source проектами. Без лишних слов, давайте перейдем к интервью.

Рики: Спасибо, что пришёл, Себастьян. Сначала я бы хотел задать тебе те же вопросы, что и другим своим гостям. Как ты начал программировать? Когда познакомился с Python?

Себастьян: Спасибо, что пригласил [улыбается].

Я начал программировать, когда мне было пятнадцать. Я пытался создать веб-сайт для бизнеса своих родителей. Первым моим настоящим кодом был JavaScript внутри HTML модальное диалоговое окно (alert) с фразой Hello World. Я до сих пор помню, как обрадовался, увидев это маленькое окно с сообщением, и испытал чувство всемогущества от мысли, что это запрограммировал я.

Я много лет боялся изучать какой-либо другой язык, думая, что сначала должен хотя бы освоить JavaScript. Но потом на одном из многих онлайн-курсов, которые я проходил, возникла необходимость использовать Python для управления искусственным интеллектом в Pac-Man и для некоторых других задач. Курс состоял из одного длинного туториала по основам Python, и этого было достаточно. Мне очень хотелось попробовать.

Я быстро влюбился в Python и пожалел, что не начал раньше!

Рики: На сегодняшний день мне известно [поправь меня, если ошибаюсь], что ты трудишься в Explosion AI, компании, создавшей популярную платформу обработки естественного языка (NLP-библиотеку spaCy). Расскажи немного о трудовых буднях. Какие задачи в сфере искусственного интеллекта и машинного обучения интересуют команду и какие инструменты создала компания, чтобы помочь разработчикам быстрее продвигаться в обеих областях?

Себастьян: Да, Explosion в основном известен благодаря spaCy. Это NLP-библиотека с открытым исходным кодом. Они также создали Prodigy, коммерческий инструмент с поддержкой скриптования для эффективного аннотирования наборов данных в машинном обучении. Я работал в основном в Prodigy Teams. Это облачная версия Prodigy для совместного использования. Поскольку продукт ориентирован на конфиденциальность, создание облачной версии было связано с множеством особых проблем.

Тем не менее недавно я решил покинуть компанию. Теперь я планирую найти способ посвятить большую часть своего рабочего времени FastAPI, Typer и другим моим open source проектам. А ещё я, скорее всего, буду консультировать другие команды и компании.

Рики: Ты более известен как разработчик FastAPI, высокопроизводительной веб-платформы для создания API-интерфейсов, которая быстро стала одной из самых популярных в Python-сообществе. Что вдохновило тебя на его создание и как планируешь развивать его дальше? И вопрос от тех, кто ещё не пробовал FastAPI: почему можно смело использовать его в своём следующем проекте вместо других популярных фреймворков?

Себастьян: На самом деле я годами откладывал создание нового фреймворка.

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

У меня также была возможность поработать с другими технологиями с JavaScript и TypeScript на фронте, несколькими фреймворками для гибридных приложений, с Electron для десктопа и так далее. Тем не менее, большинство моих проектов (или даже все) были связаны с данными (data science, ML и так далее).

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

  1. Автозаполнение в редакторе кода.
  2. Автоматическое обнаружение ошибок в редакторе (проверка типов).
  3. Возможность писать простой код.
  4. Автоматическая валидация данных.
  5. Автоматическое преобразование данных (сериализация).
  6. Автоматическая генерация документации для API.
  7. Поддержка стандартов OpenAPI для Web API, OAuth 2.0 для аутентификации и авторизации и JSON Schema для документирования.
  8. Внедрение зависимостей для упрощения кода и повторного использования кода в качестве утилит.
  9. Хорошая производительность / параллелизм.

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

В какой-то момент мой очередной эксперимент по скрещиванию опять провалился. Я как будто перепробовал всё, что можно, и задумался о том, что делать дальше. И тогда я понял: Час Х настал. Я стал изучать стандарты OpenAPI, JSON Schema, OAuth 2.0 и многие другие. Затем я начал реализовывать свои идеи именно так, как это работало у меня в голове. Сначала тестировал на нескольких редакторах, оптимизируя взаимодействие с разработчиками, и только потом закреплял это во внутренней логике моего проекта.

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

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

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

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

А вообще, мигрировать на FastAPI относительно просто, там нет сложных интеграций. Вы можете использовать обычные Python-пакеты непосредственно в сочетании с FastAPI. Мигрировать можно постепенно или создавать с FastAPI только новые компоненты.

Рики: Ещё ты создал Typer, фреймворк, работающий в режиме командной строки (CLI). Он, так же как и FastAPI, во многом опирается на аннотации типов Python. Кажется, я замечаю закономерность [улыбается]. Что такого особенного в этой типизации, которая тебе так нравится? Считаешь ли ты, что всё больше библиотек должны использовать аннотации типов Python?

Себастьян: Да, конечно! Аннотации типов позволяют реализовать автозаполнение и проверку типов в редакторе кода. Аннотации типов, собственно, и были придуманы для этих задач.

Это особенно актуально, если вы создаёте инструмент, который, как ожидается, будут использовать другие разработчики. Я бы хотел, чтобы многие Python-пакеты, например, для инфраструктуры API или SaaS-клиентов, поддерживали аннотации типов. Это значительно улучшило бы жизнь их разработчиков и упростило бы внедрение инструментов.

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

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

Ещё аннотации типов можно использовать для сериализации данных. Например, в URL-адресе всё является строкой, и то же самое справедливо для CLI. Но если в аннотациях типов мы укажем, что нам нужно целое число, инфраструктура (FastAPI или Typer) может попытаться преобразовать, например, строку 42 из URL-адреса или командной строки в целое число.

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

И в подавляющем большинстве случаев для всех этих функций требуется одна и та же информация по типу возраст это целое число. Таким образом, повторно используя один и тот же фрагмент кода (аннотацию типа), мы можем избежать его дублирования. Поддерживая единый источник истины (Single Source of Truth, SSOT), мы убережём себя от ошибок в будущем, если решим изменить тип в каком-то месте (например, для валидации данных), но забудем обновить его в другом месте (например, в документации).

Всё это из коробки могут делать FastAPI и Typer.

Рики: Каковы твои планы на будущее? Над какими ещё проектами будешь работать?

Себастьян: О да, у меня много планов. Может быть, даже слишком много [улыбается].

Есть несколько фич, которые я хочу добавить в FastAPI и Typer. Я также планирую поработать над автоматизацией UI администратора FastAPI, который не будет зависеть от базы данных (буду делать на основе OpenAPI). Ещё хочу интегрировать pydantic с SQLAlchemy для тех случаев, когда нужно общаться с базами данных (опять же, хочу воспользоваться преимуществами аннотаций типов и уменьшить дублирование кода).

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

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

Рики: Теперь, задам тебе, пожалуй, два последних вопроса. Чем ещё занимаешься в свободное время? Чем интересуешься, помимо Python и программирования?

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

Мне очень нравится работать над своими open source проектами, поэтому я легко могу сидеть за этим делом часами даже в выходные дни и не замечать, как проходит время [улыбается].

Рики: Спасибо, что пришёл, Себастьян. Классно пообщались!

Себастьян: Всегда рад.Большое спасибо за приглашение!



VPS серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Перевод Ищем уязвимости в Python-коде с помощью open source инструмента Bandit

17.06.2021 16:13:58 | Автор: admin


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

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

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

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

Наиболее распространённые уязвимости в Python-коде


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

С наиболее распространёнными атаками более-менее справляются современные фреймворки и другие умные инструменты разработки ПО, имеющие встроенную защиту. Но понятно, что не со всеми и далеко не всегда.

Командная инъекция (внедрение команд)


Командная инъекция вид атаки, целью которой является выполнение произвольных команд в ОС сервера. Атака срабатывает, например, при запуске процесса с помощью функций модуля subprocess, когда в качестве аргументов используются значения, хранящиеся в переменных программы.

В этом примере мы используем модуль subprocess, чтобы выполнить nslookup и получить информацию о домене:

# nslookup.pyimport subprocessdomain = input("Enter the Domain: ")output = subprocess.check_output(f"nslookup {domain}, shell=True, encoding='UTF-8')print(output)

Что здесь может пойти не так?

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

$ python3 nslookup.pyEnter the Domain: stackabuse.com ; lsServer:     218.248.112.65Address:    218.248.112.65#53Non-authoritative answer:Name:  stackabuse.comAddress: 172.67.136.166Name:  stackabuse.comAddress: 104.21.62.141Name:  stackabuse.comAddress: 2606:4700:3034::ac43:88a6Name:  stackabuse.comAddress: 2606:4700:3036::6815:3e8dconfig.ymlnslookup.py

Используя эту уязвимость, можно выполнять команды на уровне ОС (у нас ведь shell = true).

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

SQL-инъекция


SQL-инъекция это атака, в ходе которой из пользовательского ввода конструируется SQL-выражение, содержащее вредоносные запросы.

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

Рассмотрим пример:

from django.db import connectiondef find_user(username):with connection.cursor() as cur:cur.execute(f ""select username from USERS where name = '%s'"" % username)output = cur.fetchone()return output

Тут всё просто: в качестве аргумента передадим, например, строку Foobar. Строка вставляется в SQL-запрос, в результате чего получается:

select username from USERS where name = 'Foobar'

Так же, как и в случае с командной инъекцией, если кто-то передаст символ ";, он сможет выполнять несколько команд. Например, добавим к нашему запросу вместо имени пользователя строку "'; DROP TABLE USERS; -- и получим:

select username from USERS where name = ' '; DROP TABLE USERS; --'

Этот запрос удалит всю таблицу USERS. Упс!

Обратите внимание, на двойной дефис в конце запроса. Это комментарий, который нейтрализует следующий за ним символ "'". В результате команда select отработает с аргументом "' '" вместо имени пользователя, а потом выполнится команда DROP, которая больше не является частью строки.

select username from USERS where name = ' ';DROP TABLE USERS;

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

Команда assert


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

def foo(request, user):assert user.is_admin, "user does not have access"

# далее идёт код с ограниченным доступом

По умолчанию __debug__ установлено в True. Однако на продакшне могут сделать ряд оптимизаций, в том числе установить для __debug__ значение False. В этом случае команды assert не сработают и злоумышленник добраться до кода с ограниченным доступом.

Используйте команду assertтолько для отправки сообщений о нюансах реализации другим разработчикам.

Bandit


Bandit это инструмент с открытым исходным кодом, написанный на Python. Он помогает анализировать Python-код и находить в нём наиболее распространённые уязвимости. О некоторых из них я рассказал в предыдущем разделе. Используя менеджер пакетов pip, Bandit можно легко установить локально или на удалённую виртуалку например.

Устанавливается эта штука с помощью простой команды:

$ pip install bandit

Bandit нашёл применение в двух основных сферах:

  1. DevSecOps: как один из процессов Continuous Integration (CI).
  2. Разработка: как часть локального инструментария разработчика, используется для проверки кода на уязвимость до коммита.

Как использовать Bandit


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

Результаты проверки кода на уязвимость можно экспортировать в CSV, JSON и так далее.

Во многих компаниях существуют ограничения и запреты на использование некоторых модулей, потому что с ними связаны определённые, хорошо известные в узких кругах, уязвимости. Bandit может показать, какие модули можно использовать, а какие внесены в чёрный список: конфигурации тестов для соответствующих уязвимостей хранятся в специальном файле. Его можно создать с помощью Генератора конфигураций (bandit-config-generator):

$ bandit-config-generator -o config.yml


Сгенерированный файл config.yml содержит блоки конфигурации для всех тестов и чёрного списка. Эти данные можно удалить или отредактировать. Для указания списка идентификаторов тестов, которые должны быть включены или исключены из процедуры проверки, нужно использовать флаги -t и -s:

  • -t TESTS, --tests TESTS, где TESTS список идентификаторов тестов (в квадратных скобках, через запятую), которые нужно включить.

  • -s SKIPS, --skip SKIPS, где SKIPS список идентификаторов тестов (в квадратных скобках, через запятую), которые нужно исключить.

Проще всего использовать конфигурационный файл с настройками по умолчанию.

$ bandit -r code/ -f csv -o out.csv[main] INFO  profile include tests: None[main] INFO  profile exclude tests: None[main] INFO  cli include tests: None[main] INFO  cli exclude tests: None[main] INFO  running on Python 3.8.5434 [0.. 50.. 100.. 150.. 200.. 250.. 300.. 350.. 400.. ][csv]  INFO  CSV output written to file: out.csv

В команде выше после флага -r указан каталог проекта, после флага -f формат вывода, а после флага -o указан файл, в который нужно записать результаты проверки. Bandit проверяет весь python-код внутри каталога проекта и возвращает результат в формате CSV.

После проверки мы получим достаточно много информации:



Продолжение таблицы

Как упоминалось в предыдущем разделе, импорт модуля subprocess и использование аргумента shell = True в вызове функции subprocess.check_output несут серьёзную угрозу безопасности. Если использование этого модуля и аргумента неизбежно, их можно внести в белый список в файле конфигурации и заставить Banditа пропускать тесты, включив в список SKIPS идентификаторы B602 (subprocess_popen_with_shell_equals_true) и B404 (import_subprocess):

$ bandit-config-generator -s [B602, B404] -o config.yml

Если повторно запустить Bandit, используя новый файл конфигурации, на выходе получим пустой CSV-файл. Это означает, что все тесты были пройдены:

> bandit -c code/config.yml -r code/ -f csv -o out2.csv[main] INFO  profile include tests: None[main] INFO  profile exclude tests: B404,B602[main] INFO  cli include tests: None[main] INFO  cli exclude tests: None[main] INFO  using config: code/config.yml[main] INFO  running on Python 3.8.5434 [0.. 50.. 100.. 150.. 200.. 250.. 300.. 350.. 400.. ][csv]  INFO  CSV output written to file: out2.csv


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

Что важнее для вас?


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



VPS серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Имя им легион. Самые громкие акции Anonymous

19.06.2021 12:05:20 | Автор: admin

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

Давным-давно в далеком-далеком интернете, нравы которого сильно отличались от современных, еще оставались места, где было позволено чуть больше, чем обычно. Анонимность позволяла юзерам имиджборда 4chan, не рискуя ни лицом, ни репутацией, высказывать любые мнения в том числе, сильно отличающиеся от принятых в приличном обществе. Форчан стал не только месторождением множества популярных мемов, но и источником целого ряда массовых акций, которые периодически устраивали участники этого анонимного форума. Массовые набеги на другие интернет-ресурсы, троллинг некоторых одиозных личностей в соцсетях и даже организованные кибератаки то, чем запомнился 4chan в первой половине нулевых. Именно здесь и зародились Anonymous международное движение активистов, или, как их еще называют, хактивистов, практически не имеющее единой централизованной структуры и цементируемое разве что общей идеологией. С которой, впрочем, тоже не все однозначно.

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

Несмотря на то, что сам исторический момент появления Anonymous достоверно проследить сейчас довольно непросто (интернет-археологи относят его к 2003 году), внешний антураж движения хорошо известен всем и каждому. По крайней мере, маска Гая Фокса с прищуренными глазами и тонкими усиками еще один чрезвычайно распространенный мем, известный, пожалуй, абсолютно всем и каждому.



Гай Фокс реальный персонаж, один из участников Порохового заговора в 1605 году. Не самая интересная фигура: знаменит, в первую очередь, тем, что попался и под пытками сдал сообщников. В родной Британии долгие годы считался героем отрицательным, и до сих пор во многих англоязычных странах 5 ноября проводится Ночь костров, во время которой чучело этого деятеля торжественно сжигают, как в России куклу на Масленицу. Маска Гая Фокса в современном виде появилась благодаря графической новелле V значит вендетта, изданной в 1980-е годы в США. Герой комикса, прячущий лицо за маской, выступает против тоталитарного правительства личность его до самого конца остается загадкой. Это должно как бы намекать читателю, что любой человек мог бы оказаться на его месте. Еще одним мощным толчком к популяризации персонажа стала экранизация комикса, выпущенная в 2006 году сёстрами-братьями Вачовски, до этого подарившими миру великолепную Матрицу. Киноэпопея V for Vendetta завоевала не меньшую популярность среди айтишников, чем похождения Нэо и Тринити, а чувак в маске воцарился в умах современников в качестве воплощения и символа анонимности потеснив официальный логотип Anonymous в виде костюма с вопросительным знаком, чем-то напоминающий эмблему ООН.



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

Проект Чанология


Одной из первых акций Anonymous стала кампания против Церкви саентологии в США в 2008 году. И без того скандальная организация, постоянно всплывающая в массовой культуре (даже во второй части Fallout засветились, не говоря уж про такие шедевры мирового искусства, как мультсериал South Park) привлекла к себе внимание активистов требованием убрать из сети интервью с Томом Крузом. Расценив это как форму цензуры, анонимусы обрушили свой гнев на организацию Рона Хаббарда: последовали взломы сайтов, призывы к правительству США лишить саентологов налоговых льгот и уличные демонстрации по всему миру. Отличительной особенностью этих акций, к слову, стали костюмы протестующих здесь-то и пригодилась упомянутая ранее маска Гая Фокса. Церковь саентологии объявила себя жертвой религиозной дискриминации и понесла серьезные репутационные и, возможно, даже финансовые потери.



Арабская весна


В 2010-х в различных государствах арабского мира прокатилась волна протестов, и, сообщество Anonymous не осталось в стороне. Правительство Египта, наверное, очень удивилось, когда в стране ни с того ни с сего когда посыпались ресурсы Министерства информации и правящей Национальной демократической партии. Но еще больше удивилось правительство Сирии, обнаружив на сайте собственного министерства обороны сообщение в поддержку восстания. В Тунисе же анонимусы затроллили целого президента: после беспорядков, в которых не обошлось без участия хактивистов, глава государства Бен Али бежал в Саудовскую Аравию.

Операция Расплата


В том же 2010 году в ответ на очередную попытку прикрыть торрент-трекер The Pirate Bay анонимы обрушили сайты Бюро авторского права США, Американской ассоциации звукозаписывающих компаний и личную страницу Джина Симмонса басиста группы Kiss. Последний отличился тем, что в интервью сравнил пиратов с нацистами, призвал правоохранителей устраивать на них облавы и конфисковывать имущество.



Скандал разгорелся с новой силой, когда в дело вмешалась политика: в 2010 году Джулиану Ассанжу, основателю WikiLeaks, было предъявлено обвинение в изнасиловании. Anonymious усмотрели в этом травлю Ассанжа из-за его деятельности, а значит очередное проявление цензуры в интернете. Карающая длань анонимусов обрушилась на платежные системы, заморозившие пожертвования для сайта WikiLeaks и личные счета его руководителя. В неравном бою пали PayPal, VISA, MasterCard, банк PostFinance, сайты шведского правительства и прокуратуры, личные страницы сенатора Джо Либермана и бывшего губернатора Аляски Сары Пэйлин, пострадал даже адвокат со стороны обвинения.

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

Операция Darknet


В октябре 2011 года Anonymous взломали около 40 ресурсов, распространявщих детскую порнографию и слили в сеть данные пользователей. Да-да, возможно, тех самых людей, которые когда-то сделали популярным мем про Педобира! Увы, сайты с нелегальным контентом быстро починили, а популярность некоторых из них и вовсе возросла в разы благодаря эффекту Барбары Стрейзанд.

Год 2012


2012 год запомнился миру атакой на сайт Европарламента после подписания Польшей Торгового соглашения по борьбе с контрафактом, и перехватом телеконференции ФСБ и Скотланд-Ярда, содержание которой было слито в интернет вместе с личными данными участников.

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

У католической церкви временами тоже возникают сложности в отношениях с ее прихожанами вспомнить хотя бы случай, связанный с обвинением священников в педофилии, из-за которого в 2010 году неизвестные злоумышленники украсили плакаты с изображением Папы Римского Бенедикта XVI педобирами. Anonymous не могли вечно игнорировать Ватикан, хоть и подчеркнули в своем обращении, что акция направлена не против христианской религии и верующих, а исключительно против политики католической церкви, которую анонимусы считают устаревшей. В результате бурной деятельности Anonymous в марте 2012 года официальный сайт города-государства Ватикан с Божьей помощью был выведен из строя. Дважды.

В том же году Anonymous решили заняться проблемами экологии и в знак протеста против нефтяных разработок в Арктике выложили в сеть данные более чем 1000 пользователей сайтов компаний Exxon Mobil, Shell, BP, Газпром и Роснефть. Нефть добывать продолжают, но теперь копорации стараются лучше охранять собственные секреты.

Акция Aaron Swartz


Аарон Шварц американский программист, сооснователь Reddit, писатель и хактивист, посмертно удостоенный премии Зал славы интернета. Отстаивая право на открытую науку и свободный интернет, Аарон нелегально, воспользовавшись локальной сетью Массачусетского технологического института, скачал из базы данных JSTOR (платной цифровой библиотеки академических журналов и научных работ) более 4 миллионов статей и планировал разместить их в свободном доступе.

Прокуратура США, как оказалось, не разделяла взгляды активиста и ему были предъявлены обвинения по тринадцати статьям. Предположительно, Аарон страдал депрессией и, не выдержав морального давления, 11 января 2013 года покончил с собой, находясь под домашним арестом в собственной квартире.



Дело вызвало широкий общественный резонанс, в том числе отреагировали и Anonymous: хакеры взломали сайт Комиссии США по исполнению наказаний, опубликовали на главной странице сообщение, что США перешли черту и угрожали выложить в сеть добытую информацию. Впрочем, закончилось дело фактически ничем реального слива не последовало, расследование забуксовало, а предложения активистов о поправках в законы так и остались предложениями

Операция Израиль


Конфликт Израиля с Палестиной привлек внимание Anonymous в 2013 году после угроз Израиля перекрыть телекоммуникационные связи Сектора Газа с внешним миром. По мнению анонимов, тут-то Израиль перешел все границы и в Твиттере появилось сообщение с угрозой стереть страну из интернета.

7 апреля атаке хакеров подверглись 87 сайтов, включая страницы министерства обороны, министерства образования и крупных израильских банков. Часть из них стала недоступна, а на некоторых были размещены материалы в поддержку ХАМАС и справочник для палестинцев с инструкциями по восстановлению связи на случай отключения телефонии. Пожалуй, это один из ярких примеров идеологии Anonymous: воюйте там себе на здоровье, но интернет не трогайте!

Операция Париж


В 2015 году в Париже прошла серия терактов и нападений, ответственность за которые взяла на себя запрещенная в России террористическая группировка Исламское государство. Всего спустя несколько часов в интернете появился ролик, где аноним в маске Гая Фокса объявляет террористам кибервойну.



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

ИГ весьма уязвимы к атакам в сети. А по словам Anonymous, за время деятельности хактивистов им удалось заблокировать около 150 различных сайтов, связанных с этой организацией, удалить несколько тысяч пропагандистских роликов, а также выложить в открытый доступ список пользователей Twitter, предположительно связанных с террористами или симпатизирующих им.

Современность


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

В 2020 году от Anonymous взломали сайт ООН и разместили на нем страницу Тайваня, который, бедняга, не имеет своего представителя в этой организации вот уже с 1971 года. Дональд Трамп удостоился внимания хакеров на тему связанного с ним сексуального скандала, впрочем, без особого результата. А основатель компании Tesla Илон Маск получил угрозы из-за своего влияния на курс криптовалют на которые традиционно ответил в своем Twitterе в духе кота Леопольда призывом жить дружно.



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

Эпилог


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



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

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

21.06.2021 12:17:59 | Автор: admin

image


Что такое Workbox?


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


Если вы впервые слышите о СВ, то перед изучением данного руководства настоятельно рекомендуется ознакомиться со следующими материалами:



WB предоставляет следующие возможности:


  • предварительное кэширование
  • кэширование во время выполнения
  • стратегии (кэширования)
  • обработка (перехват сетевых) запросов
  • фоновая синхронизация
  • помощь в отладке

Это вторая часть руководства. Вот ссылка на первую часть.


Модули, предоставляемые WB


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


  • workbox-background-sync: фоновая синхронизация, позволяющая выполнять сетевые запросы в режиме офлайн
  • workbox-broadcast-update: отправка уведомлений об обновлении кэша (через Broadcast Channel API)
  • workbox-cacheable-response: фильтрация кэшируемых запросов на основе статус-кодов или заголовков ответов
  • workbox-core: изменение уровня логгирования и названий кэша. Содержит общий код, используемый другими модулями
  • workbox-expiration: установка лимита записей в кэше и времени жизни сохраненных ресурсов
  • workbox-google-analytics: фиксация действий пользователей на странице в режиме офлайн
  • workbox-navigation-preload: предварительная загрузка запросов, связанных с навигацией
  • workbox-precaching: предварительное кэширование ресурсов и управление их обновлением
  • workbox-range-request: поддержка частичных ответов
  • workbox-recipes: общие паттерны использования WB
  • workbox-routing: обработка запросов с помощью встроенных стратегий кэширования или колбэков
  • workbox-strategies: стратегии кэширования во время выполнения, как правило, используемые совместно с workbox-routing
  • workbox-streams: формирование ответа на основе нескольких источников потоковой передачи данных
  • workbox-window: регистрация, управление обновлением и обработка событий жизненного цикла СВ

workbox-background-sync


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


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


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


Базовое использование


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


import { BackgroundSyncPlugin } from 'workbox-background-sync'import { registerRoute } from 'workbox-routing'import { NetworkOnly } from 'workbox-strategies'const bgSyncPlugin = new BackgroundSyncPlugin('myQueueName', {  maxRetentionTime: 24 * 60, // Попытка выполнения повторного запроса будет выполнена в течение 24 часов (в минутах)})registerRoute(  /\/api\/.*\/*.json/,  new NetworkOnly({    plugins: [bgSyncPlugin],  }),  'POST')

Продвинутое использование


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


Создание очереди


import { Queue } from 'workbox-background-sync'const queue = new Queue('myQueueName') // название очереди должно быть уникальным

Название очереди используется как часть названия "тега", который получает register() глобального SyncManager. Оно также используется как название "объектного хранилища" IndexedDB.


Добавление запроса в очередь


import { Queue } from 'workbox-background-sync'const queue = new Queue('myQueueName')self.addEventListener('fetch', (event) => {  // Клонируем запрос для безопасного чтения  // при добавлении в очередь  const promiseChain = fetch(event.request.clone()).catch((err) => {    return queue.pushRequest({ request: event.request })  })  event.waitUntil(promiseChain)})

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


workbox-cacheable-response


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


Рассматриваемый модуль позволяет определять пригодность ответа для кэширования на основе статус-кода или присутствия заголовка с определенным значением.


Кэширование на основе статус-кода


import { registerRoute } from 'workbox-routing'import { CacheFirst } from 'workbox-strategies'import { CacheableResponsePlugin } from 'workbox-cacheable-response'registerRoute(  ({ url }) =>    url.origin === 'https://example.com' && url.pathname.startsWith('/images/'),  new CacheFirst({    cacheName: 'image-cache',    plugins: [      new CacheableResponsePlugin({        statuses: [0, 200]      })    ]  }))

Данная настройка указывает WB кэшировать любые ответы со статусом 0 или 200 при обработке запросов к https://example.com.


Кэширование на основе заголовка


import { registerRoute } from 'workbox-routing'import { StaleWhileRevalidate } from 'workbox-strategies'import { CacheableResponsePlugin } from 'workbox-cacheable-response'registerRoute(  ({ url }) => url.pathname.startsWith('/path/to/api/'),  new StaleWhileRevalidate({    cacheName: 'api-cache',    plugins: [      new CacheableResponsePlugin({        headers: {          'X-Is-Cacheable': 'true'        }      })    ]  }))

При обработке ответов на запросы к URL, начинающемуся с /path/to/api/, проверяется, присутствует ли в ответе заголовок X-Is-Cacheable (который добавляется сервером). Если заголовок присутствует и имеет значение true, такой ответ кэшируется.


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


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


import { registerRoute } from 'workbox-routing'import { StaleWhileRevalidate } from 'workbox-strategies'import { CacheableResponsePlugin } from 'workbox-cacheable-response'registerRoute(  ({ url }) => url.pathname.startsWith('/path/to/api/'),  new StaleWhileRevalidate({    cacheName: 'api-cache',    plugins: [      new CacheableResponsePlugin({        statuses: [200, 404],        headers: {          'X-Is-Cacheable': 'true'        }      })    ]  }))

При использовании встроенной стратегии без явного определения cacheableResponse.CacheableResponsePlugin, для проверки валидности ответа используются следющие критерии:


  • staleWhileRevalidate и networkFirst: ответы со статусом 0 (непрозрачные ответы) и 200 считаются валидными
  • cacheFirst: только ответы со статусом 200 считаются валидными

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


Продвинутое использование


Для определения логики кэширования за пределами стратегии можно использовать класс CacheableResponse:


import { CacheableResponse } from 'workbox-cacheable-response'const cacheable = new CacheableResponse({  statuses: [0, 200],  headers: {    'X-Is-Cacheable': 'true'  }})const response = await fetch('/path/to/api')if (cacheable.isResponseCacheable(response)) {  const cache = await caches.open('api-cache')  cache.put(response.url, response)} else {  // Ответ не может быть кэширован}

workbox-expiration


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


Ограничение количества записей в кэше


import { registerRoute } from 'workbox-routing'import { CacheFirst } from 'workbox-strategies'import { ExpirationPlugin } from 'workbox-expiration'registerRoute(  ({ request }) => request.destination === 'image',  new CacheFirst({    cacheName: 'image-cache',    plugins: [      new ExpirationPlugin({        // ограничиваем количество записей в кэше        maxEntries: 20      })    ]  }))

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


Ограничение времени хранения ресурсов в кэше


import { registerRoute } from 'workbox-routing'import { CacheFirst } from 'workbox-strategies'import { ExpirationPlugin } from 'workbox-expiration'registerRoute(  ({ request }) => request.destination === 'image',  new CacheFirst({    cacheName: 'image-cache',    plugins: [      new ExpirationPlugin({        // ограничиваем время хранения ресурсов в кэше        maxAgeSeconds: 24 * 60 * 60      })    ]  }))

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


Продвинутое использование


Класс CacheExpiration позволяет отделять логику ограничения от других модулей. Для установки ограничений создается экземпляр названного класса:


import { CacheExpiration } from 'workbox-expiration'const cacheName = 'my-cache'const expirationManager = new CacheExpiration(cacheName, {  maxAgeSeconds: 24 * 60 * 60,  maxEntries: 20})

Затем, при обновлении записи в кэше, вызывается метод updateTimestamp() для обновления "возраста" записи.


await openCache.put(request, response)await expirationManager.updateTimestamp(request.url)

Для проверки всех записей в кэше на предмет их соответствия установленным критериям вызывается метод expireEntries():


await expirationManager.expireEntries()

workbox-precaching


СВ позволяет записывать файлы в кэш во время установки. Это называется предварительным кэшированием, поскольку контент кэшируется перед использованием СВ.


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


WB предоставляет простой и понятный API для реализации этого паттерна и эффективной загрузки ресурсов.


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


workbox-precaching делает все это при обработке события install СВ.


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


Новый СВ не будет использоваться для ответов на запросы до его активации. В событии activate workbox-precaching определяет кэшированные ресурсы, отсутствующие в новом списке URL, и удаляет их из кэша.


Обработка предварительно кэшированных ответов


Вызов precacheAndRoute() или addRoute() создает маршрутизатор, который определяет совпадения запросов с предварительно кэшированными URL.


В этом маршрутизаторе используется стратегия "сначала кэш".


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


Список предварительно кэшируемых ресурсов


workbox-precaching ожидает получения массива объектов со свойствами url и revision. Данный массив иногда называют "манифестом предварительного кэширования":


import { precacheAndRoute } from 'workbox-precaching'precacheAndRoute([  { url: '/index.html', revision: '383676' },  { url: '/styles/app.0c9a31.css', revision: null },  { url: '/scripts/app.0d5770.js', revision: null },  // другие записи])

Свойства revision второго и третьего объектов имеют значения null. Это объясняется тем, что версионная информация этих объектов является частью значений их свойств url.


В отличие от JavaScript и CSS URL, указывающие на HTML-файлы, как правило, не включают в себя версионную информацию по той причине, что ссылки на такие файлы должны быть статическими.


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


Обратите внимание: для генерации списка предварительно кэшируемых ресурсов следует использовать один из встроенных инструментов WB: workbox-build, workbox-webpack-plugin или workbox-cli. Создавать такой список вручную очень плохая идея.


Автоматическая обработка входящих запросов


При поиске совпадения входящего запроса с кэшированным ресурсом workbox-precaching автоматически выполняет некоторые манипуляции с URL.


Например, запрос к / оценивается как запрос к index.html.


Игнорирование параметров строки запроса


По умолчанию игнорируются параметры поиска, которые начинаются с utm_ или точно совпадают с fbclid. Это означает, что запрос к /about.html?utm_campaign=abcd оценивается как запрос к /about.html.


Игнорируемые параметры указываются в настройке ignoreURLParametersMatching:


import { precacheAndRoute } from 'workbox-precaching'precacheAndRoute(  [    { url: '/index.html', revision: '383676' },    { url: '/styles/app.0c9a31.css', revision: null },    { url: '/scripts/app.0d5770.js', revision: null }  ],  {    // Игнорируем все параметры    ignoreURLParametersMatching: [/.*/]  })

Основной файл директории


По умолчанию основным файлом директории считается index.html. Именно поэтому запросы к / оцениваются как запросы к /index.html. Это поведение можно изменить с помощью настройки directoryIndex:


import { precacheAndRoute } from 'workbox-precaching'precacheAndRoute(  [    { url: '/index.html', revision: '383676' },    { url: '/styles/app.0c9a31.css', revision: null },    { url: '/scripts/app.0d5770.js', revision: null },  ],  {    directoryIndex: null  })

"Чистые" URL


По умолчанию к запросу добавляется расширение .html. Например, запрос к /about оценивается как /about.html. Это можно изменить с помощью настройки cleanUrls:


import { precacheAndRoute } from 'workbox-precaching'precacheAndRoute([{ url: '/about.html', revision: 'b79cd4' }], {  cleanUrls: false})

Кастомные манипуляции


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


import { precacheAndRoute } from 'workbox-precaching'precacheAndRoute(  [    { url: '/index.html', revision: '383676' },    { url: '/styles/app.0c9a31.css', revision: null },    { url: '/scripts/app.0d5770.js', revision: null }  ],  {    urlManipulation: ({ url }) => {      // Логика определения совпадений      return [alteredUrlOption1, alteredUrlOption2]    }  })

workbox-routing


СВ может перехватывать сетевые запросы. Он может отвечать браузеру кэшированным контентом, контентом, полученным из сети, или контентом, генерируемым СВ.


workbox-routing это модуль, позволяющий "связывать" поступающие запросы с функциями, формирующими на них ответы.


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


Обратите внимание на следующее:


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

Определение совпадений и обработка запросов


В WB "роут" это две функции: функция "определения совпадения" и функция "обработки запроса".


WB предоставляет некоторые утилиты для помощи в реализации названных функций.


Функция определения совпадения принимает ExtendableEvent, Request и объект URL. Возврат истинного значения из этой функции означает совпадение. Например, вот пример определения совпадения с конкретным URL:


const matchCb = ({ url, request, event }) => {  return (url.pathname === '/special/url')}

Функция обработки запроса принимает такие же параметры + аргумент value, который имеет значение, возвращаемое из первой функции:


const handlerCb = async ({ url, request, event, params }) => {  const response = await fetch(request)  const responseBody = await response.text()  return new Response(`${responseBody} <!-- Глядите-ка! Новый контент. -->`, {    headers: response.headers  })}

Обработчик должен возвращать промис, разрешающийся Response.


Регистрация колбэков выглядит следующим образом:


import { registerRoute } from 'workbox-routing'registerRoute(matchCb, handlerCb)

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


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


import { registerRoute } from 'workbox-routing'import { StaleWhileRevalidate } from 'workbox-strategies'registerRoute(  matchCb,  new StaleWhileRevalidate())

Определение совпадений с помощью регулярного выражения


Вместо функции определения совпадения, можно использовать регулярное выражение:


import { registerRoute } from 'workbox-routing'registerRoute(  new RegExp('/styles/.*\\.css'),  handlerCb)

Для запросов из одного источника данная "регулярка" будет регистрировать совпадения для следующих URL:



Однако, в случае с запросами к другим источникам, регулярка должна совпадать с началом URL. Поэтому совпадение для следующих URL обнаружено не будет:



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


new RegExp('https://cdn\\.third-party-site\\.com.*/styles/.*\\.css')

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


Роут для навигации


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


import { createHandlerBoundToURL } from 'workbox-precaching'import { NavigationRoute, registerRoute } from 'workbox-routing'// Предположим, что страница `/app-shell.html` была предварительно кэшированаconst handler = createHandlerBoundToURL('/app-shell.html')const navigationRoute = new NavigationRoute(handler)registerRoute(navigationRoute)

При посещении пользователем вашего сайта, запрос на получение страницы будет считаться навигационным, следовательно, ответом на него будет кэшированная страница /app-shell.html.


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


import { createHandlerBoundToURL } from 'workbox-precaching'import { NavigationRoute, registerRoute } from 'workbox-routing'const handler = createHandlerBoundToURL('/app-shell.html')const navigationRoute = new NavigationRoute(handler, {  allowlist: [    new RegExp('/blog/')  ],  denylist: [    new RegExp('/blog/restricted/')  ]})registerRoute(navigationRoute)

Обратите внимание, что denyList имеет приоритет перед allowList.


Обработчик по умолчанию


import { setDefaultHandler } from 'workbox-routing'setDefaultHandler(({ url, event, params }) => {  // ...})

Обработчик ошибок


import { setCatchHandler } from 'workbox-routing'setCatchHandler(({ url, event, params }) => {  // ...})

Обработка не-GET-запросов


import { registerRoute } from 'workbox-routing'registerRoute(  matchCb,  handlerCb,  // определяем метод  'POST')registerRoute(  new RegExp('/api/.*\\.json'),  handlerCb,  // определяем метод  'POST')

workbox-strategies


Стратегия кэширования это паттерн, определяющий порядок формирования СВ ответа на запрос (после возникновения события fetch).


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


Stale-While-Revalidate


Данная стратегия возвращает ответ из кэша (при наличии ответа в кэше) или из сети (при отсутствии кэшированного ответа). Сетевой запрос используется для обновления кэша. Такой запрос выполняется независимо от возраста кэшированного ответа.


import { registerRoute } from 'workbox-routing'import { StaleWhileRevalidate } from 'workbox-strategies'registerRoute(  ({url}) => url.pathname.startsWith('/images/avatars/'),  new StaleWhileRevalidate())

Cache-Fisrt


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


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


import { registerRoute } from 'workbox-routing'import { CacheFirst } from 'workbox-strategies'registerRoute(  ({ request }) => request.destination === 'style',  new CacheFirst())

Network-First


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


import { registerRoute } from 'workbox-routing'import { NetworkFirst } from 'workbox-strategies'registerRoute(  ({ url }) => url.pathname.startsWith('/social-timeline/'),  new NetworkFirst())

Network-Only


import { registerRoute } from 'workbox-routing'import { NetworkOnly } from 'workbox-strategies'registerRoute(  ({url}) => url.pathname.startsWith('/admin/'),  new NetworkOnly())

Cache-Only


import { registerRoute } from 'workbox-routing'import { CacheOnly } from 'workbox-strategies'registerRoute(  ({ url }) => url.pathname.startsWith('/app/v2/'),  new CacheOnly())

Настройка стратегии


Каждая стратегия позволяет кастомизировать:


  • название кэша
  • лимит записей в кэше и время их "жизни"
  • плагины

Название кэша


import { registerRoute } from 'workbox-routing'import { CacheFirst } from 'workbox-strategies'registerRoute(  ({ request }) => request.destination === 'image',  new CacheFirst({    cacheName: 'image-cache',  }))

Плагины


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


  • workbox-background-sync
  • workbox-broadcast-update
  • workbox-cacheable-response
  • workbox-expiration
  • workbox-range-requests

import { registerRoute } from 'workbox-routing'import { CacheFirst } from 'workbox-strategies'import { ExpirationPlugin } from 'workbox-expiration'registerRoute(  ({ request }) => request.destination === 'image',  new CacheFirst({    cacheName: 'image-cache',    plugins: [      new ExpirationPlugin({        // Хранить ресурсы в течение недели        maxAgeSeconds: 7 * 24 * 60 * 60,        // Хранить до 10 ресурсов        maxEntries: 10      })    ]  }))

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


workbox-recipies


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


Рецепты


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


Резервный контент


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


По умолчанию резервная страница должна иметь название offline.html.


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


Рецепт


import { offlineFallback } from 'workbox-recipes'import { setDefaultHandler } from 'workbox-routing'import { NetworkOnly } from 'workbox-strategies'setDefaultHandler(  new NetworkOnly())offlineFallback()

Паттерн


import { setCatchHandler, setDefaultHandler } from 'workbox-routing'import { NetworkOnly } from 'workbox-strategies'const pageFallback = 'offline.html'const imageFallback = falseconst fontFallback = falsesetDefaultHandler(  new NetworkOnly())self.addEventListener('install', event => {  const files = [pageFallback]  if (imageFallback) {    files.push(imageFallback)  }  if (fontFallback) {    files.push(fontFallback)  }  event.waitUntil(self.caches.open('workbox-offline-fallbacks').then(cache => cache.addAll(files)))})const handler = async (options) => {  const dest = options.request.destination  const cache = await self.caches.open('workbox-offline-fallbacks')  if (dest === 'document') {    return (await cache.match(pageFallback)) || Response.error()  }  if (dest === 'image' && imageFallback !== false) {    return (await cache.match(imageFallback)) || Response.error()  }  if (dest === 'font' && fontFallback !== false) {    return (await cache.match(fontFallback)) || Response.error()  }  return Response.error()}setCatchHandler(handler)

Подготовка кэша


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


Рецепт


import { warmStrategyCache } from 'workbox-recipes'import { CacheFirst } from 'workbox-strategies'// Здесь может испоьзоваться любая стратегияconst strategy = new CacheFirst()const urls = [  '/offline.html']warmStrategyCache({urls, strategy})

Паттерн


import { CacheFirst } from 'workbox-strategies'// Здесь может использоваться любая стратегияconst strategy = new CacheFirst()const urls = [  '/offline.html',]self.addEventListener('install', event => {  // `handleAll` возвращает два промиса, второй промис разрешается после добавления всех элементов в кэш  const done = urls.map(path => strategy.handleAll({    event,    request: new Request(path),  })[1])  event.waitUntil(Promise.all(done))})

Кэширование страницы


Данный рецепт позволяет СВ отвечать на запрос на получение HTML-страницы с помощью стратегии "сначала сеть". При этом, СВ оптимизируется таким образом, что в случае отсутствия подключения к сети, возвращает ответ из кэша менее чем за 4 секунды. По умолчанию запрос к сети выполняется в течение 3 секунд. Настройка warmCache позволяет подготовить ("разогреть") кэш к использованию.


Рецепт


import { pageCache } from 'workbox-recipes'pageCache()

Паттерн


import { registerRoute } from 'workbox-routing'import { NetworkFirst } from 'workbox-strategies'import { CacheableResponsePlugin } from 'workbox-cacheable-response'const cacheName = 'pages'const matchCallback = ({ request }) => request.mode === 'navigate'const networkTimeoutSeconds = 3registerRoute(  matchCallback,  new NetworkFirst({    networkTimeoutSeconds,    cacheName,    plugins: [      new CacheableResponsePlugin({        statuses: [0, 200]      })    ]  }))

Кэширование статических ресурсов


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


Рецепт


import { staticResourceCache } from 'workbox-recipes'staticResourceCache()

Паттерн


import { registerRoute } from 'workbox-routing'import { StaleWhileRevalidate } from 'workbox-strategies'import { CacheableResponsePlugin } from 'workbox-cacheable-response'const cacheName = 'static-resources'const matchCallback = ({ request }) =>  // CSS  request.destination === 'style' ||  // JavaScript  request.destination === 'script' ||  // веб-воркеры  request.destination === 'worker'registerRoute(  matchCallback,  new StaleWhileRevalidate({    cacheName,    plugins: [      new CacheableResponsePlugin({        statuses: [0, 200]      })    ]  }))

Кэширование изображений


Данный рецепт позволяет СВ отвечать на запросы на получение изображений с помощью стратегии "сначала кэш". По умолчанию кэшируется до 60 изображений в течение 30 дней. Поддерживается разогрев кэша.


Рецепт


import { imageCache } from 'workbox-recipes'imageCache()

Паттерн


import { registerRoute } from 'workbox-routing'import { CacheFirst } from 'workbox-strategies'import { CacheableResponsePlugin } from 'workbox-cacheable-response'import { ExpirationPlugin } from 'workbox-expiration'const cacheName = 'images'const matchCallback = ({ request }) => request.destination === 'image'const maxAgeSeconds = 30 * 24 * 60 * 60const maxEntries = 60registerRoute(  matchCallback,  new CacheFirst({    cacheName,    plugins: [      new CacheableResponsePlugin({        statuses: [0, 200]      }),      new ExpirationPlugin({        maxEntries,        maxAgeSeconds      })    ]  }))

Кэширование гугл-шрифтов


Данный рецепт кэширует таблицу стилей для шрифтов с помощью стратегии "считается устаревшим после запроса" и сами шрифты с помощью стратегии "сначала кэш". По умолчанию кэшируется до 30 шрифтов в течение 1 года.


Рецепт


import { googleFontsCache } from 'workbox-recipes'googleFontsCache()

Паттерн


import { registerRoute } from 'workbox-routing'import { StaleWhileRevalidate } from 'workbox-strategies'import { CacheFirst } from 'workbox-strategies'import { CacheableResponsePlugin } from 'workbox-cacheable-response'import { ExpirationPlugin } from 'workbox-expiration'const sheetCacheName = 'google-fonts-stylesheets'const fontCacheName = 'google-fonts-webfonts'const maxAgeSeconds = 60 * 60 * 24 * 365const maxEntries = 30registerRoute(  ({ url }) => url.origin === 'https://fonts.googleapis.com',  new StaleWhileRevalidate({    cacheName: sheetCacheName  }))// Кэшируем до 30 шрифтов с помощью стратегии "сначала кэш" и храним кэш в течение 1 годаregisterRoute(  ({ url }) => url.origin === 'https://fonts.gstatic.com',  new CacheFirst({    cacheName: fontCacheName,    plugins: [      new CacheableResponsePlugin({        statuses: [0, 200],      }),      new ExpirationPlugin({        maxAgeSeconds,        maxEntries      })    ]  }))

Быстрое использование


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


import {  pageCache,  imageCache,  staticResourceCache,  googleFontsCache,  offlineFallback} from 'workbox-recipes'pageCache()googleFontsCache()staticResourceCache()imageCache()offlineFallback()

workbox-window


Данный модуль выполняется в контексте window. Его основными задачами является следующее:


  • упрощение процесса регистрации и обновления СВ в наиболее подходящие для этого моменты жизненного цикла СВ
  • помощь в обнаружении наиболее распространенных ошибок, совершаемых разработчиками при работе с СВ
  • облегчение коммуникации между кодом СВ и кодом, запускаемым в window

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


<script type="module">import { Workbox } from 'https://storage.googleapis.com/workbox-cdn/releases/6.1.5/workbox-window.prod.mjs'if ('serviceWorker' in navigator) {  const wb = new Workbox('/sw.js')  wb.register()}</script>

Использование сборщика модулей


Установка


yarn add workbox-window# илиnpm i workbox-window

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


import { Workbox } from 'workbox-window'if ('serviceWorker' in navigator) {  const wb = new Workbox('/sw.js')  wb.register()}

Примеры


Регистрация СВ и уведомление пользователя о его активации


const wb = new Workbox('/sw.js')wb.addEventListener('activated', (event) => {  // `event.isUpdate` будет иметь значение `true`, если другая версия СВ  // управляет страницей при регистрации данной версии  if (!event.isUpdate) {    console.log('СВ был активирован в первый раз!')    // Если СВ настроен для предварительного кэширования ресурсов,    // эти ресурсы могут быть получены здесь  }})// Региструем СВ после добавления обработчиков событийwb.register()

Уведомление пользователя о том, что СВ был установлен, но ожидает активации


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


const wb = new Workbox('/sw.js')wb.addEventListener('waiting', (event) => {  console.log(    `Новый СВ был установлен, но он не может быть активирован, пока все вкладки браузера не будут закрыты или перезагружены`  )})wb.register()

Уведомление пользователя об обновлении кэша


Модуль workbox-broadcast-update позволяет информировать пользователей об обновлении контента. Для получения этой информации в браузере используется событие message с типом CACHE_UPDATED:


const wb = new Workbox('/sw.js')wb.addEventListener('message', (event) => {  if (event.data.type === 'CACHE_UPDATED') {    const { updatedURL } = event.data.payload    console.log(`Доступна новая версия ${updatedURL}!`)  }})wb.register()

Отправка СВ списка URL для кэширования


В некоторых приложениях имеет смысл кэшировать только те ресурсы, которые используются посещенной пользователем страницей. Модуль workbox-routing принимает список URL и кэширует их на основе правил, определенных в маршрутизаторе.


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


const wb = new Workbox('/sw.js')wb.addEventListener('activated', (event) => {  // Получаем `URL` текущей страницы + все загружаемые страницей ресурсы  const urlsToCache = [    location.href,    ...performance      .getEntriesByType('resource')      .map((r) => r.name)  ]  // Передаем этот список СВ  wb.messageSW({    type: 'CACHE_URLS',    payload: { urlsToCache }  })})wb.register()

Практика


В этом разделе представлено несколько сниппетов, которые можно использовать в приложениях "как есть", а также краткий обзор готовых решений для разработки PWA, предоставляемых такими фреймворками для фронтенда, как React и Vue.


Для обеспечения работы приложения в режиме офлайн требуется не только СВ и его регистрация в основной файле приложения, но и так называемый "манифест".


О том, что такое манифест можно почитать здесь, здесь и здесь.


Как правило, манифест (и СВ) размещаются на верхнем уровне (в корневой директории) проекта. Манифест может иметь расширение .json или .webmanifest (лучше использовать первый вариант).


Манифест


{  "name": "Название приложения",  "short_name": "Краткое название (будет указано под иконкой приложения при его установке)",  "scope": "/", // зона контроля СВ, разные страницы могут обслуживаться разными СВ  "start_url": ".", // начальный URL, как правило, директория, в которой находится index.html, в котором регистрируется СВ  "display": "standalone",  "orientation": "portrait",  "background_color": "#f0f0f0",  "theme_color": "#3c3c3c",  "description": "Описание приложения",  // этих иконок должно быть достаточно для большинства девайсов  "icons": [    {      "src": "./icons/64x64.png",      "sizes": "64x64",      "type": "image/png"    },    {      "src": "./icons/128x128.png",      "sizes": "128x128",      "type": "image/png"    },    {      "src": "./icons/256x256.png",      "sizes": "256x256",      "type": "image/png",      "purpose": "any maskable"    },    {      "src": "./icons/512x512.png",      "sizes": "512x512",      "type": "image/png"    }  ],  "serviceworker": {    "src": "./service-worker.js" // ссылка на файл с кодом СВ  }}

Ручная реализация СВ, использующего стратегию "сначала кэш"


// Название кэша// используется для обновления кэша// в данном случае, для этого достаточно изменить версию кэша - my-cache-v2const CACHE_NAME = 'my-cache-v1'// Критические для работы приложения ресурсыconst ASSETS_TO_CACHE = [  './index.html',  './offline.html',  './style.css',  './script.js']// Предварительное кэширование ресурсов, выполняемое во время установки СВself.addEventListener('install', (e) => {  e.waitUntil(    caches      .open(CACHE_NAME)      .then((cache) => cache.addAll(ASSETS_TO_CACHE))  )  self.skipWaiting()})// Удаление старого кэша во время активации нового СВself.addEventListener('activate', (e) => {  e.waitUntil(    caches      .keys()      .then((keys) =>        Promise.all(          keys.map((key) => {            if (key !== CACHE_NAME) {              return caches.delete(key)            }          })        )      )  )  self.clients.claim()})// Обработка сетевых запросов/*  1. Выполняется поиск совпадения  2. Если в кэше имеется ответ, он возвращается  3. Если ответа в кэше нет, выполняется сетевой запрос  4. Ответ на сетевой запрос кэшируется и возвращается  5. В кэш записываются только ответы на `GET-запросы`  6. При возникновении ошибки возвращается резервная страница*/self.addEventListener('fetch', (e) => {  e.respondWith(    caches      .match(e.request)      .then((response) =>          response || fetch(e.request)            .then((response) =>              caches.open(CACHE_NAME)                .then((cache) => {                  if (e.request.method === 'GET') {                    cache.put(e.request, response.clone())                  }                  return response                })          )      )      .catch(() => caches.match('./offline.html'))  )})

Конфигурация Webpack


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


Предположим, что в нашем проекте имеется 4 директории:


  • public директория со статическими ресурсами, включая index.html, manifest.json и sw-reg.js
  • src директория с кодом приложения
  • build директория для сборки
  • config директория с настройками, включая .env, paths.js и webpack.config.js

В файле public/sw-reg.js содержится код регистрации СВ:


if ('serviceWorker' in navigator) {  window.addEventListener('load', () => {    navigator.serviceWorker      .register('./service-worker.js')      .then((reg) => {        console.log('СВ зарегистрирован: ', reg)      })      .catch((err) => {        console.error('Регистрация СВ провалилась: ', err)      })  })}

В файле config/paths.js осуществляется экспорт путей к директориям с файлами приложения:


const path = require('path')module.exports = {  public: path.resolve(__dirname, '../public'),  src: path.resolve(__dirname, '../src'),  build: path.resolve(__dirname, '../build')}

Допустим, что в качестве фронтенд-фреймворка мы используем React, а также, что в проекте используется TypeScript. Тогда файл webpack.config.js будет выглядеть следующим образом:


const webpack = require('webpack')// импортируем пути к директориям с файлами приложенияconst paths = require('../paths')// плагин для копирования статических ресурсов в директорию сборкиconst CopyWebpackPlugin = require('copy-webpack-plugin')// плагин для обработки `index.html` - вставки ссылок на стили и скрипты, добавления метаданных и т.д.const HtmlWebpackPlugin = require('html-webpack-plugin')// плагин для обеспечения прямого доступа к переменным среды окруженияconst Dotenv = require('dotenv-webpack')// плагин для минификации и удаления неиспользуемого CSSconst MiniCssExtractPlugin = require('mini-css-extract-plugin')// плагин для сжатия изображенийconst ImageminPlugin = require('imagemin-webpack-plugin').default// плагин для добавления блоков кодаconst AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')// Плагин для генерации СВconst { GenerateSW } = require('workbox-webpack-plugin')// настройки Babelconst babelLoader = {  loader: 'babel-loader',  options: {    presets: ['@babel/preset-env', '@babel/preset-react'],    plugins: [      '@babel/plugin-proposal-class-properties',      '@babel/plugin-syntax-dynamic-import',      '@babel/plugin-transform-runtime'    ]  }}module.exports = {  // режим сборки  mode: 'production',  // входная точка  entry: {    index: {      import: `${paths.src}/index.js`,      dependOn: ['react', 'helpers']    },    react: ['react', 'react-dom'],    helpers: ['immer', 'nanoid']  },  // отключаем логгирование  devtool: false,  // результат сборки  output: {    // директория сборки    path: paths.build,    // название файла    filename: 'js/[name].[contenthash].bundle.js',    publicPath: './',    // очистка директории при каждой сборке    clean: true,    crossOriginLoading: 'anonymous',    module: true  },  resolve: {    alias: {      '@': `${paths.src}/components`    },    extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json']  },  experiments: {    topLevelAwait: true,    outputModule: true  },  module: {    rules: [      // JavaScript, React      {        test: /\.m?jsx?$/i,        exclude: /node_modules/,        use: babelLoader      },      // TypeScript      {        test: /.tsx?$/i,        exclude: /node_modules/,        use: [babelLoader, 'ts-loader']      },      // CSS, SASS      {        test: /\.(c|sa|sc)ss$/i,        use: [          'style-loader',          {            loader: 'css-loader',            options: { importLoaders: 1 }          },          'sass-loader'        ]      },      // статические ресурсы - изображения и шрифты      {        test: /\.(jpe?g|png|gif|svg|eot|ttf|woff2?)$/i,        type: 'asset'      },      {        test: /\.(c|sa|sc)ss$/i,        use: [          MiniCssExtractPlugin.loader,          {            loader: 'css-loader',            options: { importLoaders: 1 }          },          'sass-loader'        ]      }    ]  },  plugins: [    new CopyWebpackPlugin({      patterns: [        {          from: `${paths.public}/assets`        }      ]    }),    new HtmlWebpackPlugin({      template: `${paths.public}/index.html`    }),    // это позволяет импортировать реакт только один раз    new webpack.ProvidePlugin({      React: 'react'    }),    new Dotenv({      path: './config/.env'    }),    new MiniCssExtractPlugin({      filename: 'css/[name].[contenthash].css',      chunkFilename: '[id].css'    }),    new ImageminPlugin({      test: /\.(jpe?g|png|gif|svg)$/i    }),    // Добавляем код регистрации СВ в `index.html`    new AddAssetHtmlPlugin({ filepath: `${paths.public}/sw-reg.js` }),    // Генерируем СВ    new GenerateSW({      clientsClaim: true,      skipWaiting: true    })  ],  optimization: {    runtimeChunk: 'single'  },  performance: {    hints: 'warning',    maxEntrypointSize: 512000,    maxAssetSize: 512000  }}

Здесь вы найдете шпаргалку по настройке вебпака. Пример полной конфигурации вебпака для JS/React/TS-проекта можно посмотреть здесь.


React PWA


Для того, чтобы получить готовый шаблон React-приложения с возможностями PWA, достаточно выполнить команду:


yarn create react-app my-app --template pwa# илиnpx create-react-app ...

Или, если речь идет о TypeScript-проекте:


yarn create react-app my-app --template pwa-typescript# илиnpx create-react-app ...

Кроме прочего, в директории src создаются файлы service-worker.ts и serviceWorkerRegister.ts (последний импортируется в index.tsx), а в директории public файл manifest.json.


Затем, перед сборкой проекта с помощью команды yarn build или npm run build, в файл src/index.tsx необходимо внести одно изменение:


// доserviceWorkerRegistration.unregister();// послеserviceWorkerRegistration.register();

Подробнее об этом можно прочитать здесь.


Vue PWA


С Vue дела обстоят еще проще.


Глобально устанавливаем vue-cli:


yarn global add @vue/cli# илиnpm i -g @vue/cli

Затем, при создании шаблона проекта с помощью команды vue create my-app, выбираем Manually select features и Progressive Web App (PWA) Support.


Кроме прочего, в директории src создается файл registerServiceWorker.ts, который импортируется в main.ts. Данный файл содержит ссылку на файл service-worker.js, который, как и manifest.json, автоматически создается при сборке проекта с помощью команды yarn build или npm run build. Разумеется, содержимое обоих файлов можно кастомизировать.

Подробнее..

Перевод Запускаем DOOM на лампочке

14.06.2021 18:13:31 | Автор: admin
image


В DOOM уже поиграли на пианино и на клавиатуре, на тесте на беременность (кстати, это был фейк) и на паяльнике, на самолёте, банкомате, принтере и осциллографе.

Пришло время для лампочек.

imageВнутри лампочки TRDFRI RGB GU10 (IKEA model: LED1923R5) хакеры из Next-Hack нашли модуль Silicon lab's MGM210L RF module с 108кб оперативки и запустили на нем DOOM. Исследователям-хакерам пришлось попотеть над оптимизацией использования оперативки, потому что оригинальный DOOM требует 4мб, но они смогли.

Модуль имеет только 1 МБ внутренней флэш-памяти, поэтому умельцы добавили внешнюю флэш-память SPI для хранения файла WAD, который можно загрузить с помощью YMODEM. Процессор у лампочки 40-MHz Cortex M4.



image

Лампочка в разборе

image

Дисплей: 1.8 TFT 160128 SPI

image

Блок-схема железа

image

Схема токов

image

Блок питания

image

Прототип платы

image

Несущая плата

image

Клавиатура

image

Микроконтроллер, усановленный на несущую плату.

image

Система в сборе.

image

Фичи проекта:

  • Основан на прекрасном Doomhack's GBA Doom Port с некоторыми дополнениями Kippykip.
  • Поддерживает полную версию Doom Shareware WADS.
  • Полный движок Vanilla Render, включая Z-depth lighting. Однако составные текстуры имеют MIP-карту.
  • Реализовано поведение монстров и распространение звука.
  • Поддержка Sound FX.


Ограничения проекта:

  • Музыка.
  • Воспроизведение демо из-за несовместимости.
  • Полный WAD DOOM Не тестировал.
  • Требуется отладка.
  • Мультиплеер не реализован.
  • Оптимизация производительности.
  • Читы не проверяли, наверно с багами.


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

Условия DOOM-challenge:



  • Найдите готовое устройство, не предназначенное для игры в Doom или в другие игры.
  • Выбранное устройство должно иметь микроконтроллер с разумно ограниченной вычислительной мощностью и/или памятью в соответствии с минимальными требованиями DOOM (DOOM работает с приемлемой частотой кадров даже на 486 @ 33MHz [1], оборудованном 4MB RAM). В качестве примера мы должны исключить современные цифровые камеры, которые имеют систему на несколько сотен МГц на кристалле, с несколькими десятками мегабайт оперативной памяти.
  • Мы должны использовать именно тот микроконтроллер, который встроен в выбранное устройство. Замена невозможна. Никакого дополнительного микроконтроллера добавить нельзя. Тем не менее, разгон (например, даже если он ограничен только некоторыми периферийными устройствами или шинами) возможен при условии, что нам не нужны какие-либо методы охлаждения.
  • Для хранения файлов WAD можно добавить дополнительную флеш-память или карту памяти.
  • Можно добавить цветной дисплей, если на выбранном стандартном устройстве его нет. Разрешение должно быть достаточно высоким, чтобы можно было прилично играть в Doom. Например, экран 3216 пикселей слишком мал, но 128x64 может быть достаточно. С другой стороны, слишком большие разрешения, безусловно, потребуют очень мощного микроконтроллера, вопреки правилу 2.
  • Устройство ввода может быть любым, поэтому для этой цели можно добавить дополнительную электронику.
  • При необходимости блок питания можно заменить.
  • Мы хотели, чтобы движок был максимально приближен к исходному (ванильному) условно-бесплатному ПО Doom. Возможность играть в эпизоде 1 на карте 1 условно-бесплатной игры Doom (E1M1) это минимальная цель, даже если мы не скрываем, что мы мечтали иметь возможность сыграть полную условно-бесплатную версию без ограничений на всех картах.
  • К звуку требований нет, но звуковые эффекты были бы действительно плюсом. Если реализовано, то на аудиоподсистему ограничений нет.
  • Мультиплеер не обязателен


Тест на беременность




Принтер Canon Proxima




Осциллограф




Банкомат




DOOM в DOOM




Пианино




Minecraft




Валидатор билетов




iPod Mini




Калькулятор




Apple Watch




MacBook Pro Touch Bar




Kodak DC260 Digital Camera from 1998




На клавиатуре




На билборде




На электронной книге




На самолёте




Porsche 911




Vectrex




Паяльник




PS


Как тебе такое, AlexeyNadezhin?

zhovner, ждём DOOM на FlipperZero.

image
Подробнее..

Перевод Карманная книга по TypeScript. Часть 6. Манипуляции с типами

15.06.2021 16:19:06 | Автор: admin

image


Мы продолжаем серию публикаций адаптированного и дополненного перевода "Карманной книги по TypeScript".

Другие части:



Система типов TS позволяет создавать типы на основе других типов.


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


Дженерики


Создадим функцию identity, которая будет возвращать переданное ей значение:


function identity(arg: number): number { return arg}

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


function identity(arg: any): any { return arg}

Однако, при таком подходе мы не будем знать тип возвращаемого функцией значения.


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


function identity<Type>(arg: Type): Type { return arg}

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


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


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


const output = identity<string>('myStr')   // let output: string

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


Второй способ заключается в делегировании типизации компилятору:


const output = identity('myStr')   // let output: string

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


Работа с переменными типа в дженериках


Что если мы захотим выводить в консоль длину аргумента arg перед его возвращением?


function loggingIdentity<Type>(arg: Type): Type { console.log(arg.length) // Property 'length' does not exist on type 'Type'. // Свойства 'length' не существует в типе 'Type' return arg}

Мы получаем ошибку, поскольку переменные типа указывают на любой (а, значит, все) тип, следовательно, аргумент arg может не иметь свойства length, например, если мы передадим в функцию число.


Изменим сигнатуру функции таким образом, чтобы она работала с массивом Type:


function loggingIdentity<Type>(arg: Type[]): Type[] { console.log(arg.length) return arg}

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


Мы можем сделать тоже самое с помощью такого синтаксиса:


function loggingIdentity<Type>(arg: Array<Type>): Array<Type> { console.log(arg.length) return arg}

Общие типы


Тип общей функции (функции-дженерика) похож на тип обычной функции, в начале которого указывается тип параметра:


function identity<Type>(arg: Type): Type { return arg}const myIdentity: <Type>(arg: Type) => Type = identity

Мы можем использовать другое название для параметра общего типа:


function identity<Type>(arg: Type): Type { return arg}const myIdentity: <Input>(arg: Input) => Input = identity

Мы также можем создавать общие типы в виде сигнатуры вызова типа объектного литерала:


function identity<Type>(arg: Type): Type { return arg}const myIdentity: { <Type>(arg: Type): Type } = identity

Это приводит нас к общему интерфейсу:


interface GenericIdentityFn { <Type>(arg: Type): Type}function identity<Type>(arg: Type): Type { return arg}const myIdentity: GenericIdentityFn = identity

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


interface GenericIdentityFn<Type> { (arg: Type): Type}function identity<Type>(arg: Type): Type { return arg}const myIdentity: GenericIdentityFn<number> = identity

Кроме общих интерфейсов, мы можем создавать общие классы.


Обратите внимание, что общие перечисления (enums) и пространства имен (namespaces) создавать нельзя.


Общие классы


Общий класс имеет такую же форму, что и общий интерфейс:


class GenericNumber<NumType> { zeroValue: NumType add: (x: NumType, y: NumType) => NumType}const myGenericNum = new GenericNumber<number>()myGenericNum.zeroValue = 0myGenericNum.add = (x, y) => x + y

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


const stringNumeric = new GenericNumber<string>()stringNumeric.zeroValue = ''stringNumeric.add = (x, y) => x + yconsole.log(stringNumeric.add(stringNumeric.zeroValue, 'test'))

Класс имеет две стороны с точки зрения типов: статическую сторону и сторону экземпляров. Общие классы являются общими только для экземпляров. Это означает, что статические члены класса не могут использовать тип параметра класса.


Ограничения дженериков


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


function loggingIdentity<Type>(arg: Type): Type { console.log(arg.length) // Property 'length' does not exist on type 'Type'. return arg}

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


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


interface Lengthwise { length: number}function loggingIdentity<Type extends Lengthwise>(arg: Type): Type { console.log(arg.length) // Теперь мы можем быть увереными в существовании свойства `length` return arg}

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


loggingIdentity(3)// Argument of type 'number' is not assignable to parameter of type 'Lengthwise'.// Аргумент типа 'number' не может быть присвоен параметру типа 'Lengthwise'

Мы должны передавать ему значения, отвечающие всем установленным требованиям:


loggingIdentity({ length: 10, value: 3 })

Использование типов параметров в ограничениях дженериков


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


function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) { return obj[key]}const x = { a: 1, b: 2, c: 3, d: 4 }getProperty(x, 'a')getProperty(x, 'm')// Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.

Использование типов класса в дженериках


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


function create<Type>(c: { new (): Type }): Type { return new c()}

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


class BeeKeeper { hasMask: boolean}class ZooKeeper { nametag: string}class Animal { numLegs: number}class Bee extends Animal { keeper: BeeKeeper}class Lion extends Animal { keeper: ZooKeeper}function createInstance<A extends Animal>(c: new () => A): A { return new c()}createInstance(Lion).keeper.nametagcreateInstance(Bee).keeper.hasMask

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


Оператор типа keyof


Оператор keyof "берет" объектный тип и возвращает строковое или числовое литеральное объединение его ключей:


type Point = { x: number, y: number }type P = keyof Point // type P = keyof Point

Если типом сигнатуры индекса (index signature) типа является string или number, keyof возвращает эти типы:


type Arrayish = { [n: number]: unknown }type A = keyof Arrayish // type A = numbertype Mapish = { [k: string]: boolean }type M = keyof Mapish // type M = string | number

Обратите внимание, что типом M является string | number. Это объясняется тем, что ключи объекта в JS всегда преобразуются в строку, поэтому obj[0] это всегда тоже самое, что obj['0'].


Типы keyof являются особенно полезными в сочетании со связанными типами (mapped types), которые мы рассмотрим позже.


Оператор типа typeof


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


console.log(typeof 'Привет, народ!') // string

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


const s = 'привет'const n: typeof s // const n: string

В сочетании с другими операторами типа мы можем использовать typeof для реализации нескольких паттернов. Например, давайте начнем с рассмотрения предопределенного типа ReturnType<T>. Он принимает тип функции и производит тип возвращаемого функцией значения:


type Predicate = (x: unknown) => booleantype K = ReturnType<Predicate> // type K = boolean

Если мы попытаемся использовать название функции в качестве типа параметра ReturnType, то получим ошибку:


function f() { return { x: 10, y: 3 }}type P = ReturnType<f>// 'f' refers to a value, but is being used as a type here. Did you mean 'typeof f'?// 'f' является ссылкой на значение, но используется как тип. Возможно, вы имели ввиду 'typeof f'

Запомните: значения и типы это не одно и тоже. Для ссылки на тип значения f следует использовать typeof:


function f() { return { x: 10, y: 3 }}type P = ReturnType<typeof f> // type P = { x: number, y: number }

Ограничения


TS ограничивает виды выражений, на которых можно использовать typeof.


typeof можно использовать только в отношении идентификаторов (названий переменных) или их свойств. Это помогает избежать написания кода, который не выполняется:


// Должны были использовать ReturnType<typeof msgbox>, но вместо этого написалиconst shouldContinue: typeof msgbox('Вы уверены, что хотите продолжить?')// ',' expected

Типы доступа по индексу (indexed access types)


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


type Person = { age: number, name: string, alive: boolean }type Age = Person['age'] // type Age = number

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


type I1 = Person['age' | 'name'] // type I1 = string | numbertype I2 = Person[keyof Person] // type I2 = string | number | booleantype AliveOrName = 'alive' | 'name'type I3 = Person[AliveOrName] // type I3 = string | boolean

При попытке доступа к несуществующему свойству возникает ошибка:


type I1 = Person['alve']// Property 'alve' does not exist on type 'Person'.

Другой способ индексации заключается в использовании number для получения типов элементов массива. Мы также можем использовать typeof для перехвата типа элемента:


const MyArray = [ { name: 'Alice', age: 15 }, { name: 'Bob', age: 23 }, { name: 'John', age: 38 },]type Person = typeof MyArray[number]type Person = {   name: string   age: number}type Age = typeof MyArray[number]['age']type Age = number// илиtype Age2 = Person['age']type Age2 = number

Обратите внимание, что мы не можем использовать const, чтобы сослаться на переменную:


const key = 'age'type Age = Person[key]/* Type 'any' cannot be used as an index type. 'key' refers to a value, but is being used as a type here. Did you mean 'typeof key'?*//* Тип 'any' не может быть использован в качестве типа индекса. 'key' является ссылкой на значение, но используется как тип. Возможно, вы имели ввиду 'typeof key'*/

Однако, в данном случае мы можем использовать синоним типа (type alias):


type key = 'age'type Age = Person[key]

Условные типы (conditional types)


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


interface Animal { live(): void}interface Dog extends Animal { woof(): void}type Example1 = Dog extends Animal ? number : string // type Example1 = numbertype Example2 = RegExp extends Animal ? number : string // type Example2 = string

Условные типы имеют форму, схожую с условными выражениями в JS (условие ? истинноеВыражение : ложноеВыражение).


SomeType extends OtherType ? TrueType : FalseType

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


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


Рассмотрим такую функцию:


interface IdLabel { id: number /* некоторые поля */}interface NameLabel { name: string /* другие поля */}function createLabel(id: number): IdLabelfunction createLabel(name: string): NameLabelfunction createLabel(nameOrId: string | number): IdLabel | NameLabelfunction createLabel(nameOrId: string | number): IdLabel | NameLabel { throw 'не реализовано'}

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


Обратите внимание на следующее:


  1. Если библиотека будет выполнять такую проверку снова и снова, это будет не очень рациональным.
  2. Нам пришлось создать 3 перегрузки: по одной для каждого случая, когда мы уверены в типе (одну для string и одну для number), и еще одну для общего случая (string или number). Количество перегрузок будет увеличиваться пропорционально добавлению новых типов.

Вместо этого, мы можем реализовать такую же логику с помощью условных типов:


type NameOrId<T extends number | string> = T extends number ? IdLabel : NameLabel

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


function createLabel<T extends number | string>(idOrName: T): NameOrId<T> { throw 'не реализовано'}let a = createLabel('typescript') // let a: NameLabellet b = createLabel(2.8) // let b: IdLabellet c = createLabel(Math.random() ? 'hello' : 42) // let c: NameLabel | IdLabel

Ограничения условных типов


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


Рассмотрим такой пример:


type MessageOf<T> = T['message']// Type '"message"' cannot be used to index type 'T'.// Тип '"message"' не может быть использован для индексации типа 'T'

В данном случае возникает ошибка, поскольку TS не знает о существовании у T свойства message. Мы можем ограничить T, и тогда TS перестанет "жаловаться":


type MessageOf<T extends { message: unknown }> = T['message']interface Email { message: string}interface Dog { bark(): void}type EmailMessageContents = MessageOf<Email> // type EmailMessageContents = string

Но что если мы хотим, чтобы MessageOf принимал любой тип, а его "дефолтным" значением был тип never? Мы можем "вынести" ограничение и использовать условный тип:


type MessageOf<T> = T extends { message: unknown } ? T['message'] : neverinterface Email { message: string}interface Dog { bark(): void}type EmailMessageContents = MessageOf<Email> // type EmailMessageContents = stringtype DogMessageContents = MessageOf<Dog> // type DogMessageContents = never

Находясь внутри истинной ветки, TS будет знать, что T имеет свойство message.


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


type Flatten<T> = T extends any[] ? T[number] : T// Извлекаем тип элементаtype Str = Flatten<string[]> // type Str = string// Сохраняем типtype Num = Flatten<number> // type Num = number

Когда Flatten получает тип массива, он использует доступ по индексу с помощью number для получения типа элемента string[]. В противном случае, он просто возвращает переданный ему тип.


Предположения в условных типах


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


Условные типы предоставляют возможность делать предположения на основе сравниваемых в истинной ветке типов с помощью ключевого слова infer. Например, мы можем сделать вывод относительно типа элемента во Flatten вместо его получения вручную через доступ по индексу:


type Flatten<Type> = Type extends Array<infer Item> ? Item : Type

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


Мы можем создать несколько вспомогательных синонимов типа (type aliases) с помощью infer. Например, в простых случаях мы можем извлекать возвращаемый тип из функции:


type GetReturnType<Type> = Type extends (...args: never[]) => infer Return ? Return : nevertype Num = GetReturnType<() => number> // type Num = numbertype Str = GetReturnType<(x: string) => string> // type Str = stringtype Bools = GetReturnType<(a: boolean, b: boolean) => boolean[]> // type Bools = boolean[]

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


declare function stringOrNum(x: string): numberdeclare function stringOrNum(x: number): stringdeclare function stringOrNum(x: string | number): string | numbertype T1 = ReturnType<typeof stringOrNum> // type T1 = string | number

Распределенные условные типы (distributive conditional types)


Когда условные типы применяются к дженерикам, они становятся распределенными при получении объединения (union). Рассмотрим следующий пример:


type ToArray<Type> = Type extends any ? Type[] : never

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


type ToArray<Type> = Type extends any ? Type[] : nevertype StrArrOrNumArr = ToArray<string | number> // type StrArrOrNumArr = string[] | number[]

Здесь StrOrNumArray распределяется на:


string | number

и применяется к каждому члену объединения:


ToArray<string> | ToArray<number>

что приводит к следующему:


string[] | number[]

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


type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never// 'StrOrNumArr' больше не является объединениемtype StrOrNumArr = ToArrayNonDist<string | number> // type StrOrNumArr = (string | number)[]

Связанные типы (mapped types)


Связанные типы основаны на синтаксисе сигнатуры доступа по индексу, который используется для определения типов свойств, которые не были определены заранее:


type OnlyBoolsAndHorses = { [key: string]: boolean | Horse}const conforms: OnlyBoolsAndHorses = { del: true, rodney: false,}

Связанный тип это общий тип, использующий объединение, созданное с помощью оператора keyof, для перебора ключей одного типа в целях создания другого:


type OptionsFlags<Type> = { [Property in keyof Type]: boolean}

В приведенном примере OptionsFlag получит все свойства типа Type и изменит их значения на boolean.


type FeatureFlags = { darkMode: () => void newUserProfile: () => void}type FeatureOptions = OptionsFlags<FeatureFlags> // type FeatureOptions = { darkMode: boolean, newUserProfile: boolean }

Модификаторы связывания (mapping modifiers)


Существует два модификатора, которые могут применяться в процессе связывания типов: readonly и ?, отвечающие за иммутабельность (неизменность) и опциональность, соответственно.


Эти модификаторы можно добавлять и удалять с помощью префиксов - или +. Если префикс отсутствует, предполагается +.


// Удаляем атрибуты `readonly` из свойств типаtype CreateMutable<Type> = { -readonly [Property in keyof Type]: Type[Property]}type LockedAccount = { readonly id: string readonly name: string}type UnlockedAccount = CreateMutable<LockedAccount> // type UnlockedAccount = { id: string, name: string }

// Удаляем атрибуты `optional` из свойств типаtype Concrete<Type> = { [Property in keyof Type]-?: Type[Property]}type MaybeUser = { id: string name?: string age?: number}type User = Concrete<MaybeUser> // type User = { id: string, name: string, age: number }

Повторное связывание ключей с помощью as


В TS 4.1 и выше, можно использовать оговорку as для повторного связывания ключей в связанном типе:


type MappedTypeWithNewProperties<Type> = { [Properties in keyof Type as NewKeyType]: Type[Properties]}

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


type Getters<Type> = { [Property in keyof Type as `get${Capitalize<string & Property>}`]: () => Type[Property]}interface Person { name: string age: number location: string}type LazyPerson = Getters<Person> // type LazyPerson = { getName: () => string, getAge: () => number, getLocation: () => string }

Ключи можно фильтровать с помощью never в условном типе:


// Удаляем свойство `kind`type RemoveKindField<Type> = {   [Property in keyof Type as Exclude<Property, 'kind'>]: Type[Property]}interface Circle { kind: 'circle' radius: number}type KindlessCircle = RemoveKindField<Circle> // type KindlessCircle = { radius: number }

Связанные типы хорошо работают с другими возможностями по манипуляции типами, например, с условными типами. В следующем примере условный тип возвращает true или false в зависимости от того, содержит ли объект свойство pii с литерально установленным true:


type ExtractPII<Type> = { [Property in keyof Type]: Type[Property] extends { pii: true } ? true : false}type DBFields = { id: { format: 'incrementing' } name: { type: string, pii: true }}type ObjectsNeedingGDPRDeletion = ExtractPII<DBFields> // type ObjectsNeedingGDPRDeletion = { id: false, name: true }

Типы шаблонных литералов (template literal types)


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


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


type World = 'world'type Greeting = `hello ${World}` // type Greeting = 'hello world'

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


type EmailLocaleIDs = 'welcome_email' | 'email_heading'type FooterLocaleIDs = 'footer_title' | 'footer_sendoff'type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`/* type AllLocaleIDs = 'welcome_email_id' | 'email_heading_id' | 'footer_title_id' | 'footer_sendoff_id'*/

Для каждой интерполированной позиции в шаблонном литерале объединения являются множественными:


type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`type Lang = 'en' | 'ja' | 'pt'type LocaleMessageIDs = `${Lang}_${AllLocaleIDs}`/* type LocaleMessageIDs = 'en_welcome_email_id' | 'en_email_heading_id' | 'en_footer_title_id' | 'en_footer_sendoff_id' | 'ja_welcome_email_id' | 'ja_email_heading_id' | 'ja_footer_title_id' | 'ja_footer_sendoff_id' | 'pt_welcome_email_id' | 'pt_email_heading_id' | 'pt_footer_title_id' | 'pt_footer_sendoff_id'*/

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


Строковые объединения в типах


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


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


const person = makeWatchedObject({ firstName: 'John', lastName: 'Smith', age: 30,})person.on('firstNameChanged', (newValue) => { console.log(`Имя было изменено на ${newValue}!`)})

Обратите внимание, что on регистрирует событие firstNameChanged, а не просто firstName.


Шаблонные литералы предоставляют способ обработки такой операции внутри системы типов:


type PropEventSource<Type> = {   on(eventName: `${string & keyof Type}Changed`, callback: (newValue: any) => void): void}// Создаем "наблюдаемый объект" с методом `on`,// позволяющим следить за изменениями значений свойствdeclare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>

При передаче неправильного свойства возникает ошибка:


const person = makeWatchedObject({ firstName: 'John', lastName: 'Smith', age: 26})person.on('firstNameChanged', () => {})person.on('firstName', () => {})// Argument of type '"firstName"' is not assignable to parameter of type '"firstNameChanged" | "lastNameChanged" | "ageChanged"'.// Параметр типа '"firstName"' не может быть присвоен типу...person.on('frstNameChanged', () => {})// Argument of type '"firstNameChanged"' is not assignable to parameter of type '"firstNameChanged" | "lastNameChanged" | "ageChanged"'.

Предположения типов с помощью шаблонных литералов


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


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


type PropEventSource<Type> = { on<Key extends string & keyof Type>   // (eventName: `${Key}Changed`, callback: (newValue: Type[Key]) => void ): void}declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>const person = makeWatchedObject({ firstName: 'Jane', lastName: 'Air', age: 26})person.on('firstNameChanged', newName => {                         // (parameter) newName: string   console.log(`Новое имя - ${newName.toUpperCase()}`)})person.on('ageChanged', newAge => {                   // (parameter) newAge: number   if (newAge < 0) {       console.warn('Предупреждение! Отрицательный возраст')   }})

Здесь мы реализовали on в общем методе.


При вызове пользователя со строкой firstNameChanged, TS попытается предположить правильный тип для Key. Для этого TS будет искать совпадения Key с "контентом", находящимся перед Changed, и дойдет до строки firstName. После этого метод on сможет получить тип firstName из оригинального объекта, чем в данном случае является string. Точно также при вызове с ageChanged, TS обнаружит тип для свойства age, которым является number.


Внутренние типы манипуляций со строками (intrisic string manipulation types)


TS предоставляет несколько типов, которые могут использоваться при работе со строками. Эти типы являются встроенными и находятся в файлах .d.ts, создаваемых TS.


  • Uppercase<StringType> переводит каждый символ строки в верхний регистр

type Greeting = 'Hello, world'type ShoutyGreeting = Uppercase<Greeting> // type ShoutyGreeting = 'HELLO, WORLD'type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`type MainID = ASCIICacheKey<'my_app'> // type MainID = 'ID-MY_APP'

  • Lowercase<StringType> переводит каждый символ в строке в нижний регистр

type Greeting = 'Hello, world'type QuietGreeting = Lowercase<Greeting> // type QuietGreeting = 'hello, world'type ASCIICacheKey<Str extends string> = `id-${Lowercase<Str>}`type MainID = ASCIICacheKey<'MY_APP'> // type MainID = 'id-my_app'

  • Capitilize<StringType> переводит первый символ строки в верхний регистр

type LowercaseGreeting = 'hello, world'type Greeting = Capitalize<LowercaseGreeting> // type Greeting = 'Hello, world'

  • Uncapitilize<StringType> переводит первый символ строки в нижний регистр

type UppercaseGreeting = 'HELLO WORLD'type UncomfortableGreeting = Uncapitalize<UppercaseGreeting> // type UncomfortableGreeting = 'hELLO WORLD'

Вот как эти типы реализованы:


function applyStringMapping(symbol: Symbol, str: string) { switch (intrinsicTypeKinds.get(symbol.escapedName as string)) {   case IntrinsicTypeKind.Uppercase: return str.toUpperCase()   case IntrinsicTypeKind.Lowercase: return str.toLowerCase()   case IntrinsicTypeKind.Capitalize: return str.charAt(0).toUpperCase() + str.slice(1)   case IntrinsicTypeKind.Uncapitalize: return str.charAt(0).toLowerCase() + str.slice(1) } return str}



Облачные серверы от Маклауд быстрые и безопасные.


Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!


Подробнее..

Перевод Карманная книга по TypeScript. Часть 7. Классы

18.06.2021 12:04:53 | Автор: admin

image


Мы продолжаем серию публикаций адаптированного и дополненного перевода "Карманной книги по TypeScript".

Другие части:



Члены класса (class members)


Вот пример самого простого класса пустого:


class Point {}

Такой класс бесполезен, поэтому давайте добавим ему несколько членов.


Поля (fields)


Поле это открытое (публичное) и доступное для записи свойство класса:


class Point {x: numbery: number}const pt = new Point()pt.x = 0pt.y = 0

Аннотация типа является опциональной (необязательной), но неявный тип будет иметь значение any.


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


class Point {x = 0y = 0}const pt = new Point()// Вывод: 0, 0console.log(`${pt.x}, ${pt.y}`)

Как и в случае с const, let и var, инициализатор свойства класса используется для предположения типа этого свойства:


const pt = new Point()pt.x = '0'// Type 'string' is not assignable to type 'number'.// Тип 'string' не может быть присвоен типу 'number'

--strictPropertyInitialization


Настройка strictPropertyInitialization определяет, должны ли поля класса инициализироваться в конструкторе.


class BadGreeter {name: string// Property 'name' has no initializer and is not definitely assigned in the constructor.// Свойство 'name' не имеет инициализатора и ему не присваивается значения в конструкторе}class GoodGreeter {name: stringconstructor() {this.name = 'привет'}}

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


Если вы намерены инициализировать поле вне конструктора, можете использовать оператор утверждения определения присвоения (definite assignment assertion operator, !):


class OKGreeter {// Не инициализируется, но ошибки не возникаетname!: string}

readonly


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


class Greeter {readonly name: string = 'народ'constructor(otherName?: string) {if (otherName !== undefined) {this.name = otherName}}err() {this.name = 'не ok'// Cannot assign to 'name' because it is a read-only property.// Невозможно присвоить значение свойству 'name', поскольку оно является доступным только для чтения}}const g = new Greeter()g.name = 'тоже не ok'// Cannot assign to 'name' because it is a read-only property.

Конструкторы (constructors)


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


class Point {x: numbery: number// Обычная сигнатура с дефолтными значениямиconstructor(x = 0, y = 0) {this.x = xthis.y = y}}class Point {// Перегрузкиconstructor(x: number, y: string)constructor(s: string)constructor(xs: any, y?: any) {// ...}}

Однако, между сигнатурами конструктора класса и функции существует несколько отличий:


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


  • Конструкторы не могут иметь аннотацию возвращаемого типа всегда возвращается тип экземпляра класса



super


Как и в JS, при наличии базового класса в теле конструктора, перед использованием this необходимо вызывать super():


class Base {k = 4}class Derived extends Base {constructor() {// В ES5 выводится неправильное значение, в ES6 выбрасывается исключениеconsole.log(this.k)// 'super' must be called before accessing 'this' in the constructor of a derived class.// Перед доступом к 'this' в конструкторе или производном классе необходимо вызвать 'super'super()}}

В JS легко забыть о необходимости вызова super, в TS почти невозможно.


Методы (methods)


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


class Point {x = 10y = 10scale(n: number): void {this.x *= nthis.y *= n}}

Как видите, TS не добавляет к методам ничего нового.


Обратите внимание, что в теле метода к полям и другим методам по-прежнему следует обращаться через this. Неквалифицированное название (unqualified name) в теле функции всегда будет указывать на лексическое окружение:


let x: number = 0class C {x: string = 'привет'm() {// Здесь мы пытаемся изменить значение переменной `x`, находящейся на первой строке, а не свойство классаx = 'world'// Type 'string' is not assignable to type 'number'.}}

Геттеры/сеттеры


Классы могут иметь акцессоры (вычисляемые свойства, accessors):


class C {_length = 0get length() {return this._length}set length(value) {this._length = value}}

TS имеет несколько специальных правил, касающихся предположения типов в случае с акцессорами:


  • Если set отсутствует, свойство автоматически становится readonly


  • Параметр типа сеттера предполагается на основе типа, возвращаемого геттером


  • Если параметр сеттера имеет аннотацию типа, она должна совпадать с типом, возвращаемым геттером


  • Геттеры и сеттеры должны иметь одинаковую видимость членов (см. ниже)



Если есть геттер, но нет сеттера, свойство автоматически становится readonly.


Сигнатуры индекса (index signatures)


Классы могут определять сигнатуры индекса. Они работают также, как сигнатуры индекса в других объектных типах:


class MyClass {[s: string]: boolean | ((s: string) => boolean)check(s: string) {return this[s] as boolean}}

Обычно, индексированные данные лучше хранить в другом месте.


Классы и наследование


Как и в других объектно-ориентированных языках, классы в JS могут наследовать членов других классов.


implements


implements используется для проверки соответствия класса определенному interface. При несоответствии класса интерфейсу возникает ошибка:


interface Pingable {ping(): void}class Sonar implements Pingable {ping() {console.log('пинг!')}}class Ball implements Pingable {// Class 'Ball' incorrectly implements interface 'Pingable'. Property 'ping' is missing in type 'Ball' but required in type 'Pingable'.// Класс 'Ball' некорректно реализует интерфейс 'Pingable'. Свойство 'ping' отсутствует в типе 'Ball', но является обязательным в типе 'Pingable'pong() {console.log('понг!')}}

Классы могут реализовывать несколько интерейсов одновременно, например, class C implements A, B {}.


Предостережение


Важно понимать, что implements всего лишь проверяет, соответствует ли класс определенному интерфейсу. Он не изменяет тип класса или его методов. Ошибочно полагать, что implements изменяет тип класса это не так!


interface Checkable {check(name: string): boolean}class NameChecker implements Checkable {check(s) {// Parameter 's' implicitly has an 'any' type.// Неявным типом параметра 's' является 'any'// Обратите внимание, что ошибки не возникаетreturn s.toLowercse() === 'ok'// any}}

В приведенном примере мы, возможно, ожидали, что тип s будет определен на основе name: string в check. Это не так implements не меняет того, как проверяется тело класса или предполагаются его типы.


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


interface A {x: numbery?: number}class C implements A {x = 0}const c = new C()c.y = 10// Property 'y' does not exist on type 'C'.// Свойства с названием 'y' не существует в типе 'C'

extends


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


class Animal {move() {console.log('Moving along!')}}class Dog extends Animal {woof(times: number) {for (let i = 0; i < times; i++) {console.log('woof!')}}}const d = new Dog()// Метод базового классаd.move()// Метод производного классаd.woof(3)

Перезапись методов


Производный класс может перезаписывать свойства и методы базового класса. Для доступа к методам базового класса можно использовать синтаксис super. Поскольку классы в JS это всего лишь объекты для поиска (lookup objects), такого понятия как супер-поле не существует.


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


Пример легального способа перезаписи метода:


class Base {greet() {console.log('Привет, народ!')}}class Derived extends Base {greet(name?: string) {if (name === undefined) {super.greet()} else {console.log(`Привет, ${name.toUpperCase()}`)}}}const d = new Derived()d.greet()d.greet('читатель!')

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


// Создаем синоним для производного экземпляра с помощью ссылки на базовый классconst b: Base = d// Все работаетb.greet()

Что если производный класс не будет следовать конракту базового класса?


class Base {greet() {console.log('Привет, народ!')}}class Derived extends Base {// Делаем этот параметр обязательнымgreet(name: string) {// Property 'greet' in type 'Derived' is not assignable to the same property in base type 'Base'. Type '(name: string) => void' is not assignable to type '() => void'.// Свойство 'greet' в типе 'Derived' не может быть присвоено одноименному свойству в базовом типе 'Base'...console.log(`Привет, ${name.toUpperCase()}`)}}

Если мы скомпилируем этот код, несмотря на ошибку, такой сниппет провалится:


const b: Base = new Derived()// Не работает, поскольку `name` имеет значение `undefined`b.greet()

Порядок инициализации


Порядок инициализации классов может быть неожиданным. Рассмотрим пример:


class Base {name = 'базовый'constructor() {console.log('Меня зовут ' + this.name)}}class Derived extends Base {name = 'производный'}// Вывод: 'базовый', а не 'производный'const d = new Derived()

Что здесь происходит?


Порядок инициализации согласно спецификации следующий:


  • Инициализация полей базового класса


  • Запуск конструктора базового класса


  • Инициализация полей производного класса


  • Запуск конструктора производного класса



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


Наследование встроенных типов


В ES2015 конструкторы, неявно возвращающие объекты, заменяют значение this для любого вызова super. Для генерируемого конструктора важно перехватывать потенциальное значение, возвращаемое super, и заменять его значением this.


Поэтому подклассы Error, Array и др. могут работать не так, как ожидается. Это объясняется тем, что Error, Array и др. используют new.target из ES6 для определения цепочки прототипов; определить значение new.target в ES5 невозможно. Другие компиляторы, обычно, имеют такие же ограничения.


Для такого подкласса:


class MsgError extends Error {constructor(m: string) {super(m)}sayHello() {return 'Привет ' + this.message}}

вы можете обнаружить, что:


  • методы объектов, возвращаемых при создании подклассов, могут иметь значение undefined, поэтому вызов sayHello завершится ошибкой


  • instanceof сломается между экземплярами подкласса и их экземплярами, поэтому (new MsgError()) instanceof MsgError возвращает false



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


class MsgError extends Error {constructor(m: string) {super(m)// Явно устанавливаем прототипObject.setPrototypeOf(this, MsgError.prototype)}sayHello() {return 'Привет ' + this.message}}

Тем не менее, любой подкласс MsgError также должен будет вручную устанавливать прототип. В среде выполнения, в которой не поддерживается Object.setPrototypeOf, можно использовать __proto__.


Видимость членов (member visibility)


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


public


По умолчанию видимость членов класса имеет значение public. Публичный член доступен везде:


class Greeter {public greet() {console.log('Привет!')}}const g = new Greeter()g.greet()

Поскольку public является дефолтным значением, специально указывать его не обязательно, но это повышает читаемость и улучшает стиль кода.


protected


Защищенные члены видимы только для подклассов класса, в котором они определены.


class Greeter {public greet() {console.log('Привет, ' + this.getName())}protected getName() {return 'народ!'}}class SpecialGreeter extends Greeter {public howdy() {// Здесь защищенный член доступенconsole.log('Здорово, ' + this.getName())}}const g = new SpecialGreeter()g.greet() // OKg.getName()// Property 'getName' is protected and only accessible within class 'Greeter' and its subclasses.// Свойство 'getName' является защищенным и доступно только в классе 'Greeter' и его подклассах

Раскрытие защищенных членов


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


class Base {protected m = 10}class Derived extends Base {// Модификатор отсутствует, поэтому значением по умолчанию является `public`m = 15}const d = new Derived()console.log(d.m) // OK

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


Доступ к защищенным членам за пределами иерархии классов


Разные языки ООП по-разному подходят к доступу к защищенным членам из базового класса:


class Base {protected x: number = 1}class Derived1 extends Base {protected x: number = 5}class Derived2 extends Base {f1(other: Derived2) {other.x = 10}f2(other: Base) {other.x = 10// Property 'x' is protected and only accessible through an instance of class 'Derived2'. This is an instance of class 'Base'.// Свойство 'x' является защищенным и доступно только через экземпляр класса 'Derived2'. А это  экземпляр класса 'Base'}}

Java, например, считает такой подход легальным, а C# и C++ нет.


TS считает такой подход нелегальным, поскольку доступ к x из Derived2 должен быть легальным только в подклассах Derived2, а Derived1 не является одним из них.


private


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


class Base {private x = 0}const b = new Base()// Снаружи класса доступ получить нельзяconsole.log(b.x)// Property 'x' is private and only accessible within class 'Base'.class Derived extends Base {showX() {// В подклассе доступ получить также нельзяconsole.log(this.x)// Property 'x' is private and only accessible within class 'Base'.}}

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


class Base {private x = 0}class Derived extends Base {// Class 'Derived' incorrectly extends base class 'Base'. Property 'x' is private in type 'Base' but not in type 'Derived'.// Класс 'Derived' неправильно расширяет базовый класс 'Base'. Свойство 'x' является частным в типе 'Base', но не в типе 'Derived'x = 1}

Доступ к защищенным членам между экземплярами


Разные языки ООП также по-разному подходят к предоставлению доступа экземплярам одного класса к защищенным членам друг друга. Такие языки как Java, C#, C++, Swift и PHP разрешают такой доступ, а Ruby нет.


TS разрешает такой доступ:


class A {private x = 10public sameAs(other: A) {// Ошибки не возникаетreturn other.x === this.x}}

Предостережение


Подобно другим аспектам системы типов TS, private и protected оказывают влияние на код только во время проверки типов. Это означает, что конструкции вроде in или простой перебор свойств имеют доступ к частным и защищенным членам:


class MySafe {private secretKey = 12345}// В JS-файле...const s = new MySafe()// Вывод 12345console.log(s.secretKey)

Для реализации настоящих частных членов можно использовать такие механизмы, как замыкания (closures), слабые карты (weak maps) или синтаксис приватных полей класса (private fields, #).


Статические члены (static members)


В классах могут определеяться статические члены. Такие члены не связаны с конкретными экземплярами класса. Они доступны через объект конструктора класса:


class MyClass {static x = 0static printX() {console.log(MyClass.x)}}console.log(MyClass.x)MyClass.printX()

К статическим членам также могут применяться модификаторы public, protected и private:


class MyClass {private static x = 0}console.log(MyClass.x)// Property 'x' is private and only accessible within class 'MyClass'.

Статические члены наследуются:


class Base {static getGreeting() {return 'Привет, народ!'}}class Derived extends Base {myGreeting = Derived.getGreeting()}

Специальные названия статических членов


Изменение прототипа Function считается плохой практикой. Поскольку классы это функции, вызываемые с помощью new, некоторые слова нельзя использовать в качестве названий статических членов. К таким словам относятся, в частности, свойства функций name, length и call:


class S {static name = 'S!'// Static property 'name' conflicts with built-in property 'Function.name' of constructor function 'S'.// Статическое свойство 'name' вступает в конфликт со встроенным свойством 'Function.name' функции-конструктора 'S'}

Почему не существует статических классов?


В некоторых языках, таких как C# или Java существует такая конструкция, как статический класс (static class).


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


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


// Ненужный статический классclass MyStaticClass {static doSomething() {}}// Альтернатива 1function doSomething() {}// Альтернатива 2const MyHelperObject = {dosomething() {},}

Общие классы (generic classes)


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


class Box<Type> {contents: Typeconstructor(value: Type) {this.contents = value}}const b = new Box('Привет!')// const b: Box<string>

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


Параметр типа в статических членах


Следующий код, как ни странно, является НЕлегальным:


class Box<Type> {static defaultValue: Type// Static members cannot reference class type parameters.// Статические члены не могут ссылаться на типы параметров класса}

Запомните, что типы полностью удаляются! Во время выполнения существует только один слот Box.defaultValue. Это означает, что установка Box<string>.defaultValue (если бы это было возможным) изменила бы Box<number>.defaultValue, что не есть хорошо. Поэтому статические члены общих классов не могут ссылаться на параметры типа класса.


Значение this в классах во время выполнения кода


TS не изменяет поведения JS во время выполнения. Обработка this в JS может показаться необычной:


class MyClass {name = 'класс'getName() {return this.name}}const c = new MyClass()const obj = {name: 'объект',getName: c.getName,}// Выводится 'объект', а не 'класс'console.log(obj.getName())

Если кратко, то значение this внутри функции зависит от того, как эта функция вызывается. В приведенном примере, поскольку функция вызывается через ссылку на obj, значением this является obj, а не экземпляр класса.


TS предоставляет некоторые средства для изменения такого поведения.


Стрелочные функции


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


class MyClass {name = 'класс'getName = () => {return this.name}}const c = new MyClass()const g = c.getName// Выводится 'класс'console.log(g())

Это требует некоторых компромиссов:


  • Значение this будет гарантированно правильным во время выполнения, даже в коде, не прошедшем проверки с помощью TS


  • Будет использоваться больше памяти, поскольку для каждого экземпляра класса будет создаваться новая функция


  • В производном классе нельзя будет использовать super.getName, поскольку отсутствует входная точка для получения метода базового класса в цепочке прототипов



Параметры this


При определении метода или функции начальный параметр под названием this имеет особое значение в TS. Данный параметр удаляется во время компиляции:


// TSfunction fn(this: SomeType, x: number) {/* ... */}// JSfunction fn(x) {/* ... */}

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


class MyClass {name = 'класс'getName(this: MyClass) {return this.name}}const c = new MyClass()// OKc.getName()// Ошибкаconst g = c.getNameconsole.log(g())// The 'this' context of type 'void' is not assignable to method's 'this' of type 'MyClass'.// Контекст 'this' типа 'void' не может быть присвоен методу 'this' типа 'MyClass'

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


  • Мы все еще имеем возможность вызывать метод неправильно


  • Выделяется только одна функция для каждого определения класса, а не для каждого экземпляра класса


  • Базовые определения методов могут по-прежнему вызываться через super



Типы this


В классах специальный тип this динамически ссылается на тип текущего класса:


class Box {contents: string = ''set(value: string) {// (method) Box.set(value: string): thisthis.contents = valuereturn this}}

Здесь TS предполагает, что типом this является тип, возвращаемый set, а не Box. Создадим подкласс Box:


class ClearableBox extends Box {clear() {this.contents = ''}}const a = new ClearableBox()const b = a.set('привет')// const b: ClearableBox

Мы также можем использовать this в аннотации типа параметра:


class Box {content: string = ''sameAs(other: this) {return other.content === this.content}}

Это отличается от other: Box если у нас имеется производный класс, его метод sameAs будет принимать только другие экземпляры этого производного класса:


class Box {content: string = ''sameAs(other: this) {return other.content === this.content}}class DerivedBox extends Box {otherContent: string = '?'}const base = new Box()const derived = new DerivedBox()derived.sameAs(base)// Argument of type 'Box' is not assignable to parameter of type 'DerivedBox'. Property 'otherContent' is missing in type 'Box' but required in type 'DerivedBox'.

Основанные на this защитники типа


Мы можем использовать this is Type в качестве возвращаемого типа в методах классов и интерфейсах. В сочетании с сужением типов (например, с помощью инструкции if), тип целевого объекта может быть сведен к более конкретному Type.


class FileSystemObject {isFile(): this is FileRep {return this instanceof FileRep}isDirectory(): this is Directory {return this instanceof Directory}isNetworked(): this is Networked & this {return this.networked}constructor(public path: string, private networked: boolean) {}}class FileRep extends FileSystemObject {constructor(path: string, public content: string) {super(path, false)}}class Directory extends FileSystemObject {children: FileSystemObject[]}interface Networked {host: string}const fso: FileSystemObject = new FileRep('foo/bar.txt', 'foo')if (fso.isFile()) {fso.content// const fso: FileRep} else if (fso.isDirectory()) {fso.children// const fso: Directory} else if (fso.isNetworked()) {fso.host// const fso: Networked & FileSystemObject}

Распространенным случаем использования защитников или предохранителей типа (type guards) на основе this является ленивая валидация определенного поля. В следующем примере мы удаляем undefined из значения, содержащегося в box, когда hasValue проверяется на истинность:


class Box<T> {value?: ThasValue(): this is { value: T } {return this.value !== undefined}}const box = new Box()box.value = 'Gameboy'box.value// (property) Box<unknown>.value?: unknownif (box.hasValue()) {box.value// (property) value: unknown}

Свойства параметров


TS предоставляет специальный синтаксис для преобразования параметров конструктора в свойства класса с аналогичными названиями и значениями. Это называется свойствами параметров (или параметризованными свойствами), такие свойства создаются с помощью добавления модификаторов public, private, protected или readonly к аргументам конструктора. Создаваемые поля получают те же модификаторы:


class Params {constructor(public readonly x: number,protected y: number,private z: number) {// ...}}const a = new Params(1, 2, 3)console.log(a.x)// (property) Params.x: numberconsole.log(a.z)// Property 'z' is private and only accessible within class 'Params'.

Выражения классов (class expressions)


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


const someClass = class<Type> {content: Typeconstructor(value: Type) {this.content = value}}const m = new someClass('Привет, народ!')// const m: someClass<string>

Абстрактные классы и члены


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


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


Абстрактные классы выступают в роли базовых классов для подклассов, которые реализуют абстрактных членов. При отсутствии абстрактных членов класс считается конкретным (concrete).


Рассмотрим пример:


abstract class Base {abstract getName(): stringprintName() {console.log('Привет, ' + this.getName())}}const b = new Base()// Cannot create an instance of an abstract class.// Невозможно создать экземпляр абстрактного класса

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


class Derived extends Base {getName() {return 'народ!'}}const d = new Derived()d.printName()

Обратите внимание: если мы забудем реализовать абстрактных членов, то получим ошибку:


class Derived extends Base {// Non-abstract class 'Derived' does not implement inherited abstract member 'getName' from class 'Base'.// Неабстрактный класс 'Derived' не реализует унаследованный от класса 'Base' абстрактный член 'getName'// Забыли про необходимость реализации абстрактных членов}

Сигнатуры абстрактных конструкций (abstract construct signatures)


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


Рассмотрим пример:


function greet(ctor: typeof Base) {const instance = new ctor()// Cannot create an instance of an abstract class.instance.printName()}

TS сообщает нам о том, что мы пытаемся создать экземпляр абстрактного класса. Тем не менее, имея определение greet, мы вполне можем создать абстрактный класс:


// Плохо!greet(Base)

Вместо этого, мы можем написать функцию, которая принимает нечто с сигнатурой конструктора:


function greet(ctor: new () => Base) {const instance = new ctor()instance.printName()}greet(Derived)greet(Base)/*Argument of type 'typeof Base' is not assignable to parameter of type 'new () => Base'.Cannot assign an abstract constructor type to a non-abstract constructor type.*//*Аргумент типа 'typeof Base' не может быть присвоен параметру типа 'new () => Base'.Невозможно присвоить тип абстрактного конструктора типу неабстрактного конструктора*/

Теперь TS правильно указывает нам на то, какой конструктор может быть вызван Derived может, а Base нет.


Отношения между классами


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


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


class Point1 {x = 0y = 0}class Point2 {x = 0y = 0}// OKconst p: Point1 = new Point2()

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


class Person {name: stringage: number}class Employee {name: stringage: numbersalary: number}// OKconst p: Person = new Employee()

Однако, существует одно исключение.


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


class Empty {}function fn(x: Empty) {// С `x` можно делать что угодно}// OK!fn(window)fn({})fn(fn)



Облачные серверы от Маклауд быстрые и безопасные.


Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!


Подробнее..

Перевод Карманная книга по TypeScript. Часть 8. Модули

21.06.2021 10:15:25 | Автор: admin

image


Мы продолжаем серию публикаций адаптированного и дополненного перевода "Карманной книги по TypeScript".

Другие части:



Определение модуля


В TS, как и в ECMAScript2015, любой файл, содержащий import или export верхнего уровня (глобальный), считается модулем.


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


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


Не модули


Для начала, давайте разберемся, что TS считает модулем. Спецификация JS определяет, что любой файл без export или await верхнего уровня является скриптом, а не модулем.


Переменные и типы, объявленные в скрипте, являются глобальными (имеют глобальную область видимости), для объединения нескольких файлов на входе в один на выходе следует использовать либо настроку компилятора outFile, либо несколько элементов script в разметке (указанных в правильном порядке).


Если у нас имеется файл, который не содержит import или export, но мы хотим, чтобы этот файл считался модулем, просто добавляем в него такую строку:


export {}

Модули в TS


Существует 3 вещи, на которые следует обращать внимание при работе с модулями в TS:


  • Синтаксис: какой синтаксис я хочу использовать для импорта и экспорта сущностей?
  • Разрешение модулей: каковы отношения между названиями модулей (или их путями) и файлами на диске?
  • Результат: на что должен быть похож код модуля?

Синтаксис


Основной экспорт в файле определяется с помощью export default:


// @filename: hello.tsexport default function helloWorld() {  console.log('Привет, народ!')}

Затем данная функция импортируется следующим образом:


import hello from './hello.js'hello()

В дополнению к экспорту по умолчанию, из файла может экспортироваться несколько переменных и функций с помощью export (без default):


// @filename: maths.tsexport var pi = 3.14export let squareTwo = 1.41export const phi = 1.61export class RandomNumberGenerator {}export function absolute(num: number) {  if (num < 0) return num * -1  return num}

Указанные сущности импортируются так:


import { pi, phi, absolute } from './maths.js'console.log(pi)const absPhi = absolute(phi)  // const absPhi: number

Дополнительный синтаксис импорта


Название импортируемой сущности можно менять с помощью import { old as new }:


import { pi as  } from './maths.js'console.log()        /*          (alias) var : number          import         */

Разные способы импорта можно смешивать:


// @filename: maths.tsexport const pi = 3.14export default class RandomNumberGenerator {}// @filename: app.tsimport RNGen, { pi as  } from './maths.js'RNGen/*  (alias) class RNGen  import RNGen*/console.log()/*  (alias) const : 3.14  import */

Все экспортированные объекты при импорте можно поместить в одно пространство имен с помощью * as name:


// @filename: app.tsimport * as math from './maths.js'console.log(math.pi)const positivePhi = math.absolute(math.phi)  // const positivePhi: number

Файлы можно импортировать без указания переменных:


// @filename: app.tsimport './maths.js'console.log('3.14')

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


Специфичный для TS синтаксис модулей


Типы могут экспортироваться и импортироваться с помощью такого же синтаксиса, что и значения в JS:


// @filename: animal.tsexport type Cat = { breed: string, yearOfBirth: number }export interface Dog {  breeds: string[]  yearOfBirth: number}// @filename: app.tsimport { Cat, Dog } from './animal.js'type Animals = Cat | Dog

TS расширяет синтаксис import с помощью import type, что позволяет импортировать только типы.


// @filename: animal.tsexport type Cat = { breed: string, yearOfBirth: number }// 'createCatName' cannot be used as a value because it was imported using 'import type'.// 'createCatName' не может использоваться в качестве значения, поскольку импортируется с помощью 'import type'export type Dog = { breeds: string[], yearOfBirth: number }export const createCatName = () => 'fluffy'// @filename: valid.tsimport type { Cat, Dog } from './animal.js'export type Animals = Cat | Dog// @filename: app.tsimport type { createCatName } from './animal.js'const name = createCatName()

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


Синтаксис ES-модулей с поведением CommonJS


Синтаксис ES-модулей в TS напрямую согласуется с CommonJS и require из AMD. Импорт с помощью ES-модулей в большинстве случаев представляет собой тоже самое, что require в указанных окружениях, он позволяет обеспечить полное совпадение TS-файла с результатом CommonJS:


import fs = require('fs')const code = fs.readFileSync('hello.ts', 'utf8')

Синтаксис CommonJS


CommonJS это формат, используемый большинством npm-пакетов. Даже если вы используете только синтаксис ES-модулей, понимание того, как работает CommonJS, поможет вам в отладке приложений.


Экспорт


Идентификаторы экпортируются посредством установки свойства exports глобальной переменной module:


function absolute(num: number) {  if (num < 0) return num * -1  return num}module.exports = {  pi: 3.14,  squareTwo: 1.41,  phi: 1.61,  absolute}

Затем эти файлы импортируются с помощью инструкции require:


const maths = require('maths')maths.pi  // any

В данном случае импорт можно упростить с помощью деструктуризации:


const { squareTwo } = require('maths')squareTwo  // const squareTwo: any

Взаимодействие CommonJS с ES-модулями


Между CommonJS и ES-модулями имеется несовпадение, поскольку ES-модули поддерживают "дефолтный" экспорт только объектов, но не функций. Для преодоления данного несовпадения в TS используется флаг компиляции esModuleInterop.


Настройки, связанные с разрешением модулей


Разрешение модулей это процесс определения файла, указанного в качестве ссылки в строке из инструкции import или require.


TS предоставляет две стратегии разрешения модулей: классическую и Node. Классическая стратегия является стратегией по умолчанию (когда флаг module имеет значение, отличное от commonjs) и включается для обеспечения обратной совместимости. Стратегия Node имитирует работу Node.js в режиме CommonJS с дополнительными проверками для .ts и .d.ts.


Существует большое количество флагов, связанных с разрешением модулей: moduleResolution, baseUrl, paths, rootDirs и др.


Настройки для результатов разрешения модулей


Имеется две настройки, которые влияют на результирующий JS-код:


  • target определяет версию JS, в которую компилируется TS-код
  • module определяет, какой код используется для взаимодействия модулей между собой

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


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


Ниже приведено несколько примеров использования синтаксиса ES-модулей с разными настройками module:


import { valueOfPi } from './constants.js'export const twoPi = valueOfPi * 2

ES2020


import { valueOfPi } from './constants.js'export const twoPi = valueOfPi * 2

CommonJS


"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.twoPi = void 0;const constants_js_1 = require("./constants.js");exports.twoPi = constants_js_1.valueOfPi * 2;

UMD


(function (factory) {  if (typeof module === "object" && typeof module.exports === "object") {    var v = factory(require, exports);    if (v !== undefined) module.exports = v;  }  else if (typeof define === "function" && define.amd) {    define(["require", "exports", "./constants.js"], factory);  }})(function (require, exports) {  "use strict";  Object.defineProperty(exports, "__esModule", { value: true });  exports.twoPi = void 0;  const constants_js_1 = require("./constants.js");  exports.twoPi = constants_js_1.valueOfPi * 2;});

Пространства имен (namespaces)


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




VPS серверы от Маклауд быстрые и безопасные.


Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!


Подробнее..

Перевод Десятикратное улучшение производительности React-приложения

14.06.2021 16:14:45 | Автор: admin

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


Около года назад в Techgoise я получил возможность поработать с большим React-приложением. Мы получили (унаследовали) готовую кодовую базу, внесли основные правки и начали добавлять в приложение новые интересные возможности.


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


В данной статье я расскажу о том, как нам удалось добиться уменьшения этой цифры с 1,5 Гб до 150 Мб, что, как следствие, привело к улучшению производительности почти в 10 раз, и мы больше никогда не сталкивались с Ошибкой.


Поиск узких мест в производительности


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


1. Профилирование компонентов с помощью расширения для Google Chrome


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


2. Снимки используемой памяти в Firefox


Данный инструмент предоставляет снимок "кучи", которая используется текущей вкладкой браузера. Древовидное представление снимка показывает следующие вещи:


  1. Объекты: объекты JavaScript и DOM, такие как функции, массивы или, собственно, объекты, а также типы DOM, такие как Window и HTMLDivElement.
  2. Скрипты: источники JavaScript-кода, загружаемые страницей.
  3. Строки.
  4. Другое: внутренние объекты, используемые SpiderMonkey.

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


3. Пакет why-did-you-render


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


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


Как же нам удалось решить эту задачу?


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


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


1. Удаление встроенных функций


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


import Child from 'components/Child'const Parent = () => ( <Child onClick={() => {   console.log('Случился клик!') }} />)export default Parent

В нашем коде имеется встроенная функция. С такими функциями сопряжено 2 главных проблемы:


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

В основном, это связано с тем, что в данном случае метод "передается по ссылке", поэтому на каждом цикле рендеринга создается новая функция и изменяется ссылка на нее. Это присходит даже при использовании PureComponent или React.memo().


Решение: выносим встроенные функции из рендеринга компонента.


import Child from 'components/Child'const Parent = () => { const handleClick = () => {   console.log('Случился клик!') } return (   <Child onClick={handleClick} /> )}

Это позволило снизить расход памяти с 1,5 Гб до 800 Мб.


2. Сохранение состояния при отсутствии изменений хранилища Redux


Как правило, для хранения состояния мы используем хранилище Redux. Предположим, что мы повторно обращаемся к API и получаем те же данные. Должны ли мы в этом случае обновлять хранилище? Короткий ответ нет. Если мы это сделаем, то компоненты, использующие такие данные будут повторно отрисованы, поскольку изменилась ссылка на данные.


В унаследованной кодовой базе для этого использовался такой хак: JSON.stringify(prevProps.data) !== JSON.stringify(this.props.data). Однако, на нескольких страницах он не давал желаемого эффекта.


Решение: перед обновлением состояния в хранилище Redux проводим эффективное сравнение данных. Мы обнаружили два хороших пакета, отлично подходящих для решения этой задачи: deep-equal и fast-deep-equal.


Это привело с уменьшению Цифры с 800 до 500 Мб.


3. Условный рендеринг компонентов


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


import { useState } from 'react'import { Modal, Button } from 'someCSSFramework'const Modal = ({ isOpen, title, body, onClose }) => { const [open, setOpen] = useState(isOpen || false) const handleClick =   typeof onClose === 'function'     ? onClose     : () => { setOpen(false) } return (   <Modal show={open}>     <Button onClick={handleClick}>x<Button>     <Modal.Header>{title}</Modal.Header>     <Modal.Body>{body}</Modal.Body>   </Modal> )}

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


Решение: рендеринг таких компонентов по условию (условный рендеринг). Также можно рассмотреть вариант с "ленивой" (отложенной) загрузкой кода таких компонентов.


Это привело к снижению расхода памяти с 500 до 150 Мб.


Перепишем приведеный выше пример:


import { useState } from 'react'import { Modal, Button } from 'someCSSFramework'const Modal = ({ isOpen, title, body, onClose }) => { const [open, setOpen] = useState(isOpen || false) const handleClick =   typeof onClose === 'function'     ? onClose     : () => { setOpen(false) } // условный рендеринг if (!open) return null return (   <Modal show={open}>     <Button onClick={handleClick}>x<Button>     <Modal.Header>{title}</Modal.Header>     <Modal.Body>{body}</Modal.Body>   </Modal> )}

4. Удаление ненужных await и использование Promise.all()


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


Обычно, для получения начальных данных мы обращаемся к API. Представьте, что для инициализации приложение требуется получить данные от 3-5 API, как в приведенном ниже примере. Методы get... в примере связанны с соответствующими запросами к API:


const userDetails = await getUserDetails()const userSubscription = await getUserSubscription()const userNotifications = await getUserNotifications()

Решение: для одновременного выполнения запросов к API следует использовать Promise.all(). Обратите внимание: это будет работать только в том случае, когда ваши данные не зависят друг от друга и порядок их получения не имеет значения.


В нашем случае это увеличило скорость начальной загрузки приложения на 30%.


const [ userSubscription, userDetails, userNotifications] = await Promise.all([ getUserSubscription(), getUserDetails(), getUserNotifications()])

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


Заключение


Итак, для повышения производительности React-приложения необходимо придерживаться следующих правил:


  1. Избегайте использования встроенных функций. В небольших приложениях это не имеет особого значения, но по мере роста приложения это негативно отразится на скорости работы приложения.
  2. Помните о том, что иммутабельность (неизменяемость) данных это ключ к предотвращению ненужного рендеринга.
  3. В отношении скрытых компонентов вроде модальных окон и раскрывающихся списков следует применять условный или отложенный рендеринг. Такие компоненты не используются до определенного момента, но их рендеринг влияет на производительность.
  4. По-возможности, отправляйте параллельные запросы к API. Их последовательное выполнение занимает гораздо больше времени.

Спустя 3 недели разработки (включая тестирование), мы, наконец, развернули продакшн-версию приложения. С тех пор мы ни разу не сталкивались в ошибкой "Aw! Snap".


Благодарю за внимание и хорошего дня!




Облачные серверы от Маклауд быстрые и безопасные.


Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!


Подробнее..

Такая разная колонизация Африки

15.06.2021 10:13:02 | Автор: admin


Африка. Континент контрастов, с которыми многие из нас сталкиваются ещё в детстве. С одной стороны Николай Дроздов, Иван Затевахин и канал Animal Planet рассказывают нам про всяких интересных зверушек, а Discovery показывает прекрасные виды на всякие вулканы Килиманджаро и водопады Виктория. С другой стороны Не ходите в Африку гулять вперемешку с ужасами от мам и бабушек, мотивирующих поедание невкусной каши упоминанием голодающих африканских детей. И чем старше мы становимся, тем больше на первый план выходят не реки вот такой ширины и горы вот такой вышины, а разного рода Бармалеи. Причём чаще всего представляющие собой лютую смесь из обрывков новостных репортажей, остатков слабой тройки по географии в школе и каких-то диких баек из жёлтых газет и того же цвета сайтов. В итоге, весь континент воспринимается как сплошное месиво из гражданских войн, голодающих детей, военных переворотов, эпидемий и каннибализма. Очень похоже, кстати, на распространённое восприятие средних веков. Просто одно было давно, а другое сейчас, но далеко. Не сказать, чтобы всех перечисленных прелестей был особый дефицит, но всё-таки оно творится не везде и не всегда, а местами и время от времени. Причём где-то чаще, а где-то практически никогда. Вот о том, почему так происходит, речь и пойдёт.



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

Начнём, конечно же, с Англии. Для начала, одна важная особенность все приличия должны быть соблюдены. Поэтому все важные моменты всегда были аккуратно юридически оформлены. Захват территории договор с местными правителями о купле-продажи или передаче земли. Война обязательно с мирным договором в конце, закрепляющим территориальные приобретения. И всё в этом духе. И никто не может сказать, что британский колониализм был незаконным. Всё было полностью в соответствии с законами. Законами Великобритании, само собой. Вторая особенность английской колониальной модели это сохранение местных элит. Английская колониальная администрация не заменяла собой существующую систему, а вставала над ней. Создавалась связующая административная прослойка, причём в массе своей из представителей местных народов. Чаще всего, не из самых многочисленных. Местные правители сохраняли статус, что чётко прописывалось в тех самых безупречно составленных договорах. Да, в Африке это обычно не доходило до индийских тонкостей, где каждому правителю было прописано чётко обозначенное количество залпов приветственного салюта, но тоже права и обязанности сторон обозначались. Проще говоря местным делегировалось большое количество серьёзных управленческих функций. Без этого функционирование британской колониальной империи было бы невозможно просто в силу относительно малочисленного населения метрополии.


Обучение вождению королевских африканских стрелков. Британские владения в Восточной Африке, 1943 год.

Важнейшим плюсом этой системы было то, что для своего функционирования она требовала серьёзной инфраструктуры. Для местных администраций нужны были образованные люди, причём по английским стандартам. Значит, нужно строить школы, и немало. Образованное население стоит дорого, его нужно лечить значит, нужны больницы. И так далее. Подтягивались транспорт, культура, производство, торговля. Понятное дело, что это касалось не всего населения, но прослойка образованных людей получалась немалая. С учётом местных администраторов англоязычные государства вполне успешно могли функционировать сразу после обретения независимости. Которое, конечно же, тоже должно было происходить прилично. Непременным атрибутом ухода англичан из колоний были выборы, проводимые перед этим. Да, часто это была сплошная фикция, потому что голосовать могла небольшая часть населения или кандидаты могли быть слабо известны народу, но тем не менее ритуал соблюдался. Бывшие колонизаторы всегда могли предъявить, что они не просто хлопнули дверью, а передали власть избранному правительству. В принципе, традиция сохраняется. Какие-никакие, а выборы в англоязычных африканских странах обычно проходят. Пусть даже и с заранее известным результатом.
Главной проблемой английских колоний оказались их размеры. Огромные территории, административное деление которых происходило без учёта местных особенностей, чисто исходя из политических и административных интересов колонизаторов. Под сенью британской короны оно особой проблемы не составляло, а вот после получения независимости вместе зачастую оказались народы, плохо друг друга переносящие. Сразу вылезли все старые обиды, помноженные на новые экономические интересы. Хрестоматийный пример Нигерия с её сепаратистами Биафры. Нефть находится на территории одних народов, власть принадлежит другим, деньги, естественно, делятся как выгодно центру. И это ещё не учитывая религиозный фактор многостороннего конфликта христиан, мусульман и анимистов.

Франция пошла другим путём. В отличии от англичан, у французов колоний было поменьше, самих их побольше, а большинство владений было более или менее сгруппировано в одной части континента. Простой встройке местной знати в свою систему управления французы предпочли полноценное формирование компрадорских элит. Немало африканцев, выделявшихся способностями или происхождением, отправлялись учиться во Францию. Там они попадали под мощный пресс французской культурной среды. Общались со студентами на учёбе, днём пили кофе на летних верандах, а вечером ром и абсент в кафешантанах, ходили в театры и оперу, открывали для себя весёлый мир парижских варьете. И устоять перед этим влиянием было очень и очень тяжело. Так, поколение за поколением во французских колониях сформировалась прослойка влиятельных людей, для которых худшей перспективой представлялась реальная независимость от Франции. Потому что она отлучала бы их от культуры, по сути ставшей им родной. При этом французы не стеснялись назначать местных на весьма серьёзные административные должности, особенно после Первой Мировой, когда у них возник дефицит образованного мужского населения, нужного и в метрополии. Даже среди депутатов от колоний в Национальное собрание не были редкостью чернокожие депутаты. Соответственно, администрации колоний обычно носили смешанный характер, в них были как французы, так и представители местных народов. Как и у англичан, это требовало серьёзного развития инфраструктуры в колониях. В первую очередь образования для формирования тех самых компрадорских элит нужна была образованная прослойка населения.


Генерал де Голль встречается с генерал-губернатором Французской Экваториальной Африки Феликсом Эбуэ

А вот деколонизация тут пошла другим путём. Несмотря на получения статуса независимых государств, большая часть французских колоний никакой независимости по сути не получила. Их элиты ровно так же воспитываются во французских университетах. Большая часть ценных активов принадлежит французским корпорациям. На их территории находятся французские военные базы, размещённые обычно в соответствии с межгосударственными договорами о гарантиях безопасности и им подобных. Значительная часть бывших французский колоний использует единую валюту, входит в таможенные и почтовые союзы. И, конечно же, бывшие колонии входят в Франкофонию организацию франкоязычных стран, служащую проводников французского культурного, политического и экономического влияния. Как результат бывшие колонии находятся в серьёзной зависимости от бывшей метрополии. Минусом этого является сдерживание темпов роста экономик этих стран в тех моментах, когда оно не выгодно Франции. Плюс относительная стабильность. Франция следит за своими подопечными, не допускает военных переворотов (если они, конечно, не были согласованы в Париже) и гражданских войн, поскольку они мешают интересам бизнеса. При этом не стесняется регулярно использовать и вооружённые силы для поддержания своего влияния.

Совсем иная ситуация складывалась в колониях Португалии. Тут следует начать с того, что португальцы гораздо раньше начали серьёзную колонизацию, ещё с XVI века. В то время, как у большинства стран были только торговые фактории на побережье, у Португалии уже были обширные владения на территории современных Анголы и Мозамбика. Местную знать окрестили, выдали ей христианские имена и португальские титулы, ну а со временем она просто растворилась в европейской среде. В привлечении местных к управлению тоже нужды особой не было португальские колонии имели многочисленное белое население. Вплоть до такого уникального для Тропической Африки явления, как белые трущобы в крупных городах. Соответственно, процент образованного чёрного населения был мизерный и более или менее начал расти только в 50-х годах ХХ века на общей волне смягчения порядков на континенте. Второй важный момент в отличии от мирных вариантов Англии и Франции (Алжирскую войну не учитываю, там были особые обстоятельства), получение независимости португальскими колониями вылилось в долгую и ожесточённую Заморскую войну, в ходе которой сложилась своеобразная ситуация. С одной стороны, португальцы всё-таки сформировали определённый слой вестернизированной элиты из администраторов и командиров чёрных подразделений, с другой Советский Союз успел обучить и подготовить значительное количество разного рода специалистов для этих стран. Плюс свою лепту внёс Китай. В итоге, после обретения независимости в бывших португальских колониях имелась прослойка населения, изначально недовольная сложившейся ситуацией и имеющая желание её изменить. Чем не преминули воспользоваться ЮАР и, в меньшей степени, Южная Родезия. Им-то крупные и сильные государства с ориентацией на СССР совсем не нужны были под боком. Немного поддержки повстанческих группировок, плюс добавить рейды своих войск и Ангола с Мозамбиком мгновенно сваливаются в череду затяжных гражданских войн. Они то затухают, то вспыхивают с новой силой, но в целом обе страны сложно назвать стабильными.


Блогер Илья Варламов был сильно впечатлён ангольскими контрастами

Ну и на сладкое одни из главных кондитеров Европы, славные вафлями и шоколадом. Конечно, это Бельгия. Наверняка большинство наслышано о жутких событиях, творившихся в личном владении бельгийского короля Леопольда II Конго. Масштабы творимого беспредела вышли за все рамки приличий даже по меркам вполне терпимого к таким вещам европейского общества начала XX века. В общем, разразился скандал, Конго у Леопольда номинально изъяли, и владение перешло под управление непосредственно Бельгии как государства. Правда, ничего особо не поменялось. Колониальная администрация была абсолютно примитивная. По сути, это была сеть торговых факторий, выполнявших логистические функции. На них свозилась продукция плантаций и добытые полезные ископаемые, потом это всё отправлялось в океанские порты и далее в метрополию. Порядок поддерживался бельгийскими колониальными войсками, состоявшими из черных солдат под командованием белых офицеров. На местах были вооружённые отряды частных подрядчиков, имевшие похожую структуру. Вот, собственно, и всё. В развитии какой-то серьёзной инфраструктуры у бельгийцев вообще не было интереса. Если вспомнить расхожий штамп про ограбление колоний вот тут он соответствовал реальности.


Фотографии из Бельгийского Конго все как одна ужасные, поэтому вот вам фото Патриса Лумумбы всё-таки его именем наш РУДН назвали

Хоть сколько-то образованных чёрных в бельгийских колониях появились только в последние полтора-два десятка лет. И то, по сути, пределом для местных был уровень сержанта или мелкого клерка. Поэтому, когда бельгийцы начали собирать пожитки, в яростной схватке за власть сошлись такие столпы, как телеграфист Патрис Лумумба, учитель младших классов Пьер Мулеле, сержант колониальных войск Жозеф-Дезире Мобуту и аж цельный бухгалтер Жозеф Касавубу. Сержант победил. Впрочем, это было только начало. Всю историю своей независимости Конго страдало от войн, переворотов и вторжений, в чём ему периодически помогали две другие бельгийские экс-колонии Руанда и Бурунди. Они тоже очень сильно пострадали от бельгийской безответственности. Так что бельгийцы заслуженно получают титул худших колонизаторов в Африке. Возможно, им смогли бы составить в этом конкуренцию немцы, задатки были серьёзные, но их быстро из Африки выкинули.

Вот такие вот занятные моменты от нас скрывает разноцветная карта африканских колоний. Насколько разным на поверку оказывается воспринимаемый цельным явлением европейский колониализм. И насколько разным оказалось его влияние на разные страны этого континента.

Автор: Роман Воронов



VPS серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

VPS серверы от xМаклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации
Подробнее..

Старинное искусство номографии

15.06.2021 12:07:46 | Автор: admin

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


Как выглядят номограммы и как они работают


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


Допустим, вы провели реакцию в растворителе, а теперь собираетесь его удалить (выпарить), чтобы собрать продукт реакции. Растворитель улетучивается изнурительно медленно, а чтобы ускорить процесс, вы решаете его нагреть, но вот беда греть раствор нежелательно, так как продукт реакции от нагревания может испортиться. Создав пониженное давление, вы уменьшите температуру кипения растворителя и сумеете его отделить не причинив вреда растворенному в нем веществу. При нормальном атмосферном давлении 760 мм ртутного столба вода кипит при 100 С, однако, при давлении 40 мм кипит уже при 34 С.


А как быть с гамма-бутиролактоном, который кипит при 204 С? Отмечаем на оси "Температура кипения при 760 мм" точку 204 С, выставляем на кривой оси "Остаточное давление" 5 мм, проводим прямую до пересечения с третьей осью. Ага, значит, в этих условиях наш растворитель начнет выкипать примерно при 70 С.


Это был пример достаточно простой номограммы. Ниже я привожу более сложную. Достоинство номограмм в том, что в них умещаются довольно сложные функциональные зависимости с несколькими переменными. В самом деле, сколько бы понадобилось обычных графиков вида $y=f(x)$ для такой задачи?



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



Разбираемся и делаем свои номограммы


Основания общей теории номографических построений дал Морис Окань (18841891) в его же работах впервые появился термин номограмма. Книга Traite de nomographie. Theorie des abaques. Applications pratiques доступна онлайн. Это истоки. Более краткое современное изложение принципов номографии, по которому я учился делать номограммы читайте здесь The Lost Art of Nomography by Ron Doerfler.


Итак, начнём!


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


$\frac{d \ln p}{dT} = \frac{\Delta H_{vap}}{RT^2}$


где $\Delta H_{vap}$ энтальпия испарения, $R$ газовая постоянная.


Интегрируя последнее уравнение мы получаем:


$\ln \frac{p}{p^*}=-\frac{\Delta H_{vap}}{R}\left( \frac{1}{T}-\frac{1}{T^*}\right)$


где под $p^{*}$ мы обозначим давление 760 мм ртутного столба, а $T^{*}$ температуру кипения при этом давлении. Нас интересует температура кипения $T$ при пониженном давлении $p$.


Правило Трутона запишем так:


$\Delta S_{vap} = \frac{\Delta H_{vap}}{T} \approx 10.5R$


Подставив последнее выражение, получим расчётную формулу:


$\ln \frac{p}{p^*} = 10.5\left(1-\frac{T}{T^*}\right)$


Её и следует привести в номограмму.


Построение номограмм с pynomo


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


pip install pynomo

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


Нам понадобится стандартный блок номер 2 кодирующий зависимости вида:


$F_1(u_1)=F_2(u_2)\times F_3(u_3)$


где $F_i(u_i)$ какая-то одномерная функциональная зависимость. Разберём простой пример.


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


$a = \omega^2 \times r$


Исходный код номограммы
#!/usr/bin/env python3# -*- coding: utf-8 -*-"""    rpm.py    Simple nomogram of type 2: F1=F2*F3"""import syssys.path.insert(0, "..")from pynomo.nomographer import *N_params_RCF={        'u_min':1000.0,        'u_max':30000.0,        'function':lambda u:u,        'title':r'RCF, $\times g$',        'tick_levels':3,        'tick_text_levels':1,        'tick_side': 'left',        'scale_type':'linear smart',        'text_format': r"$%2.0f$",                }N_params_r = {'u_min': 1.0,              'u_max': 5.0,              'function': lambda u:u,              'tick_levels': 3,              'tick_text_levels': 1,              'tick_side': 'left',              'text_format': r"$%2.0f$",              'title':r'R, cm',              'extra_params': [                {'u_min':  5.0,                 'u_max': 10.0,                                  'tick_levels': 2,                 'tick_text_levels': 1,                 'tick_side': 'right',                 'text_format': r"$%2.0f$",},                {'u_min': 10.0,                 'u_max': 40.0,                 'scale_type': 'manual line',                 'manual_axis_data': {10.0: r'10',                                      12.0: r'12',                                      14.0: r'14',                                      16.0: r'16',                                      20.0: r'20',                                      24.0: r'24',                                      30.0: r'30',                                      40.0: r'40'},                 },                ],            }N_params_RPM={        'u_min': 1000.0,        'u_max':20000.0,        'function':lambda u:u*u*1.1182e-5,        'title':r'RPM',        'tick_levels':3,        'tick_text_levels':1,        'scale_type':'linear smart',        'text_format': r"$%2.0f$",                }block_1_params={             'block_type':'type_2',             'mirror_y':True,             'width':10.0,             'height':10.0,             'f1_params':N_params_RCF,             'f2_params':N_params_r,             'f3_params':N_params_RPM,             'isopleth_values':[['x',10.0,15200]],             }main_params={              'filename':'RPM.pdf',              'paper_height':10.0,              'paper_width':10.0,              'block_params':[block_1_params],              'transformations':[('rotate',0.01),('scale paper',)],              'title_str':r'$a=r\times \omega^2$'              }Nomographer(main_params)

Функция $F_3(u_3) = \omega^2$ записывается строкой:


'function':lambda u:u*u*1.1182e-5,

Программа построит номограмму в файл RPM.pdf, ниже на рисунке.



Пунктирная линия называется изоплета она показывает, как пользоваться номограммой для расчёта достигаемого ускорения (в единицах g) при данной геометрии ротора (радиус вращения) и числа оборотов в минуту (RPM).


Почему этот график так работает? Смотрите чертеж.



Из него видно, что треугольники ABC и CDE подобны. Следовательно:


$\frac{AB}{ED} = \frac{BC}{CD} = \frac{BC}{L-BC}$


где L длина BD, она задана. Пользуясь этим соотношением, можно построить шкалу на L.


Зная этот принцип, мы можем построить номограмму для соотношения


$\ln \frac{p}{p^*}+10.5=\frac{10.5T}{T^*}$


что даст нам номограмму для роторного вакуумного испарителя:



Усложняем номограмму


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


$\frac{\Delta H_{vap}}{RT} = (4.5 + \ln T)$


В статье Some calculations for organic chemists: boiling point variation, Boltzmann factors and the Eyring equation. Tetrahedron Letters 41 (2000) 98799882 говорится, что для неё не так то просто создать номограмму. Вот и выясним!


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


$\ln \frac{p}{p^*} + T^{*}(4.5 + \ln T^*)\times \frac{1}{T}-(4.5 + \ln T^*) =0 $


Она попадает под случай блока типа 10


$F_1(u)+F_2(v)F_3(w)+F_4(w)=0.$


Теперь ось в середине номограммы может быть не только прямолинейной. Записываем код.


Более сложная номограмма
from math import logfrom pynomo.nomographer import *import syssys.path.insert(0, "..")Pressure = {    'u_min':  1.0,    'u_max': 760.0,    'function': lambda u: log(u / 760.0),    'title_y_shift': 0.55,    'title': r'Pressure, mmHg',    'tick_levels': 3,    'tick_text_levels': 2,    'scale_type': 'log smart',}BP_guess = {    'u_min':  0.0,    'u_max': 400.0,    'function': lambda u: 1/(u + 273.15),    'title_y_shift': 0.55,    'title': r'B.P. estimated',    'tick_levels': 4,    'tick_text_levels': 2,    'scale_type': 'linear smart',}BP_at_atm = {    'u_min':  0.0,    'u_max': 700.0,    'function_3': lambda u: (u + 273.15)*(4.5 + log(u + 273.15)),    'function_4': lambda u: -(4.5 + log(u + 273.15)),    'title_y_shift': 0.55,    'title': r'B.P. at 760 mmHg',    'tick_levels': 4,    'tick_text_levels': 2,    'scale_type': 'linear smart',}block_1_params = {    'block_type': 'type_10',    'width': 10.0,    'height': 10.0,    'f1_params': Pressure,    'f2_params': BP_guess,    'f3_params': BP_at_atm,    'isopleth_values': [[10, 'x', 204]]}main_params = {    'filename': 'ex_type10_nomo_1.pdf',    'paper_height': 10.0,    'paper_width': 10.0,    'block_params': [block_1_params],    'transformations': [('rotate', 0.01), ('scale paper',)],    'title_y': 0.55,    'title_str': r'Boiling point estimation, $\Delta S_{vap} = R(4.5 + \ln T)$'}Nomographer(main_params)

Вуаля!



Заключение


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




Облачные серверы от Маклауд быстрые и безопасные.


Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!


Подробнее..

Линейные двигатели внутреннего сгорания в роли портативных источников энергии и не только

18.06.2021 10:22:12 | Автор: admin


Более 100 лет известен такой механизм, как двигатель внутреннего сгорания.

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

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

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

1) двухтактные:

image

в них процесс осуществления полезной работы и наполнения цилиндра двигателя новой порцией смеси для сжигания, производится за 2 движения поршня. При движении поршня вниз производится полезная работа, при движении его в обратном направлении, то есть верх, осуществляется сжатие поступивший смеси, для последующего её сжигания;

2) четырехтактные:

image

в них процесс осуществления полезной работы, продувка цилиндра от продуктов сгорания и заполнение его новой порцией смеси, осуществляется за 4 движения поршня:
  • при первом движении поршня вниз, осуществляется полезная работа;
  • при последующем движении поршня вверх, происходит продувка цилиндра от продуктов сгорания;
  • при втором движении поршня вниз, осуществляется заполнение цилиндра свежей порцией смеси;
  • при последующем втором движении поршня вверх, происходит сжатие поступившей свежей смеси, для последующего её сжигания.


3) дизельные двигатели:

image

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

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

Несмотря на широкое распространение двигателей внутреннего сгорания, существует один особый подвид двигателей, который хоть и не получил широкое распространение (на которое он вправе рассчитывать), тем не менее, в некоторых сферах он всё равно применяется.

Это двигатели внутреннего сгорания линейного типа:

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

Оппозитный поршневой двигатель с внешним сжатием
image

Двигатель с противоположным поршнем и внутренним сжатием
image
Однопоршневой двигатель одностороннего действия с возвратным механизмом
image

Свободнопоршневой двигатель
image

Свободнопоршневой двигатель двойного действия
image

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

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

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

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

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

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

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

Среди наиболее распространенных подходов, применяются:
  1. использование противоположной рабочему цилиндру камеры, в качестве газовой пружины;
  2. уравновешивание одного поршня другим, точно таким же поршнем, движущимся в противоположном направлении;
  3. связывание двух поршней движущихся в противоположных направлениях жёсткой рычажной сцепкой;
  4. отсутствие какого-либо балансирования движущегося поршня, за счёт того, что вся система установлена на жестком массивном основании. Это позволяет гасить возникающие вибрации;
  5. иные конструкции, а также комбинации всего вышеперечисленного.


Линейный двигатель внутреннего сгорания позволяет очень легко реализовать эффективный генератор электрического тока.



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

image

Благодаря своей простоте, данные двигатели могут развивать достаточно большие скорости. В частности, имеется информация о достижении такими двигателями частоты в 390 Герц (390 движений поршня в секунду и, соответственно, 23400 в минуту).





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

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

В частности, группой учёных была проведена серия работ, направленная на исследование применимости линейных двигателей в военных нуждах.

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

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

image
(Источник картинки: "4" в списке использованных источников, под этой статьёй)

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

image
Генератор на 300 ватт и на 5 ватт в сравнении со стандартной батарейкой, формата АА (Источник картинки: "4" в списке использованных источников, под этой статьёй)

В ходе поставленного эксперимента, показанный на рисунке генератор, мощностью 5-10 Вт, проработал в течение 100 часов, работая с частотой в 390 Герц. При этом КПД генератора составил 90%.

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


image
Плотность хранимой энергии, по сравнению с перезаряжаемыми аккумуляторами

Почему же, при таких очевидных преимуществах такого типа двигателей, они не получили широкого распространения и не вытеснили так хорошо известные нам двигатели с кривошипно-шатунным механизмом?

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

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

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

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

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

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

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

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

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

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

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

В частности, наблюдаются следующие подходы:
  1. ШИМ-контроль, когда для управления движением поршней, используется электрогенератор, связанный с движущимся поршнем или поршнями, использующийся в данный момент, в качестве подруливающего электродвигателя;
  2. установка точного времени впрыска и зажигания смеси в цилиндр. Современные средства позволяют достаточно точно контролировать местоположение поршня, давление в конкретном цилиндре, а также гарантировано осуществить зажигание смеси. Для этого могут быть использованы разнообразные датчики движения, давления, свечи поверхностного разряда, а также использование в конструкции цилиндров интегрированных в конструкцию цилиндров форкамер (данные камеры упрощают зажигание смеси);
  3. наиболее экзотическим из данного списка, является использование электромагнитных впускных и выпускных клапанов, которое позволяет четко контролировать момент и объем впускаемой/выпускаемой смеси. Данное направление является достаточно экстравагантным, хотя и применяется некоторыми компаниями в составе особо прогрессивных двигателей, используемых, в частности, в гонках формулы-1.


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

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

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

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

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



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



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







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

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

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

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

Использованные источники:

1. www.wikipedia.org
2. www.freikolben.ch/lineargeneratoren.shtml
3. www.freikolben.ch/basics-de.shtml
4. Miniature Internal Combustion Engine-Generator For High Energy Density Portable Power
Kurt D. Annen*, David B. Stickler, and Jim Woodroffe
Aerodyne Research, Inc
Billerica, MA 01821



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Залпы на орбите. Оборонительная установка Р-23М

18.06.2021 16:08:17 | Автор: admin
Так вышло, что развитию космонавтики мы обязаны военным программам США, СССР, Германии. Сперва это было просто желание закинуть бомбу подальше да потяжелее, а потом, со взрывным ростом космических технологий в конце 50-х 60-х годах, появились идеи вывода на орбиту самых разных видов вооружения. В том числе это были боевые и разведывательные станции, спутники и ракетопланы. Холодная война подталкивала противоборствующие стороны ко всё более и более сложным системам, ведь выводимое тогда на орбиту оружие было неуязвимо для имевшихся зенитных и авиационных ракет. Но стоимость развёртывания образцов доходила до таких космических значений, что СССР и США решили от греха подальше подписать Договор о космосе, ибо бюджеты не потянули бы гонку.

И как заключительный акт этой истории, 24 января 1975 года советская станция Салют-3 затряслась от длинной очереди из своей оборонительной установки.

И всё-таки, зачем нужна была пушка на орбитальной станции, тем более стрельбы были аж через 8 лет после подписания Договора о космосе? Идея орбитальных пилотируемых станций (ОПС) родилась ещё в начале 60-х, когда разведывательные спутники у нас только начинали развитие и были далеки от совершенства. Пилотируемая станция могла снять гораздо больше, причём выборочно, не тратя драгоценную плёнку на пустые районы. Также предполагалось оснастить станцию уникальным фотоаппаратом с диаметром зеркала около 2 м. В это же время в США активно развивались ракетопланы (X-15, X-20), а неказистые с виду Джемини могли активно маневрировать.


Аэрокосмический пилотируемый ракетоплан Boeing X-20 DYNA-SOAR

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


Нил Армстронг после первого полёта на North American X-15 30 ноября 1960

Огнестрельное оружие было выбрано не случайно. На время старта работ ракеты воздух-воздух были ещё далеки от совершенства и у нас в стране не считались оружием победы. Начавшаяся чуть позже война во Вьетнаме всё-таки показала, что ракеты нужны и желательно побольше, но и эксплуатация ранних Фантомов показала, что пушки всё-таки тоже нужны. Кроме капризности при размещения ракет на станции прибавлялись и другие минусы большая масса, необходимость сложной системы наведения, основанной на мощных РЛС, которые опять же много весили и требовали мощный источник питания с серьёзной системой охлаждения. Предлагалось энергетическое оружие, но на 60-е это было за гранью фантастики. Автопушка же обладала достаточно невысокой массой, возможностью нести серьёзный боекомплект, а для поражения ракетоплана было достаточно одного попадания и тот, даже если переживёт его на орбите, не сможет вернуться на Землю. Крайне низкая плотность атмосферы на орбите позволяла снарядам долгое время не терять скорость и, соответственно, свою поражающую способность. Конечно, были минусы в виде необходимости компенсации отдачи и малой прицельной дальности. С первым решили бороться компенсирующей работой двигателей, а со вторым смирились. Для основы будущей оборонительной установки взяли авиационную 23-мм пушка Рихтера Р-23 от огнестрельного комплекса бомбардировщика Ту-22.


дальний тяжёлый сверхзвуковой бомбардировщик Ту-22




23-мм автоматическая пушка Р-23

Весила она 58,5 кг и имела огромную для одноствольной конструкции скорострельность до 2500 выстрелов в минуту. То есть для создания равной с Р-23 плотности огня потребовалось бы три обычных авиапушки НР-23 весом 38 кг каждая. Этого удалось добиться барабанной схемой (часто ещё называют револьверной), когда на один ствол приходится несколько патронников. Это позволяет проводить сразу несколько операций досылание, запирание ствола, выстрел, отпирание и экстракция гильзы. Автоматика пушки работала аж от трёх (!) газовых поршней. первый производил экстракцию гильзы вперёд, второй досылал патрон, третий поворачивал барабан. Чтобы уменьшить длину системы, досылание патрона происходило спереди, что накладывало ограничения на боеприпас и не позволяло использовать уже выпускающиеся серийно.





Особенности заряжания и огромные нагрузки на боеприпас (при заряжании скорость доходила до 25 м/с, а экстрагированная гильза летела 40 м/с) потребовали создать крайне тяжёлый патрон 23х260 мм. Он весил 509 г против 320 г у привычного 23х115 мм. Начальная скорость ОФС 850 м/с, ради чего достаточно короткоствольной пушке потребовался мощный заряд в 67 грамм. Стальная толстостенная гильза вмещала в себя и заряд, и снаряд, как у современных телескопических боеприпасов. Масса боекомплекта оборонительной установки Ту-22 была аж на полтонны больше, чем если бы использовали предлагавшуюся АМ-23. Известный ныне конструктор Грязев писал, что система пушка-патрон получилась просто безумно дорогой. Были проблемы с надёжностью. Вот так Р-23 на земле осталась уникальным вооружением Ту-22.


Устройство патрона 23х260


Он же собственной персоной

Разработка орбитального варианта Р-23 началась в середине 60-х (хотя зачастую указывают начало 70-х). Тут сразу стоит сказать, что доступной официальной информации по системе очень мало и многое было неизвестно до видеоролика Военной приёмки Пуля не дура. Или шесть рекордов русских оружейников. В довольно серьёзных книгах рисовали модели чего-то с длинным и тонким стволом, что отличалось от базовой модели. Некоторые указывали, что за основу брали не Р-23, а НР-23 (но тут скорее ошибка из-за того, что в созданием Р-23М руководил Нудельман). По сети гуляли мутные фотографии из запасников Точмаша, по которым умельцы сделали довольно известные 3D-модели, но были и сомневающиеся, что изделие является именно космической пушкой и доверяли книжным рисункам. Обратимся к физике при огромной скорострельности Р-23 ствол должен либо иметь феноменальное охлаждение, либо быть весьма массивным, что и выбрал Рихтер. На орбите же проблемы с длинным и тонким стволом увеличатся это и размещение в ограниченном объеме и охлаждение, с которым в космосе очень большие проблемы передача тепла может производиться фактически только за счёт излучения, тогда как на Земле львиная доля приходится на взаимодействие частиц. Это же ограничивает применение энергетического оружия мало того, что надо найти источник энергии, так избыточное тепло надо куда-то деть.


Фото реальной Р-23М


Одно из первых доступных фото Р-23М


Это выдавали за Р-23М в 90-х 00-х

Из самого интересного, что узнали из Военной приёмки это то, что царь-то не настоящий пушка вовсе не пушка, а крупнокалиберный пулемёт калибра 14,5 мм. Оно и понятно на станции важен каждый грамм, а таскать хотя бы 300 снарядов массой по полкилограмма и из разведывательной станции получится жирный истребитель спутников. В видео замтехдиректора Точмаша Валерий Макеев указал массу пулемёта (будем уж точны и назовём вещи своими именами) в 17 кг, что гораздо легче 58 кг Р-23. Зато выросла скорострельность аж 5000 в/мин (ранее указывалось 800-950 в/мин)! Для одноствольной системы это просто фантастическая цифра. Её достигали ещё при разработке Р-23, но, опять же, сделав на базе пушки пулемёт. Правда, тогда после первой же очереди ствол сгорел. Вполне возможно, это была не отдельная разработка, а как раз первые образцы Р-23М. Получившийся новый патрон достаточно небольшой и, судя по всему, обладает латунной, а не стальной, как у 23х260, гильзой, что также снижает массу. Пули, небольшие, тупоносые. Из-за особенностей заряжания, как и у предка, донце находится на сужающемся конце гильзы. Какой боекомплект был неизвестно, но судя по патронному коробу явно невелик.


Патронная лента к Р-23М

Пулемёт, в составе установки Щит-1, неподвижно монтировался в корпусе станции. Для наведения надо было ворочать всю станцию целиком. Сделано это для одного компенсировать отдачу работой 2 ЖРД коррекции по 400 кгс тяги и 16 ЖРД жёсткой ориентации по 20 кгс. ОПС Алмаз должен был иметь массу 17,8 т (для сравнения, транспортный корабль снабжения ТКС имел стартовую массу 21 т) и стрельба из крупнокалиберного пулемёта с дикой скорострельностью вполне могла увести с целевой орбиты. То есть, возможность стрельбы ограничивалась не только боекомплектом, но и запасом топлива. В конце жизненного цикла станции, после многочисленных корректировок орбиты, вполне могло статься, что станция будет беззащитной. Стрельбу можно было вести как в ручном режиме с центра контроля полёта и управления станцией, так и дистанционном, с Земли. Из-за характерной конструкции панорамного-обзорного устройства и перископа кругового обзора Сокол обстановка напоминала место наводчика танка.


Центр контроля полёта и управления станциями типа Алмаз

В свой полёт с первой и последней стрельбой Р-23М отправилась в составе Алмаз-2 (он же Салют-3) 26 июня 1974 года. К ней были отправлены две экспедиции на кораблях Союз-14 (с 3 по 19 июля 74-го, экипаж Павел Попович и Юрий Артюхин) и Союз-15 (с 26 по 28 августа, экипаж Геннадий Сарафанов, Лев Дёмин, из-за поломки системы стыковки Игла пришлось возвращаться ни с чем). Срок жизни станции подошёл к концу в 75-ом. 24 января 1975 года её вывели на орбиту захоронения. Тогда и была дана тестовая очередь из 20 выстрелов. Больше оружия на станции не ставили американцы так и не создали орбитальных истребителей, а пулемёт всё-таки был слабой защитой, если перехватчики всё-таки создали бы развитие авиационных ракет сделало стрелковое оружие вторичным.


Салют-3 на Байконуре

Источники
Первушин А. Звёздные войны. Американская Республика против Советской Империи. 2005.

Афанасьев И. Мировая пилотируемая космонавтика. История. Техника. Люди. 2005.

Первушин А. Опасный Полёт и боевые Космосы. Warspot, 2020.

Материалы форума www.russianarms.ru.

Первушин А. Первая орбитальная станция: как Алмаз стал Салютом. Warspot, 2019.

Авиационная пушка Р-23 материал почившего ресурса dogswar: www.dogswar.ru/oborydovanie/75-oborydovanie/17.

Морозов В., Обухович В., Сидоренко С. и др. Энциклопедия современной военной авиации 1945-2002. 2005.

Военная приёмка. Пуля не дура. Или шесть рекордов русских оружейников.



Автор: Алексей Борзенков



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Компьютерное доказательство теории конденсированной математики первый шаг к великому объединению

20.06.2021 16:11:04 | Автор: admin

Пример расчётного доказательства в Lean

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

Теперь вспомогательный софт для доказательства теорем (proof assistant software) не просто для проверяет доказательства, но помогает выйти на принципиально новый уровень великого объединения разных математических разделов. Концепция конденсированной математики обещает принести новые идеи и связи между областями, начиная от геометрии и заканчивая теорией чисел. Это в своём роде великое объединение математики

Впрочем, обо всём по порядку.

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

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

Конденсированная математика


Амбициозный план Петер Шольце разработал совместно с коллегой Дастином Клаузеном из Копенгагенского университета и изложил в серии лекций по аналитической геометрии (pdf) в в 2019 году в Боннском университете (Германия), где они работают.

По мнению коллег, если замысел Шольце будет реализован, то через 50 лет преподавание математики аспирантам может сильно отличаться от сегодняшнего. Мне кажется, есть много областей математики, на которые повлияют его идеи, говорит Эмили Риль, математик из Университета Джона Хопкинса в Балтиморе.

До сих пор большая часть аналитической геометрии Шольце опиралась на техническое доказательство, настолько сложное, что даже сами авторы не были уверены в его корректности. Но 5 июня 2021 года Шольце объявил в блоге о завершении работы по компьютерному доказательству!

Великое объединение


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

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

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

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


Но если определить топологию правильным образом, то аналогии между теориями окажутся примерами одной и той же конденсированной математики, пишет Nature. Это своего рода великое объединение трёх областей.

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

Теорема


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



Работа по доказательству получила название Liquid Tensor Experiment, по имени любимой рок-группы математиков.

Вспомогательный софт для доказательства теорем


Вспомогательный софт для доказательства теорем (proof assistant software) используется уже десятилетиями. Если вкратце, то пользователь вводит в систему утверждения, которые дают машине определения математических понятий объектов на основе более простых объектов, о которых машина уже знает. Это утверждение может просто ссылаться на известные объекты. Затем программа ответит, является ли данный факт очевидно истинным или ложным, основываясь на своих текущих знаниях.

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

Из самых известных систем такого типа Isabelle, Lean, Mizar и Coq, см. более полный список.

В данном случае математики решили использовать программу Lean и кодирование всех необходимых понятий и объектов заняло полгода. Естественно, Шольце работал не в одиночку. Ему помогала группа добровольцев под руководством Йохана Коммелина, математика из Фрайбургского университета в Германии.


Числовая прямая, показано положение на ней чисел $2$, $e$ и $\pi$

По словам одного из помощников Йохана Коммелина, Lean-версия доказательства Шольце включат десятки тысяч строк кода она примерно в 100 раз больше, чем оригинальная версия: Если вы просто посмотрите на код Lean, вам будет очень трудно понять доказательство, особенно в его нынешнем виде. Но исследователи говорят, что потраченные усилия, заставить доказательство сработало в программе, помогли им лучше понять саму теорему и доказательство.

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

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

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


Выдача Lean по доказательству теоремы Шольце и Клаузена

У таких программ есть свои поклонники, но это первый случай, когда они сыграли важную роль во фронтире математической науки, говорит Кевин Баззард, математик из Имперского колледжа Лондона, который участвовал в совместном проекте по проверке результатов Шольце и Клаузена. До сих пор в воздухе висел главный вопрос: справятся ли они со сложной математикой? Мы показали, что справятся.

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



VPS серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Баги ради науки как Университет Миннесоты внедрял баги в код Linux

16.06.2021 10:13:29 | Автор: admin


Грег Кроа-Хартман, ответственный за сопровождение стабильных релизов ядра, в начале апреля запретил Университету Миннесоты (УМ) вносить изменения в код Linux. Университет Миннесоты по-видимому, всё это время сознательно вносил вредоносные изменения в код проекта. Развязка наступила после патча к механизму авторизации NFSv4, который не прошел проверку двух профильных разработчиков. Так выглядел тот самый патч.

diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.cindex 5f42aa5fc612..eb52eebb3923 100644--- a/net/sunrpc/auth_gss/auth_gss.c+++ b/net/sunrpc/auth_gss/auth_gss.c@@ -848,7 +848,8 @@ gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)warn_gssd();gss_release_msg(gss_msg);}-gss_release_msg(gss_msg);+if (gss_msg)+gss_release_msg(gss_msg);}static void gss_pipe_dentry_destroy(struct dentry *dir,--2.25.1

Ирония однако заключается в том, что спорный проект, который привел к потере доверия к Университету Миннесоты, изначально был направлен на повышение безопасности Linux. В университете с августа 2020 г ассистент профессора Kangije Lu и аспирант Qjushi Wu работали над статьёй под названием On the Feasibility of Stealthily Introducing Vulnerabilities in Open-Source Software via Hypocrite Commits. Под термином Open-Source Software имеется в виду ядро Linux.

Авторам Hypocrite Commits удалось внести в код ядра изменения, содержащие в скрытом виде уязвимость вида обращение после освобождения памяти. Статья была принята на 42-м симпозиуме IEEE по безопасности и конфиденциальности, но уже 26-го апреля Qjushi Wu и Kangije Lu отозвали своё участие в мероприятии. В письме они выражают своё сожаление по поводу случившегося и просят прощения комитету за доставленные неудобства.

We now understand that it was inappropriate and hurtful to the community to make it a subject of our research, and to waste its effort reviewing these patches without its knowledge or permission.

Исследование было поддержано со стороны National Science Foundation и содержало гарантии того, что ни один баг не попадет в исходный код Linux. Тем не менее, именно это и произошло в итоге. Однако миннесотовцы не остановились на достигнутом и решили пойти дальше, стремясь в этот раз протащить новую серию уязвимых патчей под видом нового статического анализатора кода. Развязка наступила 6-го апреля, когда Грег Кроа-Хартман назвал вещи своими именами.

Please stop submitting known-invalid patches. Your professor is playing around with the review process in order to achieve a paper in some strange and bizarre way.

This is not ok, it is wasting our time, and we will have to report this, AGAIN, to your university...

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

Излишне напоминать о том, что у разработчиков ядра и так достаточно проблем со спонтанными ошибками, следовательно поэтому патчи с намеренными ошибками явно нежелательны. Кроа-Хартман сказал, что все патчи, поступающие от @umn.edu отныне подлежат отмене, так как то, что они делают, является преднамеренным вредоносным поведением, неприемлемо и совершенно неэтично.

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

On Wed, Apr 21, 2021 at 02:56:27AM -0500, Aditya Pakki wrote:

Greg, I respectfully ask you to cease and desist from making wild accusations that are bordering on slander.

These patches were sent as part of a new static analyzer that I wrote and it's sensitivity is obviously not great. I sent patches on the hopes to get feedback. We are not experts in the linux kernel and repeatedly making these statements is disgusting to hear.

Адитья Пакки энергично отрицает злой умысел и требует от Грега прекратить эту ужасные обвинения. Последний, однако, не обращая внимания на весь пафос экспериментатора из УМ, собрал патч для отката тех 190 изменений, где это можно сделать без усилий. Оставшиеся 68 патчей требуют более тонкой работы для отката изменений, так как часть из них уже была отменена, или для них уже были исправления.

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

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

Our community does not appreciate being experimented on, and being tested by submitting known patches that are either do nothing on purpose, or introduce bugs on purpose. If you wish to do work like this, I suggest you find a different community to run your experiments on, you are not welcome here.

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

Технический Консультационный Совет Linux Foundation направил сообщение в электронной почте факультету Computer Science с полным списком патчей Университета Миннесоты, начиная с 2018 г., связанных с исследованиями по безопасности в ядре Linux. Авторы сообщения стремились исчерпать конфликтную ситуацию и восстановить доверие между обеими сторонами.

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



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Перевод Как подружиться со своей интернет-зависимостью практическое руководство

19.06.2021 16:06:42 | Автор: admin

В этом переводе автор подробно описывает 9 правил разумного потребления цифрового контента, которые он успешно протестировал на себе.



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


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


Если это про вас, то у меня есть три новости одна плохая и две хорошие.


  1. Плохая новость: в вашем мозгу нарушился естественный процесс получения вознаграждения.
  2. Первая хорошая новость: это распространённая проблема, с которой до вас справились многие.
  3. Вторая хорошая новость: в этой статье я расскажу о девяти правилах, которые позволили мне справиться с этим.

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


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


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


Обезьяний мозг и дофаминовые кнопки


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


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


Вот что повышает уровень дофамина (хотя это не полный список):


  • Социальное взаимодействие
  • Секс
  • Наказание тех, кто это заслужил
  • Новизна и неожиданность
  • Юмор
  • Владение неопровержимыми доказательствами / чувство собственной правоты

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


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


Мы могли бы не относиться к этому настолько серьёзно, но мозг очень податлив. Длительное воздействие раздражителей заставляет его ожидать и ценить определённые вещи. То, чем вы кормите свой мозг, формирует в нём закономерности. В нашем случае это (среди прочего):


  • Потребность избегать неприятных моментов в реальной жизни, прятаться в безопасном и комфортном виртуальном мире.

  • Неспособность расставить приоритеты и сосредоточиться на важном.

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

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

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

  • Подлость и цинизм. Люди привыкают потакать своему внутреннему ребёнку в сети, обесценивать чужие интересы, устраивать демагогию или жаловаться. Со временем это проникает в их реальную жизнь.

Как я уже писал выше, эту проблему можно решить и вновь научиться:


  • Ясно мыслить
  • Быть более продуктивными и креативными
  • Быть добрее к себе и к другим
  • Привнести в свою жизнь больше покоя и созерцания
  • Снизить беспокойство

Или, проще говоря: вы можете перейти от состояния постоянной рассеянности к состоянию спокойного, ясного мышления.


Итак, сформулируем нашу цель конкретнее.


Между полезно и вкусно


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


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


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


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


9 правил потребления цифрового контента


Каждое из этих правил описывает новую модель потребления. Я сформулировал их как набор советов для выработки новых привычек. Для сравнения я сначала привожу вредные советы, а потом объясняю суть соответствующих полезных советов.


Правило 1: Используйте устройства целенаправленно


Вредные советы: обращайтесь к экрану всякий раз, когда вам что-то нужно. Тревожитесь? Огорчены? Одиноко? Скучно? На экране есть то, что нужно. Жмакайте на экран, как будто это игровой автомат. Будьте обезьянкой на полную катушку!


Полезные советы: будьте осмотрительны при использовании цифровых устройств. Задавайте себе вопросы: Почему я это делаю? Что я хочу здесь найти? Какая у меня цель?


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


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


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


Правило 2: Уберите цифровые устройства из центра вашей вселенной


Вредные советы: постоянно следите за своим телефоном, за компьютером и за телевизором тоже. Лучше, чтобы всё это было включено круглосуточно. Отвечайте на все сообщения и приглашения сразу же, как только они появляются на экране. Посмотрите-ка сюда. А теперь посмотрите туда. А теперь сюда!


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


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


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


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


Переместите цифровые устройства из центра вашей вселенной в долгий ящик, куда вы время от времени заглядываете. Качество вашей жизни значительно улучшится.


Правило 3: Разделите контент на Никогда, Иногда и Всегда


Вредные советы: потребляйте нездоровую цифровую пищу:


  • С бесконечным скроллом
  • Вызывающую сильные эмоции
  • Быстро обновляющуюся
  • Крайне незрелую (субреддиты со всякой фигнёй, Twitter, 4chan и так далее)
  • Доступную везде и по запросу

Но, как и в случае с настоящей нездоровой пищей, её избыток полностью разрушает вашу диету. Вы захотите есть её всё больше и больше. Постепенно ваши мысли станут похожи на бесконечно саркастические, жестокие и стервозные выплески Twitter или Buzzfeed.


Полезные советы: вы то, что вы едите. Это верно как для еды, так и для цифрового контента. Разделите свою информационную диету на Никогда, Иногда и Всегда.


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


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


Иногда: разумные послабления. Используйте это как награду. Вы рано встали и хорошо поработали? Отлично, наслаждайтесь Netflix днём. Или сделайте десятиминутный перерыв в Instagram. Нет жёстких правил; вы сами знаете, когда вы действительно это заслужили.


Мой список Иногда также включает Reddit (ТОЛЬКО мотивационные / позитивные субреддиты), Facebook, New York Times, грамотно написанные блоги про бизнес, мотивацию или здоровье. Есть авторы и блоги, которые выдают годный контент. Добавьте их в список рассылки и читайте.


Всегда: КНИГИ. Книги не только тренируют ваш ум, чтобы монотонно усваивать большие объёмы информации, многие из них по качеству отображения текста составят конкуренцию любому экрану.


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


Помните поговорку с глаз долой из сердца вон? Отпишитесь от плохих субреддитов. Используйте Social Fixer, чтобы заблокировать неприятные ключевые слова / домены из вашей ленты Facebook. Используйте блокировщик URL, чтобы заблокировать Twitter.


Правило 4: Откажитесь от многозадачности


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


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


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


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


Правило 5: Выбирайте живое общение


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


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


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


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


Позвоните старым друзьям или знакомым и для начала сходите в кафе, чтобы наверстать упущенное. Пригласите кого-нибудь на свидание. Сходите в гости к другу и просто БУДЬТЕ с ним. Не беспокойтесь о том, чтобы зафиксировать это в своей соцсети.


Лично у меня остался аккаунт на Facebook и Reddit, а всё остальное я забросил. И это здорово. Да, я кое-что упускаю, но оставляю свободное пространство для живого общения.


Правило 6: Откажитесь от порно


Вредные советы: удовлетворяйте свои гедонистические желания сколько душе угодно. Это нормально, это здорово.


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


Полезные советы: это просто прекратите смотреть порно.


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


А ещё лучше сосредоточьтесь на близости с любимым человеком или работайте над тем, чтобы найти его. В конце концов, посетите /r/nofap или /r/noporn


Правило 7: Не используйте цифровые устройства утром и вечером


Вредные советы: держите телефон у кровати. Просыпайтесь, проверяйте свой почтовый ящик, Twitter и Instagram, прежде чем встать. Перед сном смотрите на экран до тех пор, пока не потеряете сознание.


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


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


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


Правило 8: Общайтесь онлайн так же, как в реальности


Вредные советы: позвольте своему внутреннему ребёнку свободно бегать по сети. Люди хулиганят, бесконечно ворчат, ссорятся с незнакомцами, оставляют неприятные комментарии. Что в этом плохого? Это же не вы, а ваш аватар в Интернете так себя ведёт.


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


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


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


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


Правило 9: Становитесь лучше в режиме офлайн


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


Полезные советы: жизнь коротка. Мы все умрём, но смерть это не самое страшное. Страшно угасать медленно. Ваше здоровье, надежды и мечты, вероятно, угаснут раньше, чем ум и тело.


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


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


Сделайте первые шаги


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


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




VPS серверы от Маклауд быстрые и безопасные.


Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!


Подробнее..

Краткая история Windows и что у нее под капотом

21.06.2021 16:11:25 | Автор: admin


Несколько дней назад в сеть просочился образ ранней версии Windows 11. Различные издательства провели тесты по производительности и пришли к неутешительному выводу: Windows 11 в среднем работает хуже, чем Windows 10. Но расстраиваться рано! Проблемы производительности могут быть связаны с сыростью слитого образа и нюансами совместимости с текущими программами. Так или иначе, 24 июня состоится официальная презентация нового поколения операционных систем Windows, которая, возможно, даст ответы на многие вопросы. Если сегодня у вас есть настроение для ностальгии, предлагаем вам окунуться в мир Windows: познакомиться с историей, как менялась ось и что у нее внутри.

История Windows



В начале 80 годов прошлого века компания IBM работала над персональным компьютером на базе процессора Intel 8088. С середины 70 годов компания Microsoft была основным поставщиком Basic для восьмибитных микрокомпьютеров. Когда IBM обратилась к Microsoft для лицензирования Basic для их нового компьютера IBM PC, Microsoft согласилась, а также посоветовала обратиться к компании Digital Research для лицензирования операционной системы CP/M. Но, получилось так, что глава Digital Research не нашел в своем графике времени для встречи для IBM, и IBM снова обратилась к Microsoft, теперь уже с просьбой решить вопрос операционной системы для IBM PC. Microsoft купила клон ОС CP/M у компании Seattle Computer Products и перенесла её на IBM PC. Итоговым названием получившейся ОС стало MS-DOS 1.0.


IBM PC

Первые продукты с названием Windows от Microsoft не были операционными системами. Это были графические среды для MS-DOS. На фоне успеха, в том числе и коммерческого, пользовательского интерфейса на Apple Lisa, компания решила реализовать графический интерфейс на IBM PC с MS-DOS. В отличии от относительно дешевых IBM PC, Apple Lisa стоили дорого (почти 10 тысяч долларов), и немногие покупатели могли позволить купить их. Microsoft решила занять нишу дешевых компьютеров с графическим интерфейсом. При этом низкая стоимость достигалась экономией на комплектующих и более низкая производительность, по сравнению с Lisa, избежать не получилось. Так, в 1985, 1987 и в 1990 выходят первые три версии Windows 1.0, 2.0 и 3.0. Причем за первые шесть месяцев после релиза Windows 3.0 было продано более 1 миллиона экземпляров. Дальнейшее развитие Windows можно разделить на два направления Windows на базе MS-DOS и Windows на базе NT.


Windows 1.01

Windows 9x


Windows на базе MS-DOS или Windows 9x не были первыми ОС от Microsoft, но они продолжали старые традиции и были построены на основе 16-битного кода MS-DOS. В августе 1995 года была выпущена Windows 95 первая система семейства Windows 9x. Она уже была полноценной операционной системой с соответствующими возможностями. Однако у системы были проблемы с безопасностью (например, не было администратора) и с изоляцией приложений. Зависание 16-битного приложения приводило к блокировке всей системы. Проблемы со стабильностью достались и Windows 98 и Windows ME, которые отличались от выпуска 95 года рядом небольших обновлений.


Windows 95

Windows NT


В целом, к концу 80-х годов в Microsoft появилось понимание о необходимости разработки операционной системы не на базе MS-DOS. Параллельно с разработкой софта, связанного с MS-DOS, Microsoft наняла команду инженеров из компании DEC для разработки новой 32-битной операционной системы. Главой группы стал Дэйв Катлер один из главных разработчиков ОС VMS. Новая система была названа NT от сокращения New Technology. Основной упор при разработке NT делался на безопасность и надежность системы, а также на совместимость с Windows на MS-DOS. Так получилось, что опыт при разработке VMS повлиял на NT и сходство между ними стало причиной спора между DEC и Microsoft. По итогу спор был решен во внесудебном порядке.


Дэйв Катлер

Первая система Windows называлась Windows NT 3.1 и была выпущена в 1993 году. Это была первая ОС от Microsoft. Индекс 3.1 был выбран для соответствия Windows 3.1 на MS-DOS. Эта версия не имела особого успеха. Для NT требовалось больше памяти, 32-разрядных приложений на рынке было мало, возникали проблемы с совместимостью драйвером. Достичь поставленных целей смогли в NT 3.5. А первым серьезным обновлением для NT стала версия 4.0 в 96 году. Теперь эта система была мощна, надежна и безопасна, а также обеспечивала тот же интерфейс, что и Windows 95 (которая к тому моменту была чрезвычайно популярной).


Windows NT 3.1

В 2000 году вышла новая версия Windows Windows 2000. Она развивала идеи, заложенные в системы NT. Был добавлена технология Plug-and-Play, управление электропитанием и улучшен интерфейс пользователя.


Windows 2000

Успех Windows 2000 задал вектор развития для следующего поколения Windows XP. В хрюшке Microsoft улучшила совместимость, интерфейс стал более дружелюбным. Стратегия Microsoft завоевывать аудиторию уже знакомыми системами дала плоды за несколько лет Windows XP была установлена на сотнях миллионах ПК. Эпоха MS-DOS подошла к концу.


Windows XP

Следующий проект Microsoft пал жертвой собственных амбиций. Через пять лет после Windows XP, в 2006 году на свет вышла Windows Vista. В ней был переделан графический интерфейс, переработаны и добавлены функциональные возможности в плане безопасности. Была улучшена производительность, надежность.

Первоначальные планы Microsoft по поводу Vista были настолько обширны, что через несколько лет после начала разработки проект пришлось сильно ограничить. Vista включала в себе 70 миллионов строк кода, часть которого составлял причесанный код XP. Неудача Vista отчасти с тем, что она вышла не в то время. На 2006 год пришелся бум недорогих компьютеров, которые не могли обеспечить достаточную для Vista производительность.


Windows Vista

Проблемы Vista были учтены при разработке Windows 7. Microsoft уделила большее внимание тестированию и производительности новой системы. Windows 7 быстро вытеснила Vista, а затем и XP, став самой популярной версией Windows до появления Windows 10 (сейчас Windows 7 на втором месте по популярности).


Windows 7

Бум смартфонов в начале 2010-х подтолкнул Microsoft к созданию операционной системы, которую можно было бы развернуть на разных устройствах: на телефонах, планшетах, приставках и т. д. В результате этой работы мир узрел Windows 8. Восьмерка построена на модульном подходе MinWin для получения небольшого ядра ОС, которое можно было бы расширить на линейку других типов устройств. Но аудитория встретила холодно такой подход. Многие люди критиковали смартфоноподобный интерфейс на ПК, отсутствие кнопки пуск. Для решения многих проблем Microsoft выпустила обновление под названием Windows 8.1, которая, помимо исправления имеющихся ошибок, добавила новые функции.


Windows 8.1

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


Технические аспекты


Чтобы осветить все технические аспекты и тонкости операционной системы Windows понадобится не менее 1000 страниц. Для особо любопытных советуем 7-е издание Внутреннего устройства Windows Марка Руссиновича, специалиста по внутреннему устройству Windows. Также можно почитать Современные операционные системы Эндрю Таненбаума и Operating System Concepts: в обеих книгах есть главы, посвященные Windows. Здесь же ограничимся рассмотрением инструментов взаимодействия приложений пользователя с операционной системой (Windows API) и архитектуры оси.

Архитектура


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

Windows считается операционной системой с гибридным ядром. С одной стороны компоненты ядра Windows располагаются в вытесняемой памяти и взаимодействуют друг с другом путем передачи сообщений, как в микроядерных системах. С другой стороны ядро слишком велико (более 1 Мбайт), а большая часть кода ОС и кода драйверов устройств использует одно защищенное пространство памяти защищенного режима, что свойственно монолитным ОС. Это означает, что в теории любой компонент ОС или драйвер устройства может повредить данные, используемые другими системными компонентами. В Windows эта проблема решается за счет повышения качества и контроля происхождения сторонних драйверов через такие программы, как WHQL или KMCS. Одновременно применяются дополнительные технологии защиты ядра, такие как безопасность на базе виртуализации, функции Device Guard.

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


Упрощенная схема архитектуры Windows

Вторая линия разделяет компоненты режима ядра и гипервизор (Hyper-V). Гипервизор перехватывает многие привилегированные операции, выполняемые ядром, и эмулирует их таким образом, чтобы позволить на одной и той же машине одновременно работать нескольким операционными системам. Гипервизор работает на том же уровне привилегий процессора (0), что и ядро. Но из-за использования специализированных команд процессора (VT-x у процессоров Intel, SVM у АMD) он может изолироваться от ядра с сохранением контроля над ним и приложениями. Поэтому некоторые иногда применяют термин кольцо -1.

Четыре базовых типа процессов пользовательского режима:

  • Пользовательские процессы. Эти процессы относятся к одному из следующих типов: 32- или 64-разрядные приложения Windows (приложения Windows Apps, работающие на базе среды Windows Runtime в Windows 8 и выше, включаются в эту категорию), 16-разрядные приложения Windows 3.1, 16-разрядные приложения MS-DOS, 32- и 64-разрядные приложения POSIX. Заметим, что 16-разрядные приложения могут выполняться только в 32-разрядных версиях Windows, а приложения POSIX в Windows 8 уже не поддерживаются.
  • Процессы служб. В эту категорию входят процессы, являющиеся хостами для служб Windows (например, службы планировщика задач и диспетчер печати). Обычно к службам предъявляется требование независимости выполнения от входа пользователя. Многие серверные приложения Windows (например, Microsoft SQL Server и Microsoft Exchange Server) также включают компоненты, выполняемые как службы.
  • Системные процессы. Фиксированные процессы, такие как процесс входа или диспетчер сеансов, не являются службами Windows. Другими словами, они не запускаются диспетчером служб.
  • Серверные процессы подсистем среды. Такие процессы реализуют часть поддержки среды ОС, предоставляемой пользователю и программисту. Изначально в Windows NT было три подсистемы среды: Windows, POSIX и OS/2. Подсистема OS/2 включалась только до Windows 2000, подсистема POSIX в последний раз была включена в Windows XP.Ultimate- и Enterprise-выпуски клиента Windows 7. Все серверные версии Windows 2008 R2 включают поддержку расширенной подсистемы POSIX, называемой SUA (Subsystem for UNIX-based Applications). Сейчас подсистема SUA не поддерживается и уже не включается как необязательное часть в версии Windows (Windows 10 версии 1607 включает подсистему Windows для Linux WSL, Windows Subsystem for Linux).

Обратим внимание на блок DLL подсистем под блоками Процессы служб и Пользовательские процессы. В Windows пользовательские приложения не вызывают низкоуровневые сервисные функции операционной системы напрямую. Вместо этого они проходят через одну или несколько динамических библиотек (DLL) подсистем. Их роль состоит в том, чтобы преобразовывать документированные функции в соответствующие внутренние (недокументированные) вызовы системных функций, реализованных в основном в Ntdll.dll. Преобразование может включать (а может не включать) отправку сообщения процессу, обслуживающему пользовательский процесс.

Компоненты режима ядра:

  • Исполнительная система. Она содержит базовые сервисные функции ОС: управление памятью, управление процессами и потоками, безопасность, ввод/вывод, сетевая поддержка и межпроцессные коммуникации.
  • Ядро Windows. Низкоуровневые функции ОС: планирование потоков, диспетчеризация прерываний и исключений и многопроцессорная синхронизация. Также ядро предоставляет набор функций и базовых объектов, которые используются исполнительной системой для реализации высокоуровневых конструкций.
  • Драйверы устройств. Сюда входят как драйверы физических устройств, преобразующие вызовы пользовательских функций ввода/вывода в конкретные запросы ввода/вывода к устройству, так и драйверы устройств, не относящихся к физическому оборудованию, например драйверы файловой системы или сетевые драйверы.
  • Слой абстрагирования оборудования (HAL). Прослойка кода, изолирующее ядро, драйверы устройств и прочий исполняемый код Windows от платформенно-зависимых различий в работе оборудования, например различий между системными платами.
  • Оконная и графическая система. Реализация функций графического интерфейса (GUI), также известных как функции GDI: работа с окнами, элементы пользовательского интерфейса и графический вывод.
  • Уровень гипервизора. Включает всего-навсего один компонент: сам гипервизор. В этой среде нет ни драйверов, ни других модулей. При этом сам гипервизор состоит из нескольких внутренних уровней и служб: собственный диспетчер памяти, планировщик виртуальных процессов, управление прерываниями и таймером, функции синхронизации, разделы (экземпляры виртуальных машин) и внутрипроцессные коммуникации (IPC, Inter-Process Communication) и многие другие.

В таблице ниже представлены некоторые файлы некоторых базовых компонентов Windows:

Имя файла Компоненты
Ntoskrnl.exe Исполнительная система и ядро
Hal.dll HAL
Win32k.sys Часть подсистемы Windows режима ядра (GUI)
Hvix64.exe (Intel), Hvax64.exe (AMD) Гипервизор
.sys в \SystemRoot\System32\Drivers Основные файлы драйверов: DirectX, Volume Manager, TCP/IP и поддержка ACPI
Ntdll.dll Внутренние вспомогательные функции и заглушки диспетчеризации системных сервисных функций
Kernel32.dll, Advapi32.dll, User32.dll, Gdi32.dll Dll основных подсистем Windows

Windows API


Windows API (Application Programming Interface) это программный интерфейс пользовательского режима для Windows. До появления 64-разрядной версии операционной системы программный интерфейс 32-разрядных версий Windows назывался Win32 API в отличие от исходного 16-разрядного Windows API (программный интерфейс для исходных 16-разрядных версий Windows). На данный момент термин Windows API или Win32 API относят как к 32-разрядным, так и к 64-разрядным версиям.

В доисторические времена Windows API состоял только из функций в стиле C. Выбор языка C был обусловлен тем, что написанный на нем код также мог использоваться из других языков. Он являлся достаточно низкоуровневым для предоставления сервиса ОС. Но огромное количество функций в сочетании с недостаточной последовательностью выбора имен и отсутствием логических группировок (вроде пространств имен C++) привели к тому, что в некоторых новых API используется другой механизм модель COM.

COM базируется на двух основных принципах. Во-первых, клиенты взаимодействуют с объектами (серверные объекты COM) через интерфейсы четко определенные контракты с набором логически связанных методов, сгруппированных посредством механизма диспетчеризации по виртуальным таблицам. Такой же механизм, к слову, обычно применяется компиляторами C++ для реализации диспетчеризации виртуальных функций. Таким образом обеспечивается двоичная совместимость и снимаются проблемы с декорированием имен компилятором. Поэтому, такие методы могут вызываться из многих других языков и компиляторов, включая C, C++, VB, языки .NET, Delphi и т. д. Вторым принципом является динамическая загрузка компонентов (вместо статической компоновки с клиентом).

WinRT


В Windows 8 появился новый API и исполнительная среда поддержки Windows Runtime (WinRT). WinRT состоит из платформенных сервисов, предназначенных для разработчиков приложений Windows Apps (приложения Windows Apps подходят для устройств, начиная от миниатюрных IoT-устройств до телефонов, планшетов, десктопных систем, ноутбуков и даже Xbox One и Microsoft HoloLens).

С точки зрения API платформа WinRT строится на базе COM, добавляя в базовую инфраструктуру COM различные расширения. С архитектурной точки зрения она обладает намного большей целостностью: в ней реализованы иерархии пространств имен, последовательная схема назначения имен и паттерны программирования. На базовом двоичном уровне WinRT API все равно строится на основе унаследованных двоичных файлов и API Windows. Это не новый машинный API для системы: ситуация немного напоминает то, как .NET строится на основе традиционного Windows API.

.NET Framework


.NET Framework является частью Windows. Он состоит из двух основных компонентов:

  • CLR (Common Language Runtime). Исполнительная среда .NET, включает JIT-компилятор для преобразования инструкций языка CIL в низкоуровневый язык машинных команд процессора, сборщик мусора, систему проверки типов, безопасность обращения к коду и т. д. Среда реализована в виде внутрипроцессного сервера COM (DLL) и использует различные средства, предоставляемые Windows API.
  • .NET Framework Class Library (FCL). Обширная подборка типов, реализующих функциональность, часто используемую в клиентских и серверных приложениях, средства пользовательского интерфейса, поддержка сети, работа с базами данных и т. д.

На схеме представлены отношения между .NET Framework и ОС Windows:


Отношение между .NET и ОС Windows. Термин сервер COM обычно относится к DLL библиотеке или исполняемому файлу (EXE), в котором реализованы классы COM.
Подробнее..

То, чего не может быть самые необычные версии обычных игровых консолей

17.06.2021 12:20:28 | Автор: admin


Мы с вами давно привыкли к тому, что на протяжении своего жизненного цикла игровые консоли остаются практически неизменными. Да, они подвергаются рестайлингу, выходят в разных комплектациях и лимитированных версиях, приуроченных к громким релизам или юбилею платформы, и даже могут получить более мощную начинку. Однако в общем и целом модификации одной и той же приставки идентичны по функционалу и не имеют принципиальных различий, а ситуации вроде той, что произошла с PlayStation 3, когда последующие ревизии консоли лишились аппаратного блока для запуска игр с PS2, являются скорее исключением, нежели правилом.

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

Теория гибрида: консольные эксперименты Sega


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

Sega SC-3000



8-битный игровой компьютер Sega SC-3000

Первые эксперименты со скрещиванием разнообразных девайсов Sega начала еще в 1983 году, выпустив 8-битный компьютер SC-3000, способный запускать игры для оригинальной домашней системы SC-1000 и позволяющий своему владельцу создавать простенькие программы. Хотя девайс был доступен в ограниченном числе стран (помимо Японии, SC-3000 официально продавался только в Австралии, Франции, Италии, Финляндии и Новой Зеландии), гибридная игровая консоль оказалась достаточно востребованной, показав даже лучшие результаты, чем ее предшественница.

Приставка-компьютер выпускалась в двух основных модификациях: кроме базовой версии, оснащенной встроенной мембранной клавиатурой довольно низкого качества, существовала и более дорогая SC-3000H, получившая полноценную механику.


Sega SC-3000H с механической клавиатурой

По характеристикам Sega SC-3000 и SC-1000 были практически идентичны: оба девайса были построены на базе процессора Zilog Z80A и использовали графическую подсистему от Texas Instruments TMS9918A с 16 КБ видеопамяти. Ключевое отличие между устройствами заключалось в объеме оперативной памяти: Sega SC-3000 несла на борту 2 КБ RAM вместо 1, как у SC-1000.

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

  • BASIC Level II A не добавлял дополнительной памяти;
  • BASIC Level II B добавлял системе 4 КБ RAM и 16 КБ VRAM;
  • BASIC Level III A добавлял 18 КБ RAM и 16 КБ VRAM;
  • BASIC Level III B добавлял 32 КБ RAM и 16 КБ VRAM.

В 1984 году для Sega SC-3000 был выпущен аддон Super Control Station SF-7000, расширяющий возможности оригинального устройства. Super Control Station добавлял компьютеру дополнительные 64 КБ оперативной памяти, 8 КБ ПЗУ, параллельный порт Centronics и последовательный порт RS-232, а также флоппи-дисковод.


Sega SC-3000H с подключенным к ней Super Control Station SF-7000

В отличие от самой гибридной консоли особой популярностью аддон не пользовался, прежде всего из-за дороговизны: на старте за устройство просили $600, то есть, почти вдвое больше, чем за сам SC-3000. Кроме того, дисковод Super Control Station был рассчитан не на доступные 3.5-дюймовые дискеты, а на более редкие 3-дюймовые, что делало данный девайс еще менее привлекательным в глазах конечного пользователя.

Sega Teradrive и Amstrad Mega PC



ПК со встроенной игровой консолью Sega Teradrive

Воодушевившись успехом SC-3000, в 1991 году Sega предприняла очередную попытку скрестить ПК и игровую консоль. На этот раз гибрид, выпущенный эксклюзивно для японского рынка и получивший название Teradrive, объединил в себе IBM PC на базе процессора Intel 80286 и популярную приставку Sega Mega Drive. Всего свет увидели 3 модели Teradrive, отличающиеся друг от друга объемом оперативной памяти и конфигурацией накопителей данных. Топовая модификация Model 3 несла на борту 2.5 МБ RAM, винчестер на 30 МБ и поставлялась с предустановленной операционной системой DOS/V (специальная версия DOS, поддерживающая японские иероглифы). В отличие от обычных IBM PC, системный блок Teradrive имел на корпусе слот под стандартные картриджи Sega Mega Drive.

Два года спустя Sega совместно с Amstrad выпустила более совершенную версию Teradrive, ориентированную на международный рынок и получившую название Mega PC.


Гибридный ПК Amstrad Mega PC

Гибридные ПК, выполненные на базе CPU Motorola 68000 или Intel/AMD 80386sx, оснащались 1 МБ оперативной памяти с возможностью расширения до 16 МБ, жестким диском на 40 МБ и дискретной графической подсистемой от Western Design Center WD90C11A-LR SVGA с 256 КБ VRAM на борту и возможностью расширения до 512 КБ.

И Teradrive, и Mega PC закономерно провалились в продажах. Причиной этого оказалась неадекватная цена за сравнительно слабое даже по тем временам железо. Так, например, имея на руках $2000, которые просили за Mega PC на старте, вы могли собрать куда более мощный ПК с Intel 80486DX, на котором можно было комфортно работать и играть, так что для большинства потребителей покупка такого гибрида попросту не имела смысла. Результат вполне закономерен: оба ПК были сняты с производства уже в год релиза, а анонсированный Amstrad Mega PC Plus на базе Cyrix Cx486SLC так и не увидел свет.

Victor Wondermega и JVC XEye



Игровая консоль с функцией караоке Victor Wondermega RG-M1

С момента изобретения караоке обрело невероятную популярность в Стране восходящего солнца, став неотъемлемой частью японской культуры, поэтому появление консоли Victor Wondermega RG-M1, разработанной Sega совместно c JVC, было лишь вопросом времени. Гибридный аппарат, дебют которого состоялся в 1992 году, объединял в себе игровую консоль Sega Mega Drive, аддон Mega CD и продвинутый аудиоплеер, оснащенный высококачественным цифровым сигнальным процессором от JVC, MIDI-выходом и парой 3.5-миллиметровых разъемов Audio Jack для подключения микрофонов.

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


Дисковод Wondermega получил стильную светодиодную подсветку

Дело в том, что все модификации Wondermega поддерживали работу с Sega 32X, однако из-за особенностей конструкции консоли аддон попросту блокировал крышку дисковода после подключения. Чтобы решить эту проблему, Victor предложила покупателям услугу по апгрейду консоли, который сводился к замене стандартной крышки CD-ROM на специальную версию с боковым вырезом. Воспользоваться данным сервисом можно было вплоть до 2013 года, то есть, даже спустя 20 лет после релиза аппарата.


После установки аддон Sega 32X блокировал дисковод консоли

Через год JVC выпустила обновленную версию Victor Wondermega RG-M2. Вторая модель подверглась рестайлингу (однако проблема с Sega 32X так и не была решена) и получила поддержку беспроводных геймпадов, подключаемых к приставке с помощью фронтального ИК-порта. Впрочем, при желании владельцы консоли могли использовать и классические контроллеры, благо порты DE-9 никуда не делись, а лишь были перемещены на заднюю панель.


Wondermega RG-M2 с беспроводным геймпадом

Весной 1994 года свет увидела специальная версия Wondermega RG-M2 для США разработкой и дистрибуцией которой занималась уже сама JVC. Устройство получило название XEye и во многом уступало оригиналу. Первая партия консолей лишилась ИК-порта для беспроводных геймпадов и разъема S-Video, место которого занял 9-контактный AV-порт, а более поздние модификации приставок JVC XEye остались без поддержки аддона Sega 32X.


JVC XEye для американского рынка

Увы, проект Wondermega оказался провальным, причем как и в случае с Teradrive и Mega PC, главной причиной фиаско оказалась ценовая политика компании: купить Sega Mega Drive и Sega CD было куда дешевле, нежели чудо-агрегат от JVC (70 тысяч йен против 82 тысяч), а музыкальные системы с поддержкой караоке и так были у большинства поклонников данного развлечения.

Aiwa СSD-GM1



Игровой бумбокс Aiwa СSD-GM1

Плодом сотрудничества Sega с японской компанией Aiwa, специализирующейся на производстве бытовой электроники, стала портативная стереосистема Aiwa СSD-GM1, выпущенная ограниченным тиражом в Японии в 1994 году. Девайс вышел во всех отношениях странным: если покупатель той же Wondermega получал в свое распоряжение универсальный аппарат в монолитном корпусе, то в случае с СSD-GM1 консоль Sega Mega Drive была вынесена в независимую док-станцию, подключаемую к основному устройству с помощью идущего в комплекте кабеля. Сам же бумбокс заменял собой Sega CD, позволяя запускать на Mega Drive игры на оптических дисках.


Док-станция и стереосистема соединялись с помощью кабеля

Как и в случае с Wondermega, Aiwa СSD-GM1 поддерживала Sega 32X, так что владелец системы мог подключить этот аддон, чтобы поиграть в немногочисленные эксклюзивы и улучшенные версии оригинальных проектов для Mega Drive. Однако из-за неудачного расположения слота для картриджей, в который вставлялся Sega 32X, собранный воедино девайс необходимо было каждый раз класть набок.

Хотя Aiwa СSD-GM1 была не особо востребована среди геймеров на релизе, сейчас этот артефакт весьма ценится коллекционерами: найти полный комплект на том же eBay совсем непросто, а если таковой и удастся отыскать, будьте готовы выложить за него не менее 5 тысяч долларов.

Hitachi Hi-Saturn MMP-1000NV



Hi-Saturn MMP-1000NV игровая консоль для автомобилистов

В числе стратегических партнеров Sega, получивших права на создание собственных версий игровой консоли Saturn, была и Hitachi, занимавшаяся разработкой процессора и дисковода для игровой консоли. Среди нескольких моделей приставок, выпущенных японской корпорацией, особого внимания заслуживает Hi-Saturn MMP-1000NV, также известная под именем Game & Car Navi Hi-Saturn.

Как можно догадаться по расширенному названию, данный аппарат был ориентирован на геймеров-автолюбителей. К MMP-1000NV можно было подключить портативный ЖК-монитор, ТВ-тюнер и спутниковый навигатор с поддержкой картографического ПО Naviken, которые продавались отдельно, и запитать систему через прикуриватель с помощью специального адаптера. Также Hi-Saturn можно было использовать и в качестве аудиосистемы с функцией караоке, причем эта версия приставки обзавелась полноценным мультиплексором.


Реклама Hi-Saturn MMP-1000NV на развороте японского журнала

Несмотря на богатый функционал, Hi-Saturn MMP-1000NV не пользовалась особой популярностью, ведь даже за ее базовую версию просили почти $1500, тогда как за портативный дисплей пришлось бы отдать еще $440. Стоит ли возможность скоротать время за видеоиграми в пробке подобных затрат? Для большинства автомобилистов ответ на этот вопрос оказался вполне очевиден.

Fuji Divers 2000 CX-1



Игровой телевизор Fuji Divers 2000 CX-1 со встроенной Sega Dreamcast

В мае 2000 года Sega сделала невозможное, выпустив совместно с Fuji практически идеальный геймерский телевизор, обладать которым мечтал, наверное, каждый фанат Dreamcast. Речь идет об уникальном моноблоке Fuji Divers 2000 CX-1, выполненном в форме головы бессменного маскота Sega ежика Соника, главного героя серии игр Sonic the Hedgehog.


Корпус Fuji Divers 2000 CX-1 напоминал голову ежика Соника

Помимо футуристичного дизайна, Fuji Divers 2000 CX-1 отличался весьма богатым функционалом. Это был отнюдь не просто моноблок со встроенной игровой консолью, а самый что ни на есть полноценный Smart TV! Телевизор имел полный набор сетевых интерфейсов и поддерживал работу с клавиатурой и веб-камерой Dreameye, которые поставлялись в комплекте, благодаря чему его владелец мог с комфортом серфить в Интернете и даже участвовать в видеоконференциях, используя фирменное ПО Dream Passport 3 от Sega.

Счастливые обладатели Fuji Divers 2000 CX-1 также отмечали превосходное качество цветного 14-дюймового кинескопа и встроенных стереодинамиков, что помогало с головой погрузиться в игровой процесс.


Комплектация Fuji Divers 2000 CX-1 была весьма богатой

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

Sega Fish Life



Виртуальный аквариум Sega Fish Life

Однако самой дорогой модификацией Dreamcast оказался аппарат под названием Sega Fish Life, предназначенный для запуска всего одной игры. Специальная версия консоли вышла в 2000 году и была ориентирована на рестораны и отели. В комплект стоимостью 6 тысяч долларов США, помимо самой приставки, входил разработанный Sega 15-дюймовый сенсорный LCD-монитор.

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


Совсем как настоящая, ведь правда?

Увы, Fish Life оказался слишком дорог даже для предприятий HoReCa. В общей сложности Sega удалось реализовать около 300 комплектов оборудования, а большую часть тиража впоследствии пришлось утилизировать.

Страсти по Nintendo: инновации, интриги и предательства


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

Sharp Famicom Titler



Famicom Titler самая навороченная 8-битная консоль

Если вы полагаете, что выпуск PRO-версий консолей на закате поколения является ноу-хау Sony, то жестоко ошибаетесь: именно Nintendo стала первопроходцем на этом поприще, представив в 1989 году игровую систему Famicom Titler прокаченную версию оригинальной NES, разработанную совместно с Sharp.

На момент релиза Famicom Titler являлась самой технологичной 8-битной игровой системой из представленных на рынке, поистине опередив свое время. Консоль умела генерировать RGB-видеосигнал, выводя на телевизор картинку, сопоставимую по сочности и четкости с изображением, генерируемым игровыми автоматами на базе PlayChoice-10. В свою очередь, полный набор видео- и аудио- входов и выходов, среди которых присутствовал и S-Video, позволял подключать к приставке камкордер и видеомагнитофон, записывать игровой процесс на видеокассеты и использовать Famicom Titler для видеомонтажа.


Задняя панель Famicom Titler

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

Ничего не напоминает? Фактически Sharp Famicom Titler предвосхитил появление социальных сетей, сторис и летсплеев. Другое дело, что в конце 80-х все это являлось лишь смутным концептом: тогда мало кто мог себе представить, что в будущем сетевые технологии позволят передавать файлы и потоковое видео на колоссальные расстояния, а ведь именно возможности свободного обмена информацией между людьми и не хватало для популяризации такого функционала. Как следствие, в своих последующих игровых системах Nintendo отказалась от дальнейших экспериментов подобного толка, всецело сосредоточившись на совершенствовании игровых систем.

Sharp Famicom TV C1, Sharp NES TV и Sharp SF-1 SNES TV



Игровой телевизор Sharp Famicom TV C1

В отличие от Sega, Nintendo никогда не стремилась скрестить бульдога с носорогом. Очевидно, менеджеры компании прекрасно понимали, что аудитории пользователей ПК и домашних консолей мало пересекаются, а создать производительный и, вместе с тем, доступный комбайн, который бы устроил всех, в принципе невозможно. Идея же игрового телевизора напрашивалась сама собой, так что появление проекта Sharp Nintendo Television не заставило себя долго ждать.

Первой ласточкой в рамках данной концепции стал Sharp Famicom TV C1, вышедший в 1983 году и продававшийся в Японии под официальным названием My Computer TV C1. Линейка цветных телевизоров со встроенной NES включала в себя две модели с диагональю экрана 14 и 19 дюймов. Моноблоки поставлялись с предустановленными приложениями JR GRAPHIC и TV NOTE. Также к каждому телевизору прилагался мультикартридж с урезанными версиями игр Donkey Kong Jr. и Donkey Kong Jr. Math. На тот момент это был единственный лицензионный мультикартридж для игровой системы Famicom.

Релиз версии Sharp Famicom TV C1 для США, получившей название Sharp NES TV, состоялся в августе 1989 года. Игровые телевизоры получили обновленный дизайн корпуса, который оказался не самым удачным: пластиковые ножки, призванные обеспечить устойчивость системы, были слишком хрупкими и быстро ломались.


Sharp NES TV отличался весьма хрупкими ножками

В следующем поколении консолей Nintendo вновь обратилась к Sharp, и в 1990 году свет увидели две модели игровых телевизоров Super Famicom Naizou TV SF1 (SF1 SNES TV) с диагональю экрана 14 и 21 дюйм. За пределами Японии моноблоки официально не продавались.


Телевизор Super Famicom Naizou TV SF1 со встроенной SNES

С проектом Sharp Nintendo Television связан интересный факт. В силу технологических особенностей моноблоки выдавали заметно лучшую картинку, нежели обычные NES и SNES. Из-за этого в рекламе и пресс-релизах Nintendo использовала скриншоты, полученные именно с помощью игровых телевизоров, поскольку они смотрелись значительно эффектнее. Конечно, подобное лукавство не идет ни в какое сравнение с современными махинациями с пререндеренными сценами, выдаваемыми за реальный геймплей, однако даже здесь Nintendo оказалась законодателем мод.

SNES-CD (Nintendo PlayStation)



Полумифическая Nintendo PlayStation во плоти

Долгое время SNES-CD окутывал ореол таинственности. Вокруг этого устройства ходило немало слухов и спекуляций, а многие эксперты и коллекционеры и вовсе считали консоль выдумкой. Так продолжалось вплоть до 2015 года, когда наконец был обнаружен единственный уцелевший из 200 выпущенных прототипов уникальной приставки, созданной ныне непримиримыми конкурентами Sony и Nintendo.

Обретение SNES-CD иначе, как чудесным, не назовешь. Один из бывших сотрудников Advanta Corporation, Терри Диболд, после банкротства компании выкупил часть ее имущества, выставленного на аукцион, всего за $75. Среди приобретенного по дешевке хлама было и нечто весьма необычное, больше похожее на фейк. Странная находка представляла собой игровую консоль, оснащенную слотом для картриджей и CD-приводом, и идущую в комплекте с геймпадом, облик которого один в один повторял дизайн стандартного контроллера для SNES. Вот только на всем перечисленном гордо красовалась надпись PlayStation.


SNES-CD в сравнении с оригинальными Super Famicom и PlayStation

А история была такова. Понимая, насколько перспективным может оказаться использование оптических дисков в качестве носителей данных для игровых консолей, Nintendo в конце 80-х стала искать партнера для реализации подобного проекта, обретя его в лице Sony. В 1989 году две японских компании начали разработку модификации SNES со встроенным CD-приводом, получившую кодовое название PlayStation. Официальный анонс инновационной игровой системы состоялся 28 мая 1991 года в рамках выставки Consumer Electronics Show, прошедшей в Чикаго, став чуть ли не главной сенсацией мероприятия. А уже на следующий день Nintendo объявила о прекращении сотрудничества с Sony и заключении договора с голландской компанией Philips.

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


Кен Кутараги, отец PlayStation и главный интриган игрового подразделения Sony

Превентивный удар, призванный выбить почву из под ног потенциального конкурента, возымел диаметрально противоположный эффект. Тогдашний президент Sony, Норио Ога, после такого публичного предательства буквально впал в ярость, чем и воспользовался Кен, убедив начальство, что компания сможет без труда отомстить бывшему партнеру. Норио дал добро на создание игрового подразделения Sony Computer Entertainment, которое и возглавил Кутараги, а всего через 3 года состоялся релиз первой Sony PlayStation.

В то же время Nintendo и Philips так и не сумели довести задуманное до конца, в итоге прекратив сотрудничество. А единственным живым напоминанием об этом проекте осталась мертворожденная Nintendo PlayStation, стоимость которой ныне составляет около 300 тысяч долларов США именно за такую цену уникальная консоль была продана на аукционе Heritage, прошедшем в 2020 году в Далласе (штат Техас), некоему коллекционеру, пожелавшему остаться неизвестным.

iQue Player



iQue Player лицензионная китайская консоль Nintendo

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

iQue Player представляла собой локализованную версию Nintendo 64 для рынка Китая, выпускаемую компанией iQue, основанной тайваньским инженером Вэй Йеном при участии японского игрового гиганта. Консоль представляла собой массивный геймпад, внутри которого была спрятана вся аппаратная начинка. Как и оригинал, iQue Player использовал SoC R-4300i, однако из-за различий в архитектуре и прошивке, что было следствием миниатюризации устройства, приставка не могла запускать игры для оригинальной Nintendo 64.

Еще одна интересная особенность iQue Player заключалась в необычном подходе к дистрибуции видеоигр. Последние распространялись через своеобразный аналог современного Nintendo eShop iQue Club. Поскольку на физических носителях игры для этой консоли не выходили вообще, а интернет (не говоря уже о eCommerce) тогда еще не был достаточно развит, геймерам приходилось посещать киоски iQue Depot, где они могли приобрести скретч-карты iQue Ticket с кодами активации понравившихся игр, подключить консоль к сети и скачать дистрибутив на проприетарную флеш-карту iQue Card объемом 64 МБ, заменявшую собой классический картридж (одна из них поставлялась в комплекте с приставкой).

Счастливые обладатели домашнего интернета могли воспользоваться веб-сервисом iQue@Home, чтобы приобрести и загрузить желаемые игры онлайн. С одной оговоркой: для этого было необходимо иметь еще и ПК под управлением 32-разрядной системы Windows, поскольку сама iQue Player выходить в сеть не умела, а драйверы для консоли не поддерживали 64-битные версии ОС.

Все эти сложности закономерно обеспечили приставке оглушительный провал: iQue Player продалась рекордно низким тиражом около 12 тысяч устройств, при этом для нее было портировано всего 14 игр из богатой игротеки Nintendo 64, что также не способствовало росту популярности девайса. Последним релизом на злополучной игровой системе стала Animal Crossing, вышедшая в 2006 году.

Panasonic Q



Мультимедийный центр Panasonic Q шикарный и недоступный

В отличие от прямых конкурентов в лице PlayStation 2 и Xbox, Nintendo GameCube нельзя было использовать в качестве домашнего мультимедийного центра. Поскольку консоль поддерживала исключительно проприетарные оптические диски стандарта mini-DVD, эту проблему было невозможно решить одним лишь обновлением прошивки: полноразмерные накопители попросту не влезали в миниатюрный дисковод.

Чтобы исправить это недоразумение, Nintendo заключила партнерский договор с Panasonic, благодаря чему в конце 2001 года на свет появился мультимедийный центр Panasonic Q. Цельнометаллический девайс, напоминавший по своему дизайну этакий SWAG-осциллограф, поддерживал воспроизведение DVD, VCD, Audio CD и MP3 CD, включая самописные, также позволяя запускать любые игры для оригинального GameCube и Game Boy с помощью модифицированной версии Game Boy Player Q (оригинальное устройство не подходило к станции Panasonic из-за форм-фактора). Кроме всего прочего, Panasonic Q мог похвастаться качественной аудиоподсистемой, поддерживал Dolby Digital 5.1 и DTS, и имел отдельный выход под сабвуфер.

Благодаря всему перечисленному гибридная консоль пользовалась (и продолжает пользоваться) популярностью среди коллекционеров, однако рядовые покупатели не оценили столь роскошный агрегат: по всему миру было реализовано менее 100 тысяч устройств. В результате Panasonic Q был снят с производства уже в конце 2003-го года, всего через пару лет после релиза.

Заурядная экзотика: нестандартные консоли от Sony


На фоне конкурентов Sony выглядит несколько консервативно. В портфолио компании практически нет устройств, при виде которых хочется воскликнуть: Да кто вообще мог такое придумать?. Тем не менее и в линейке PlayStation присутствуют несколько не совсем обычных консолей, заслуживающих упоминания в рамках данного материала.

Sony PlayStation Hi-Fi Edition



Аудиофильская PlayStation это как обычная PlayStation, только аудиофильская

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

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

Вступая на доселе неизведанную территорию игровой индустрии компания самую малость перестраховалась, установив в первые модели PlayStation (а это SCPH1000 для реализации на территории Японии, SCPH1001, ориентированные на американский рынок, и SCPH1002, предназначенные для продаж в Евросоюзе) топовый 16-битный сигма-дельта ЦАП АКМ AK4309AVM от Ashahi Kasei Microsystems и оснастив консоль полноценными RCA-разъемами, обеспечивающими превосходное качество передачи аудиосигнала.

В результате домашняя игровая приставка за $400 зазвучала значительно лучше, чем аудиосистемы за несколько тысяч долларов США. Благодаря этому первые ревизии PlayStation остаются востребованными среди меломанов и по сей день: поскольку таких устройств было выпущено достаточно много, приобретение б/у консоли все еще остается самым доступным способом приобщиться к миру Hi-Fi.

Sony PlayStation One



Бандл PlayStation One Combo

В 2000 году, на закате поколения, Sony выпустила компактную версию своей первой игровой консоли, получившую название PlayStation One. Данная модель отличалась от толстушки существенно меньшими габаритами (38 193 144 мм против 45 260 185 мм), улучшенной прошивкой и отсутствием последовательного порта для локального мультиплеера. Однако главной фишкой PlayStation One являлась возможность подключения 5-дюймового жидкокристаллического дисплея со встроенными динамиками, который можно было приобрести как в составе бандла PSone COMBO, так и отдельно.


LCD-дисплей для PSone можно было приобрести и отдельно от консоли

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



ЖК-дисплей PlayStation One в сложенном состоянии

Также Sony выпустила специальные блоки питания для подключения приставки к автомобильному прикуривателю и адаптер, позволяющий выводить консоль в Интернет посредством сотового телефона своеобразный мобильный гейминг начала нулевых. Однако перечисленные девайсы практически не продавались за пределами Японии: хотя PlayStation One и показала отличные результаты, обойдя на старте продаж не только предыдущие ревизии оригинальной консоли, но и свою старшую сестру, PlayStation 2, большинство геймеров продолжали воспринимать приставку сугубо в качестве домашней игровой системы.

Sony PSX



Мультимедийная игровая станция Sony PSX

Аббревиатура PSX является не только кодовым именем первого поколения игровых приставок Sony, но и названием уникального устройства 3 в 1, релиз которого состоялся в декабре 2003 года. PSX представлял собой продвинутый CD/DVD-плеер, оснащенный винчестером на 160 ГБ (5000-я серия) или 250 ГБ (7000-я серия), и способный запускать игры для PlayStation и PlayStation 2 благодаря 90-нанометровому гибридному чипу EE+GS, объединявшему в себе, как можно догадаться по названию, Emotion Engine и Graphics Synthesizer для оригинальных приставок.

Мультимедийный комбайн был совместим со всеми официальными геймпадами и картами памяти, выпускаемыми Sony, а старшие модели PSX, DESR-5700 и DESR-7700, также получили поддержку синхронизации с PlayStation Portable, что позволяло переносить между устройствами фото, видео и музыкальные файлы. К сожалению, за пределами Японии PSX не продавались.

Sony BRAVIA KDL22PX300



Телевизор Sony BRAVIA KDL22PX300 со встроенной PlayStation 2

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

Sony BRAVIA KDL22PX300 с диагональю 22 дюйма и разрешением 720p (1366 на 768 пикселей) вышел в 2010 году. От собратьев игровой телевизор отличало массивное основание, являющееся ничем иным, как модифированной игровой консолью PlayStation 2. Помимо игр, встроенная приставка поддерживала воспроизведение CD, CD-R, CD-RW, DVD, DVD-R и DVD-RW. Добавьте сюда возможность просматривать видеозаписи в большинстве популярных форматов непосредственно с флешки, а также поддержку IPTV, и на выходе вы получите универсальную мультимедийную систему, способную стать отличным приобретением даже сегодня. Тем более, что на вторичном рынке этот девайс все еще можно найти по адекватной цене.



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Перевод Руководство по пограничным вычислениям для архитектора. Самое важное

20.06.2021 12:09:43 | Автор: admin
Для современного энтерпрайз-архитектора критически важно разбираться в пограничных вычислениях (edge computing). В этой статье будут рассмотрены основы пограничных вычислений и приведены примеры использования этой технологии на практике.



Пограничные вычисления определенно существенная часть современного технологического ландшафта. Объем рынка для продукции и услуг, связанных с пограничными вычислениями, более чем удвоился с 2017 года. Причем, согласно статистическому сайту Statista, к 2025 году ожидается взрывной рост этого рынка (см. рисунок 1 ниже).


Рисунок 1: Объем мирового рынка пограничных вычислений: текущая ситуация и прогноз на 2025 год (в миллиардах долларов США) (Statista)

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

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

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

Для начала опишу, на чем основан паттерн пограничных вычислений.

Понятие о паттерне пограничных вычислений


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

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


Рисунок 2: Сеть муниципальных светофоров как образец паттерна пограничных вычислений

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

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


Рисунок 3: Сети доставки контента одна из ранних реализаций пограничных вычислений в распределенных системах

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

Базовое ценностное предложение


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

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


Рисунок 4: Онлайновая розница пример пограничных вычислений в физическом мире.

В сценарии, показанном выше, Бобби из Лос-Анджелеса покупает подарок для своего друга Билли, живущего в Нью-Йорке. Между Бобби и Билли почти четыре тысячи километров. У Acme Online есть несколько физических складов, рассредоточенных по всей стране. Один из складов находится в Лос-Анджелесе. Другой в Нью-Йорке. После того, как Бобби совершит покупку, информационная система, занятая обработкой покупок в центральном датацентре Acme Online, принимает заказ и анализирует его, чтобы определить самый быстрый и дешевый способ доставки подарка. Acme Online соотносит адрес Билли получателя подарка с ближайшим складом, на котором есть нужный товар. Затем Acme Online назначает доставку с нью-йоркского склада.

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

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

Туман и край: разница


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

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

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

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

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

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

Рассмотрим специфику.

Паттерны реализации искусственного интеллекта при применении пограничных вычислений


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

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

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

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


Рисунок 5: В стандартных облачных вычислениях ИИ находится в облаке

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


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

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

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

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

В данной ситуации будет уместен паттерн край/туман/облако, как показано на рисунке 7 ниже.


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

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

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

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

Итоги


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

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



VPS серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

17 интересных (и забавных) API для вашего проекта

17.06.2021 10:17:44 | Автор: admin


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


Numbers


Страница: http://numbersapi.com/


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


number


Также есть факты о датах и годах.


Использование API: http://numbersapi.com/<number>/<type>, где number число, а type тип факта (trivia факт из жизни, math математический факт, date и year вопрос про дату (в формате MM/DD) и год). Например, получить факт о 25 октября можно по
запросу http://numbersapi.com/10/25/date: October 25th is the day in 1760 that George III becomes King of Great Britain.


С помощью этого сервиса можно сделать виджет "Этот день в истории".


База вопросов для викторин


Сайт: http://jservice.io


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


Получение вопроса: http://jservice.io/api/random?count=1


Пример вопроса (фрагмент):


{  "id": 92576,  "answer": "7",  "question": "When writing, many Europeans cross it; most Americans don't",  "value": 800,  "airdate": "2008-12-08T12:00:00.000Z",  "created_at": "2014-02-14T01:58:09.356Z",  "updated_at": "2014-02-14T01:58:09.356Z",  "category_id": 8472,  "game_id": null,  "invalid_count": null,  "category": {    "id": 8472,    "title": "a number from 1 to 10",    "created_at": "2014-02-11T23:28:10.844Z",    "updated_at": "2014-02-11T23:28:10.844Z",    "clues_count": 10  }}

Чем заняться, когда скучно


Документация: http://www.boredapi.com/documentation


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


>> http://www.boredapi.com/api/activity?minparticipants=5&maxparticipants=8  - поиск активности с участием от 5 до 8 человек{"activity":"Play basketball with a group of friends","type":"social","participants":5,"price":0,"link":"","key":"8683473","accessibility":0.7}```>> http://www.boredapi.com/api/activity?type=diy  - выбор из категории DIY{"activity":"Learn woodworking","type":"diy","participants":1,"price":0.3,"link":"","key":"9216391","accessibility":0.3}

Колода карт


Примеры: https://deckofcardsapi.com


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


>> https://deckofcardsapi.com/api/deck/new/shuffle/?deck_count=3  - создание стопки карт из 3 колод по 52 карты"deck_id": "c3hmntqq4wne", "remaining": 156>> https://deckofcardsapi.com/api/deck/c3hmntqq4wne/draw/?count=1  - достать и вернуть одну карту{"code": "6D", "image": "https://deckofcardsapi.com/static/img/6D.png", "value": "6", "suit": "DIAMONDS"}

В репозитории проекта вы можете найти картинки карт и мастей, которые можно использовать в своих проектах: https://github.com/crobertsbmw/deckofcards/tree/master/static/img


6d


http.cat


Домашняя страница: https://http.cat


Здесь собраны картинки с котиками для каждого HTTP кода, с помощью которых можно украсить страницы с ошибками. Получить соответствующую картинку можно по запросу https://http.cat/<status_code>. Также исходные файлы находятся в открытом репозитории на Github, поэтому вы можете скачать все картинки из папки https://github.com/httpcats/http.cat/tree/master/public/images-original и использовать их локально.


Мои любимые картинки:




Интересный факт: домен .cat выделен под сайты на каталонском языке, но люди из интернета нашли и ему ещё одно применение, и теперь на нём создаются сайты про котов.


Поиск аниме


Документация: https://kitsu.docs.apiary.io/#introduction/json:api


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


Пример работы с API: поиск всех результатов, содержащих слово Tokio: https://kitsu.io/api/edge/anime?filter[text]=tokio


Один из результатов (убраны некоторые аттрибуты):


{  "id": "8271",  "type": "anime",  "attributes": {    "synopsis": "Tokyo has become a cruel and merciless citya place where vicious creatures called ghouls...",    "titles": {      "en": "Tokyo Ghoul",      "en_jp": "Tokyo Ghoul",      "en_us": "Tokyo Ghoul"    },    "canonicalTitle": "Tokyo Ghoul"  },  "favoritesCount": 3599,  "popularityRank": 30,  "ageRating": "R",  "posterImage": {    "original": "https://media.kitsu.io/anime/poster_images/8271/original.jpg?1597694836"  },  "episodeCount": 12,  "episodeLength": 24,  "totalLength": 288}

Картинки с котами, собаками и лисами


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


Картинки котиков


https://aws.random.cat/meow возвращается только ссылка на картинку или гифку. Ссылки случайные и ведут на сторонние сервера.


Каждая картинка из базы имеет номер, но API не даёт номер, а получить картинку по номеру только открыв сайт https://aws.random.cat/view/<number> и достать картинку из исходного текста страницы. Совсем простенький скрипт для этого (для более быстрого исполнения я не использую re):


from requests import getnum = int(input())source = get(f"https://aws.random.cat/view/{num}").textif "id=\"cat" in source:    print(source.split("src=\"")[1].split("\"")[0])else:    print("Incorrect id")

Картинки собачек


https://random.dog/woof.json возвращает размер картинки (или gif или видео) в байтах и ссылку на неё. Все ссылки выглядят как https://random.dog/<random name>


Другие варианты запросов:


  • /woof получить название какой-нибудь случайной картинки (случайная строка)
  • /doggos названия всех картинок
  • /upload предложить свою картинку.

Картинки лисичек


https://randomfox.ca/floof/ возвращает 2 варианта ссылки на картинку. Пути к картинкам выглядят так: https://randomfox.ca/images/<id>.jpg, где id число от 1 до 121. Также все картинки есть в репозитории: https://github.com/xinitrc-dev/randomfox.ca/tree/master/images. Это позволяет вручную выбрать интересные картинки и использовать только их.



База знаний о Покемонах


Страница: https://pokeapi.co


Этот сервис позвляет легко получить информацию о покемонах, их способностях и типах, а также о контенте про покемонов: играх и прочем.


Формат запросов: https://pokeapi.co/api/v2/<type>/<name or id>, например https://pokeapi.co/api/v2/ability/2.


Фрагмент из ответа на запрос информации про Ditto (https://pokeapi.co/api/v2/pokemon/ditto):



{  "abilities": [    {      "ability": {        "name": "limber",        "url": "https://pokeapi.co/api/v2/ability/7/"      },      "is_hidden": false,      "slot": 1    },    {      "ability": {        "name": "imposter",        "url": "https://pokeapi.co/api/v2/ability/150/"      },      "is_hidden": true,      "slot": 3    }  ]}

Be like Bill


Репозиторий: https://github.com/gautamkrishnar/Be-Like-Bill


В меме "Be like Bill" описываются какие-то положительные качества Билла и в конце звучит рекомендация "Быть как он". API позволяет генерировать картинки с заданными или рандомными текстами.


Примеры:


  • https://belikebill.ga/billgen-API.php?default=1&name=Otter случайное качество для имени Otter


  • https://belikebill.ga/billgen-API.php?text=Use%20this%20power%20wisely заданный текст


Bakon Ipsum


Документация: https://baconipsum.com/json-api/


Сервис позволяет использовать сгенерированные тексты вместо стандартного Lorem Ipsum. Можно задать количество предложений или параграфов. Пример запроса:


https://baconipsum.com/api/?type=all-meat&sentences=2 создание текста с 2 предложениями.


Tenderloin short ribs pork chop shankle chuck, cow boudin tongue. Pancetta pastrami pork loin beef ribs, cupim tenderloin filet mignon corned beef.

Шутки


Под эту категорию подходит сразу несколько сервисов с похожими API, поэтому приведу только примеры запросов к API:


Гиковские шутки (часто про Чака Норриса): https://geek-jokes.sameerkumar.website/api?format=json:
There are only 10 types of people in the world: those that understand binary and those that don't.


Шутки от папы: curl https://icanhazdadjoke.com:
Why do you never see elephants hiding in trees? Because they're so good at it.


Шутки с конструкцией сетап-панчлайн: https://official-joke-api.appspot.com/random_joke:
"setup":"I couldn't get a reservation at the library...","punchline":"They were fully booked."


Yes or No


Главная страница: https://yesno.wtf


Если вы хотите красиво отвечать на вопросы да или нет, то этот сервис идеально подходит для вас. Для получения рандомной гифки можно использовать https://yesno.wtf/api. Ответом вам будет "answer" и ссылка на гиф. Также с шансом 1 к 10000 может выпасть ответ maybe. Чтобы получить определённый ответ, можно указать ?force=<answer>, параметр answer может
быть yes, no или maybe.



Вся информация о мультсериале "Рик и Морти"


Главная страница: https://rickandmortyapi.com


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


import ramapiprint(ramapi.Character.filter(name='morty',                              status='dead'))  # получить список мёртвых персонажей, у которых в имени есть Морти. 

Фрагмент ответа


{  "results": [    {      "id": 43,      "name": "Big Morty",      "status": "Dead",      "species": "Human",      "gender": "Male",      "image": "https://rickandmortyapi.com/api/character/avatar/43.jpeg",      "episode": [        "https://rickandmortyapi.com/api/episode/28"      ]    }  ]}

Выводы


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




Облачные серверы от Маклауд быстрые и безопасные.


Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!


Подробнее..

Категории

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

  • Имя: Билал
    04.12.2024 | 19:28
  • Имя: Murshin
    13.06.2024 | 14:01
    Нейросеть-это мозг вселенной.Если к ней подключиться,то можно получить все знания,накопленные Вселенной,но этому препятствуют аннуннаки.Аннуннаки нас от неё отгородили,установив в головах барьер. Подр Подробнее..
  • Имя: Макс
    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-2025, personeltest.ru