-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51

Habrahabr/New








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/new/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-new-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

[Перевод] Что известно об атаке на цепи поставок CCleaner

Пятница, 29 Сентября 2017 г. 14:20 + в цитатник
Cloud4Y сегодня в 14:20 Администрирование

Что известно об атаке на цепи поставок CCleaner

  • Перевод


Атаки на цепи поставок (supply-chain attacks) — очень эффективный способ распространения вредоносного программного обеспечения в целевые организации. Это связано с тем, что при атаках на цепи поставок злоумышленники пользуются доверительными отношениями между производителем/поставщиком и клиентом, чтобы атаковать организации и отдельных лиц по различным мотивам. Червь Petya/Nyetya/NePetya, который был выпущен в сеть в начале 2017 года, показал насколько масштабны эти типы атак. Часто, как и в случае с Petya, исходный вектор атаки может оставаться скрытым в течение некоторого времени.

Недавно исследователи Talos заметили случай, когда серверы загрузки, используемые компанией-разработчиком для распространения легитимного пакета программного обеспечения, были использованы для загрузки вредоносного ПО на компьютеры ничего неподозревающих жертв. В течение некоторого периода версия CCleaner 5.33, распространяемая Avast, содержала многоступенчатую вредоносную нагрузку. 5 миллионов новых пользователей загружают CCleaner в неделю. Учитывая потенциальный ущерб, который может быть вызван сетью зараженных компьютеров подобного размера, решено было действовать быстро. 13 сентября 2017 года Cisco Talos уведомила Avast. В следующих разделах будут обсуждаться конкретные детали, касающиеся этой атаки.

Технические подробности


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


Рисунок 1: Снимок экрана CCleaner 5.33

13 сентября 2017 года, проведя бета-тестирование клиентов новой технологией обнаружения эксплойтов, Cisco Talos обнаружила исполняемый файл, который определялся как вредоносная программа. Это был установщик CCleaner v5.33 с легитимных серверов загрузки CCleaner. Talos провёл первоначальный анализ, чтобы определить, что заставляет систему защиты блокировать CCleaner. Они определили, что, хотя загруженный исполняемый файл был подписан с использованием действительной цифровой подписи Piriform, CCleaner не был единственным приложением, которое было загружено. Во время установки CCleaner 5.33 32-разрядный двоичный файл CCleaner, также содержал вредоносную нагрузку с возможностью использовать алгоритм генерации домена (DGA), а также функции выдачи команд и управления (Command and Control — C2).

При просмотре страницы истории версий на сайте загрузки CCleaner выяснилось, что версия (5.33) была выпущена 15 августа 2017 года. 12 сентября 2017 года была выпущена версия 5.34. Версия, содержащая вредоносную нагрузку (5.33), распространялась между этими датами. Эта версия была подписана с использованием действительного сертификата, который был выпущен для компании Piriform Ltd, которую недавно приобрел Avast, компанией Symantec и действителен до 10.10.2018.


Рисунок 2: Цифровая подпись CCleaner 5.33

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

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

В двоичном коде CCleaner был найден следующий артефакт компиляции:
S:\workspace\ccleaner\branches\v5.33\bin\CCleaner\Release\CCleaner.pdb

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

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

Процесс установки вируса


'__scrt_get_dyn_tls_init_callback' был изменен для запуска кода CC_InfectionBase (0x0040102C) с целью перенаправления потока выполнения кода на вредоносный до продолжения обычных операций CCleaner. Вызываемый код отвечает за дешифрование данных, которые содержат два уровня вредоносной нагрузки: загрузчик PIC (позиционно-независимого программного кода) и DLL-файл.

Используя HeapCreate (HEAP_CREATE_ENABLE_EXECUTE, 0,0), создаётся исполняемая куча. Содержимое расшифрованных данных, содержащих вредоносное ПО, копируется в кучу, исходные данные стираются. Затем вызывается PE-загрузчик и начинается его работа. Как только процесс заражения начался, двоичный код стирает области памяти, в которых ранее содержался PE-загрузчик и DLL-файл, освобождает ранее выделенную память, уничтожает кучу и продолжает исполняться с обычными операциями CCleaner.

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

CCBkrdr_GetShellcodeFromC2AndCall отвечает за многие из вредоносных операций, обнаруженные Talos при анализе этого вредоносного ПО. Во-первых, он записывает текущее системное время. Затем он задерживает выполнение вредоносного кода на 601 секунду, вероятно, с целью уклониться от автоматизированных систем анализа, которые настроены на проверку ПО на вирусы в отладчике в течение предопределенного периода времени. Чтобы реализовать эту функцию задержки, вредоносное ПО вызывает функцию, которая пытается выполнить ping 224.0.0.0 с использованием таймаута delay_in_seconds, установленного на 601 секунду. Затем он определяет текущее системное время, чтобы узнать прошло ли 600 секунд. Если это условие не выполняется, вирус завершает выполнение, в то время как CCleaner продолжает выполняться. В ситуациях, когда вредоносное ПО не может выполнить IcmpCreateFile, оно возвращается к использованию функции Sleep () для реализации той же функции задержки. Вредоносная программа также сравнивает текущее системное время со значением, хранящимся в следующем разделе реестра:

HKLM \ SOFTWARE \ Piriform \ Agomo: TCID

Если время, хранящееся в TCID, ещё не пришло, вирус также прекратит выполнение.


Рисунок 3: Процедура задержки

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


Рисунок 4: Проверка привилегий

Если у пользователя, запустившеего вредоносное ПО, есть права администратора, на зараженной системе активируется SeDebugPrivilege. Затем вредоносное ПО считывает значение «InstallID», которое хранится в следующем разделе реестра:

HKLM \ SOFTWARE \ Piriform \ Agomo: Muid

Если это значение не существует, вредоносное ПО создает его с помощью '((rand () * rand () ^ GetTickCount ())'.

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


Рисунок 5: Структура данных CCBkdr_System_Information

После сбора информации о системе она зашифровывается и кодируется с использованием модифицированного Base64. Затем вредоносное ПО устанавливает связь с командным сервером (C2), как описано в следующем разделе.

Command and Control (C2)


Как только ранее упомянутая системная информация была собрана и подготовлена для передачи на сервер C2, начинается попытка передать её с использованием запроса POST HTTPS на 216[.]126[.]225[.]148.

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


Рисунок 6: Структура данных CCBkdr_ShellCode_Payload

После установки вируса продолжаются обычные операции CCleaner. Ниже приведена диаграмма, описывающая работу этого вредоносного ПО на высоком уровне:


Рисунок 7: Схема процесса запуска вредоносной нагрузки

Алгоритм генерации домена


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


Рисунок 8: генерация доменов для 12 месяцев

Вредоносная программа будет инициировать DNS-поиск для каждого домена, генерируемого алгоритмом DGA. Если DNS-поиск не приведет к возврату IP-адреса, этот процесс будет продолжен. Вредоносная программа выполнит DNS-запрос активного DGA-домена и ожидает получить два IP-адреса с сервера доменных имен. Затем вредоносная программа вычислит вторичный сервер C2, выполнив серию битовых операций с полученными IP-адреса и объединив их для определения нового фактического адреса командного сервера. Диаграмма, показывающая этот процесс, приведена ниже:


Рисунок 9: Определение адреса командного сервера

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

Потенциальный ущерб


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


Рисунок 10: Статистика CCleaner

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

При анализе телеметрических данных Cisco Umbrella о доменах, связанных с этой атакой, Talos определил значительное количество систем, которые выполняли соответствующие DNS-запросы. Поскольку эти домены никогда не регистрировались, разумно сделать вывод о том, что единственной причиной является этот вирус. Только домены, относящиеся к августу и сентябрю (что коррелирует со временем, когда эта угроза была активна), показывают значительную активность.


Рисунок 11: Активность DGA домена за июль 2017 года

Как упоминалось ранее, версия CCleaner, которая включала это вредоносное ПО, была выпущена 15 августа 2017 года. Следующий график показывает значительное увеличение активности DNS, связанной с DGA-доменом, используемым в августе 2017 года:


Рисунок 12: Активность для домена в августе 2017 года


Рисунок 13: Активность для домена в сентябре 2017 года

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


Рисунок 14: Трафик после отключения сервера

Стоит также отметить, что на 18 сентября антивирусное обнаружение этой угрозы остается очень низким (1/64).


Рисунок 15: Проверка обнаружения вируса

Ещё причины для беспокойства


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

При анализе кода с сервера C2 сразу выделяется список организаций, на которые была нацелена доставка загрузчика второго уровня. Основываясь на обзоре базы данных отслеживания C2, которая охватывает только четыре дня в сентябре, мы можем подтвердить, что по меньшей мере 20 машин жертв были заражены специализированными вредоносными нагрузками. Ниже приведен список доменов, на которые атакующие пытались настроить таргетинг. Не для всех компаний идентифицированных в файле .php был замечен обмен с С2 сервером.


Рисунок 16

PHP скрипт сравнивает системные маяки полученные с заражённой машины по трём значениям: $DomainList, $IPList и $HostList. Это необходимо для определения того, должна ли зараженная система доставлять вредоносную нагрузку Stage 2. Ниже приведен сжатый PHP-код, который демонстрирует это:


Рисунок 17

База данных C2 содержала две таблицы: одну, описывающую все машины, которые «общались» с сервером, и описание всех машин, получивших загрузку Stage 2. Причем обе из них были датированы с 12 сентября по 16 сентября. За этот период времени на сервер C2 было отправлено более 700 000 машин, и более 20 машин получили Stage 2 нагрузку. Важно понимать, что целевой список может быть изменён и был изменён в течение периода, когда сервер был активен для заражения целевых организаций.

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

Основные данные подключения хранятся в таблице «Сервер». Вот пример для одного из узлов Talos в этой таблице базы данных:


Рисунок 18

А также список установленных программ.


Рисунок 19

И список процессов.


Рисунок 20

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


Рисунок 21

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


Рисунок 22

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


Рисунок 23

Уязвимые системы с доменом, содержащим слово «банк»:


Рисунок 24

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

CODE REUSE


Talos рассмотрел заявления от исследователей Лаборатории Касперского о совпадении кода с образцами вредоносных программ, которые, как известно, используются Group 72. Хотя это никоим образом не является доказательством, мы можем подтвердить совпадение и согласны с тем, что это важная информация.

Слева: 2bc2dee73f9f854fe1e0e409e1257369d9c0a1081cf5fb503264aa1bfe8aa06f (CCBkdr.dll)

Справа: 0375b4216334c85a4b29441a3d37e61d7797c2e1cb94b14cf6292449fb25c7b2 (Missl backdoor — APT17 / Group 72)


Рисунок 25

ВЫВОД


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

Ниже приведены индикаторы компроментации, связанные с этой атакой.

File Hashes
6f7840c77f99049d788155c1351e1560b62b8ad18ad0e9adda8218b9f432f0a9
1a4a5123d7b2c534cb3e3168f7032cf9ebf38b9a2a97226d0fdb7933cf6030ff
36b36ee9515e0a60629d2c722b006b33e543dce1c8c2611053e0651a0bfdb2e9

DGA Domains
ab6d54340c1a[.]com
aba9a949bc1d[.]com
ab2da3d400c20[.]com
ab3520430c23[.]com
ab1c403220c27[.]com
ab1abad1d0c2a[.]com
ab8cee60c2d[.]com
ab1145b758c30[.]com
ab890e964c34[.]com
ab3d685a0c37[.]com
ab70a139cc3a[.]com

IP Addresses
216[.]126[.]225[.]148

Installer on the CC: dc9b5e8aa6ec86db8af0a7aa897ca61db3e5f3d2e0942e319074db1aaccfdc83 (GeeSetup_x86.dll)

64-bit trojanized binary
128aca58be325174f0220bd7ca6030e4e206b4378796e82da460055733bb6f4f (EFACli64.dll)

32-bit trojanized binary: 07fb252d2e853a9b1b32f30ede411f2efbb9f01e4a7782db5eacf3f55cf34902 (TSMSISrv.dll)

DLL in registry: f0d1f88c59a005312faad902528d60acbf9cd5a7b36093db8ca811f763e1292a

Registry Keys:
HKLM\Software\Microsoft\Windows NT\CurrentVersion\WbemPerf\001
HKLM\Software\Microsoft\Windows NT\CurrentVersion\WbemPerf\002
HKLM\Software\Microsoft\Windows NT\CurrentVersion\WbemPerf\003
HKLM\Software\Microsoft\Windows NT\CurrentVersion\WbemPerf\004
HKLM\Software\Microsoft\Windows NT\CurrentVersion\WbemPerf\HBP
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/338980/


Метки:  

Больше сюрпризов от Apple: обновленные правила размещения на App Store

Пятница, 29 Сентября 2017 г. 14:08 + в цитатник
nanton сегодня в 14:08 Разработка

Больше сюрпризов от Apple: обновленные правила размещения на App Store

    Для разработчиков на iOS настала эпоха перемен: новые девайсы, новые инструменты, новые требования маркета. О технических особенностях обновленной платформы и о том, как следует оформлять и продвигать свое детище на новой версии App Store, было сказано уже немало. Сегодня мы хотим обратиться к еще одной перемене, которая обсуждается сейчас менее широко — изменениям в правилах публикации.



    В последние месяцы Apple корректировали и дополняли список официальных требований к продуктам, размещаемым на сторе уже дважды. Первая порция правок пришлась на июнь, как заблаговременная подготовка почвы для осеннего релиза; вторая была опубликована совсем недавно. За каждой следовала массовая зачистка — удаление тысяч приложений с маркета без предупреждений и надежды на амнистию. Раз уж все так серьезно, пожалуй, стоит поподробнее разобрать, что именно изменилось и стоит ли уже начинать бояться. Мы изучили июньское и сентябрьское соглашения и собрали все значительные нововведения здесь, под катом.

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

    Итак, начнем с июньских тезисов. Что здесь нужно знать?

    1. Удаляться без разговоров и предупреждений будут:

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

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

    3. То же относится и к музыкальным приложениям, которые получают доступ к пользовательским данным в Apple Music.

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

    5. Продолжительность периода подписки не может составлять менее недели; подписка должна быть актуальна на всех девайсах пользователя.

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

    7. Допускается использование кода «со стороны», при условии, что оно осуществляется через WebKit или JavaScript Core, а разработчик состоит в Apple Development Program.

    8. В текст добавлены мелкие изменения, касающиеся разных аспектов оформления:

    • Названия: не должны включать выражения типа «for children», «for kids», если не принадлежат к соответствующей категории, а также отсылки к цене.
    • В сабтайтлах запрещено: ссылаться на другие приложения или давать информацию о продукте, которую не можете подтвердить.
    • Иконки: допускается использование кастомизированных иконок, при условии что остается возможность вернуться к исходной. Вариации иконок должны быть связаны с контентом приложения (например, иконки, отображающие разные типы погоды для утилиты с прогнозами) и синхронизироваться по всей системе.

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

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

    2. Аутентификация через Face ID должна осуществляться только и исключительно при помощи LocalAuthentication (использовать ARKit и прочие технологии запрещено). Для пользователей младше 13 лет следует предоставить альтернативный метод аутентификации.

    3. ARKit допустимо применять только в том случае, если вы планируете подарить пользователям «богатый, интегрированный опыт взаимодействия с дополненной реальностью». Иными словами, парой моделей или анимаций тут не обойтись.

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

    5. Продукты, в которых используются технологии типа ARKit, Camera API, Photo API для распознавания лиц, обязательно должны иметь пользовательское соглашение и запрашивать согласие на сбор данных. Полученную информацию нельзя использовать для целей, не оговоренных в соглашении.

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

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

    Если обобщить, изменения касаются прежде всего безопасности данных и некоторых этических моментов. Конечно, в зоне риска в первую очередь те, кто совсем потерял совесть, однако с таким обилием требований к сбору данных многие приложения могут не пройти модерацию. Кроме того, Apple очерчивает границы для свежеиспеченных технологий и также понемногу начинает закручивать гайки в отношении инструментария. В первую очередь имеет смысл пристально отслеживать будущие обновления текста тем, кто работает с ARKit, Face ID и прочими новинками — они, как видно из всего сказанного, сейчас на стадии активной кодификации.

    Удачи и долгой жизни на маркете!
    Original source: habrahabr.ru (comments, light).

    https://habrahabr.ru/post/338982/


    Метки:  

    CIS Benchmarks: лучшие практики, гайдлайны и рекомендации по информационной безопасности

    Пятница, 29 Сентября 2017 г. 13:37 + в цитатник

    Метки:  

    Blockchain стартап и Имбецилы. Можно ли не рождаться?

    Пятница, 29 Сентября 2017 г. 13:07 + в цитатник
    joint сегодня в 13:07 Разработка

    Blockchain стартап и Имбецилы. Можно ли не рождаться?

      Хабр, привет.

      Я, Сергей Иноземцев, живу в Санкт-Петербурге, мне 33 года, и я, в итоге, тоже делаю свой блокчейн-стартап.

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

      image

      Сам проект пока живет на kickico, около месяца он еще будет на модерации.

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

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

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

      Так же хочу пояснить что это не краудфандинг площадка, каждая картина ставит перед собой цель заработать в прокате, как минимум, удвоиться в сборах по отношению к бюджету, но я очень надеюсь что этим не ограничиться и у нас будут проекты которые оправдывали бы инвестиции в них в пять и более раз. Поэтому продюсеры действительно должны зарабатывать деньги с тех картин, в которые они вложились. Еще одно пояснение касается бюджета картин. Агентство должно работать с дорогими проектами, с бюджетом от 2-3М $ по полному метру. Все что ниже этого уже самиздат и в действительности для съемок такого кино агентство не нужно, нужна только харизма и немного удачи. Агенство будет проводить самостоятельную PR компанию по каждой картине и привлекать к участию в финансировании крупных крипто-инвесторов из blockchain мира.

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

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

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

      Последнее, про харизму. Я отдаю себе отчет что такие проекты очень сильно зависят от итоговых картин. Должен быть некий пул своих картин, некие критерии ценности отбора проектов и общее видение фильмографии агентства. Стратегия фильмографии. И у меня эта стратегия есть. Моя личная мотивация снять как-минимум три больших картины. Я не знаю, насколько я крупный и зрелый художник, но одно могу сказать программирование научило меня тому чтобы относится к каждой своей инициативе как к версии, всегда будут ошибки и прощеты, но в мире it-разработки у нас есть шансы доработать свои проекты и предложить миру их новые версии. Версионность одно из тех ключевых свойств, которое прежде всего помогает избавиться от страхов и самокопания, от постоянной тревоги по-поводу своего профессионализма, своего таланта, своих возможностей. В фильме последнее затмение персонаж Леонардо дикаприо, французский поэт символист Артюр Рембо, заявил что хочет стать гением. Мне понравилась именно формулировка, это отношение к гениальности ни как к какому-то первородному статусу, а просто как к задаче. Задаче, которая предполагает интерес ко всем знаниям человечества без исключений и ко всем его проблемам. Это очень вдохновляющая цель, оставаться живым и продолжать проявлять интерес ко всему что происходит вокруг нас.

      Осталось только развеять интригу по поводу заголовка. «Имбецилы», и «Можно ли не рождаться?», вместе с «Myxomatosis» это названия тех трех больших картин которые я хочу снять.

      В этом посте я опубликую три синопсиса к этим картинам. Считайте, что это мой «White Paper».

      Имбецилы. Синопсис.


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

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

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

      Через некоторое агентство становиться невероятно успешным.

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

      Можно ли не рождаться? Синопсис.



      Мужчина, 35 лет. Работает в страховой компании, в продажах. Доход зависит от сделок. Не женат. Ранее всеми силами пытался разбогатеть, но ничего с этим не вышло. Переживает острую депрессию. Совершает попытку самоубийства, о которой становиться известно его старшей сестре, единственному родному человеку. У сестры своя семья. После спасения сестра записывает его к психологу. Психолог выявляет что главная причина его депрессии в том, что тот хочет создать семью, но не считает себя в достаточной степени обустроенным для этого, вскрывает обиду на своего умершего отца. Дословно “Не хочет плодить нищету как ранее ее наплодил его отец”. Из-за чего мечется между преступлением и самоубийством. Психолог настраивает его на то что просто убивать себя было бы слишком легким решением, и что есть шанс много украсть и при этом не сесть, но нужно подготовиться, а также поправить свое здоровье и сознание так как на преступление нужно идти с холодной головой. Предлагает ему найти форму в которой он мог бы выплескивать свои мысли и убеждения в какой-то пограничной форме и идти на грани закона, постепенно ставя перед собой все более сложные задачи, а также начать принимать лекарства. Герой соглашается на эту стратегию, увольняется из страховой компании и устраивается в типографию на ночную печать, начинает изучать стрит-арт. Первое время делает совсем простые и неаккуратные работы, но со временем совершенствует свое мастерство. С ростом требований к себе растет и жажда во все более сложных и провокационных проектах. Начинает с метро и железной дороги. В течении трех следующих лет, герой проникает на закрытые территории, разрисовывает вагоны поездов и метрополитена, устраивает несанкционированную выставку своих работ на одной из станций метро, заменив холстами всю наружную рекламу на станции, позже разрисовывает автомобили на стоянках офисных клерков, портит витрины магазинов с манекенами, заливает краской один из центральных торговых комплексов, оставляет свои работы в городских музеях, в частных квартирах чиновников и коллекционеров живописи. Через некоторое время его никнейм и стиль становиться знаменит. За ним начинают охотиться журналисты, а также в отношении него возбуждается дело и по его стопам пускают следователя. Работы героя начинают продаваться на аукционах и через них у него появляются деньги (но не так много чтобы построить дом и завести семью). На эти деньги он готовит уже настоящее ограбление. В финале главный герой планирует украсть деньги у прокурора который покрывает мафию, занимающуюся угоном машин, но сестра и психолог помогают следователю поймать его раньше и осудить за вандализм. Шумиха суда делает его известным и общество признает его крупным художником. Сестра, психолог и следователь который встал на его сторону, играют в его жизни не последнюю роль.

      Myxomatosis. Синопсис.



      Парень, с некоторым бекграундом в IT, решается на незаконную деятельность, и устраивается на мойку простым мойщиком автомобилей. Мойка находится в самом престижном спальном районе Санкт-Петербурга и обслуживает богатых людей, с дорогими автомобилями. Юноша намерен взламывать автомобильные “мозги” и личные электронные устройства клиентов, прослушивать машины и таким образом собирать ценную информацию для будущих ограблений. Не исключено, что в определенных случаях, он не станет избегать и установки собственных устройств, жучков в машину. Но без опыта на мойке у него не получается удержаться на работе. Тем не менее, он уже все продумал и устраивает на нее своего друга. Теперь они действуют сообща. Пока второй герой пытается удержаться на работе, они знакомятся еще с одним парнем, опытным хакером. Общаются с ним только через почту. Втроем они собирают информацию о клиентах мойки в единую базу данных, а также экспериментируют со взломами информационных систем в городской среде, морально и технически готовятся к следующему шагу, создают более подробные досье на клиентов мойки, для того чтобы определить для себя первых потенциальных жертв. В это же время они знакомятся с управляющим мойки, девятнадцатилетним сыном миллиардера, собирающим раритетные автомобили и несколькими постоянными клиентами мойки, среди которых есть и агрессивный тип на машине марки Мазерати. В начале декабря, накануне новогодних праздников, они начинают прослушивать первые пять автомобилей и их владельцев.
      В новогодние праздники, они решаются на первое преступление. В ходе слежки они выяснили, что одна из их жертв, девушка, которая встречается с состоятельным мужчиной ему изменяет. Они собирают на нее компромат и получают от нее первые деньги через шантаж. Примерный ущерб от операции 12м рублей.На эти деньги герои совершают некоторое покупки для себя, но, в основном, вкладываются в свою техническую инфраструктуру. Теперь они контролируют 10 жертв.
      В их команде появляется еще двое человек, эксперт по угонам автомобилей и специалист по кражам денег с банковских карт, и аферам связанным с операциями в электронных деньгах.
      Во второй раз они грабят цыган (или какую-либо иную организованную группу), для ограбления они взломали мозги машины, предустановили на них свой софт и спрятали в салоне балон с усыпляющим газом, отредактировали запись видеонаблюдения на мойке. Группу грабят на обратной дороге после сделки с партией наркотиков (контрабанды, оружия, медикаментов). На пустой дороге они удаленно перехватывают управление автомобилем, блокируют двери и стекла, блокируют ремни безопасности и усыпляют охрану газом. После ограбления они делают им уколы клофелином, чистят и убирают из машины все свое оборудование. Переустанавливают софт в машине. Ущерб от ограбления примерно 2М $.
      Друг главного героя ставит жучки на ретро кары управляющего мойки, и взламывает софт машин вип клиентов мойки, из негласного закрытого клуба, снимают наблюдение со всех остальных клиентов мойки. Увольняется с мойки.
      Далее, они ведут наблюдение только за управляющими и другими випами, среди которых есть и чувак на мазерати. И, однажды, во время наблюдения за ним, они становятся свидетелями того, как тот похищает в городе девушку, везет за город там избивает и бросает без сознания в прорубь, который вырубили для крещенского купания. Герой и его команда, не вмешиваясь в саму разборку, успевают спасти девушку и отвезти ее в больницу. Девушка выживает и за месяц приходит в себя.
      Вскоре им удается поговорить с ней. Девушка рассказывает что она работала внутри холдинга и вела экономическое расследование против олигарха и его теневых доходов, которому в том числе принадлежит и эта мойка. Олигарх — отец того самого девятнадцатилетнего пацана, управляющего мойкой.
      Следующие восемь месяцев группа главного героя ведет наблюдение за холдингом, получая все больший контроль за его информационной инфраструктурой. Снова в начале декабря, накануне новогодних праздников, герои проникают в тайный бункер холдинга и выкрадывают всю черную бухгалтерию.
      Пропажа бухгалтерии пугает олигарха, он спешно собирает все свои деньги, прячет их у знакомого подкупленного высокопоставленного полицейского чиновника на квартире и готовиться сбежать из страны.
      В последнем, самом крупном ограблении, герои обворовывают квартиру этого чиновника. Само ограбление чрезвычайно простое. Но сумма огромная.
      Олигарх сбегает из страны, в прессе появляется информация о возбуждении уголовного дела на него. Его объявляют в федеральный розыск.
      Фильм (возможно) заканчивается тем, что главные герои избивают на автостоянке владельца того самого Мазерати на глазах у пострадавшей ранее девушки.
      Тайным хакером, который помогал группе главного героя оказывается специалист спец.служб, который все это время вел ребят и следил за ними.

      С уважением, Сергей Иноземцев.
      Original source: habrahabr.ru (comments, light).

      https://habrahabr.ru/post/338976/


      Метки:  

      Обзор одной российской RTOS, часть 6. Средства синхронизации потоков

      Пятница, 29 Сентября 2017 г. 12:57 + в цитатник
      EasyLy сегодня в 12:57 Разработка

      Обзор одной российской RTOS, часть 6. Средства синхронизации потоков

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

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

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

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

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

        Для тех, кто ещё не видел предыдущие части, ссылки:

        Часть 1. Общие сведения
        Часть 2. Ядро ОСРВ МАКС
        Часть 3. Структура простейшей программы
        Часть 4. Полезная теория
        Часть 5. Первое приложение
        Часть 6. Средства синхронизации потоков (настоящая статья)
        Часть 7. Средства обмена данными между задачами
        Часть 8. Работа с прерываниями

        Критическая секция


        Для разминки, рассмотрим класс CriticalSection. Он применяется для обрамления участков, на которых недопустимо переключение контекста.

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

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

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

        Рассмотрим пример (участок, выделенный розовым, защищён критической секцией, здесь гарантируется, что управление у задачи не будет забрано)


        То же самое - текстом
        void ProfEye::Tune()
        {
        	ProfData::m_empty_call_overhead = 0;
        	ProfData::m_empty_constr_overhead = 0;
        	ProfData::m_embrace_overhead = 0;
        	
        	CriticalSection _cs_;
        	
        	loop ( int, i, 1000 ) {		
        		PROF_DECL(PE_EMPTY_CALL, empty_call);
        		PROF_START(empty_call);
        		PROF_STOP(empty_call);
        		
        		{ PROF_EYE(PE_EMBRACE, _embrace_); 
        			{ PROF_EYE(PE_EMPTY_CONSTR, _empty_constr_); 
        			}	
        		}	
        	}
        	ProfData::m_empty_call_overhead = prof_data[PE_EMPTY_CALL].TimeAvg();
        	ProfData::m_empty_constr_overhead = prof_data[PE_EMPTY_CONSTR].TimeAvg();
        	ProfData::m_embrace_overhead = prof_data[PE_EMBRACE].TimeAvg() + ProfData::ADJUSTMENT - 2 * ProfData::m_empty_constr_overhead;
        }
        



        Разумеется, область видимости объекта всегда можно ограничить фигурными скобками.


        Текстом
        ProfEye::ProfEye(PROF_EYE eye, bool run)
        { 
        	m_eye = eye; 
        	m_lost = 0;
        	m_run = false; 
        	if ( run ) { 
        		{ CriticalSection _cs_;
        			prof_data[m_eye].Lock(true);
        			m_up_eye = m_cur_eye; 
        			m_cur_eye = this;
        		}
        		Start(); 
        	} else 
        		m_up_eye = nullptr; 
        }	
        



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

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

        cnt++;

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

        26: cnt++;
        0x08004818 6B60 LDR r0,[r4,#0x34]
        0x0800481A 1C40 ADDS r0,r0,#1
        0x0800481C 6360 STR r0,[r4,#0x34]


        Допустим, значение (для верности, скажем 10) уже попало в регистр r0, после чего планировщик передаст управление другой задаче. Она также считает значение 10 и уменьшит его, положив 9. Затем, когда управление вернётся текущей задаче, она прибавит единицу не к переменной, а к тому, что уже попало в регистр r0 — к десятке. Получится 11. Значение счётчика окажется искажено.

        Вот как раз для защиты от таких ситуаций, вполне подойдёт критическая секция.

        {
           CriticalSection cs;
           cnt++
        }
        


        Хотя, конечно, накладные расходы на работу с NVIC также следует держать в уме, так как получится не три, а намного больше команд ассемблера

        27: CriticalSection cs;
        0x0800481A 4668 MOV r0,sp
        0x0800481C F7FEFCE8 BL.W _ZN4maks15CriticalSectionC2Ev (0x080031F0)
        28: cnt++;
        0x08004820 6B60 LDR r0,[r4,#0x34]
        0x08004822 1C40 ADDS r0,r0,#1
        29: }
        0x08004824 6360 STR r0,[r4,#0x34]
        0x08004826 4668 MOV r0,sp
        0x08004828 F7FEFDA0 BL.W _ZN4maks19InterruptMaskSetterD2Ev (0x0800336C)


        Это не считая содержимого системных подпрограмм… Приведём только первую, чтобы читатель представлял её сложность

        0x080031F0 B510 PUSH {r4,lr}
        0x080031F2 2150 MOVS r1,#0x50
        0x080031F4 F000F8AE BL.W _ZN4maks19InterruptMaskSetterC2Ej (0x08003354)
        0x080031F8 4901 LDR r1,[pc,#4] ; @0x08003200
        0x080031FA 6001 STR r1,[r0,#0x00]
        0x080031FC BD10 POP {r4,pc}


        Как видно — это ещё не максимальная вложенность… С другой стороны, это — всё равно меньшее из зол. Просто не стоит увлекаться частыми входами и выходами из критической секции.

        Критическая секция — очень мощный, но потенциально опасный инструмент, ведь пока она не выйдет из области видимости — многозадачность отключается. В идеале, блокировку следует выполнять только на несколько строк. Наличие цикла может существенно увеличить время задержки, вход в функции — и подавно (если программист слабо представляет себе время нахождения в этих функциях), а уж работа с некоторыми видами оборудования — совсем потенциально опасная вещь. Пусть программист решил гарантировать себе отсутствие переключения контекста на время передачи двух байтов по шине SPI с частотой 10 МГц. Один бит имеет период 100 нс. 16 бит — 1,6 мкс. Это вполне приемлемый результат. Следующая задача потеряет не более этого участка (в целом, это сопоставимо со временем работы планировщика). Но если передавать по UART строку из 20 символов на скорости 250 килобит в секунду, то это займёт уже 20 * 10 * 4 мкс = 0.8 мс. То есть, начнись процесс ближе к концу кванта времени задачи, он «съест» почти весь квант следующей задачи.

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

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

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

        	virtual void Execute()
        	{
        		while (true)
        		{
        			GPIOE->BSRR = (1<BSRR = (1<<(nBit+16));
        			Delay (5);
        		}
        	}
        


        Даёт нормальный меандр:



        Добавляем критическую секцию


        Текстом
        	virtual void Execute()
        	{
        		CriticalSection cs;
        		while (true)
        		{
        			GPIOE->BSRR = (1<BSRR = (1<<(nBit+16));
        			Delay (5);
        		}
        	}
        



        Получаем совершенно иной сигнал



        Увеличиваем масштаб — период сигнала совершенно неверен…



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

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

        Двоичный Семафор


        Давным-давно, на железной дороге активно использовались семафоры. Они имели два состояния: поднят — поезд может двигаться. Опущен — поезд должен ждать. Ну, а поднимал и опускал его диспетчер. Вот и здесь, какая-то задача (или задачи) дёргает за верёвочку (выполняя роль диспетчера), а какая-то задача (или задачи) — ждут, когда путь будет открыт. При этом, ожидающие задачи заблокированы, то есть — не расходуют кванты времени.



        Такой семафор реализуется классом BinarySemaphore.

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

        explicit BinarySemaphore(bool is_empty = true)

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

        Задачи, которым следует пройти через семафор, должны вызвать функцию Wait(). Аргументом этой функции является время таймаута в миллисекундах. Если за заданное время задача была разблокирована, функция вернёт значение ResultOk. Соответственно, если наступил таймаут, результат функции будет равен ResultTimeout. Когда функция должна ждать «до упора», следует передать значение таймаута, равное INFINITE_TIMEOUT.

        Если функция вызывается с нулевым значением таймаута, то она вернёт управление мгновенно, но по результату (ResultOk или ResultTimeout) будет ясно, был семафор открыт или закрыт.

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

        Если функция вернула результат ResultOk, семафор автоматически закроется.

        Как уже отмечалось, ждать семафора может сразу несколько задач. В этом случае, выбор «счастливчика», которого пропустят первым, будет осуществляться следующим образом: Задачи в списке ожидания следуют в порядке уменьшения приоритета, а при одинаковом приоритете — в порядке вызова функции Wait().

        Для того, чтобы открыть семафор, используется функция Signal(). Если семафор уже открыт, она вернёт результат ResultErrorInvalidState, иначе — ResultOk. Функция не может вызываться из прерывания с приоритетом выше, чем MAX_SYSCALL_INTERRUPT_PRIORITY.

        Семафор


        Честно говоря, мне категорически не нравится это название. Правильнее было бы назвать этот объект синхронизации «Завхоз», но против традиций не попрёшь. Везде он называется семафором, ОСРВ МАКС не является исключением. Отличие простого семафора (или завхоза) от двоичного состоит в том, что он может считать (класть ресурсы на склад). Есть хотя бы один ресурс — задача может проходить (при этом число ресурсов уменьшается). Нет ресурсов — задача будет ждать появления хотя бы одного.



        Такие семафоры пригождаются, когда необходимо распределять какие-либо ресурсы. Я, например, заводил 4 буфера для выдачи данных в USB (эта шина работает не с байтами, а с массивами байтов, поэтому удобнее всего готовить данные именно в буферах). Соответственно, рабочая задача может определить, есть ли свободные буфера. Если есть — заполнить их. Нет — ждать, пока, работающий с USB перешлёт и освободит хотя бы один. В общем, если где-то выделяется не один, а несколько ресурсов — их распределение удобнее всего поручить завхозу (или, согласно традиционному именованию — семафору).

        Соответственно, этот объект реализуется в классе Semaphore. Рассмотрим его отличие от двоичного семафора. Во-первых, у него чуть иной конструктор

        Semaphore(size_t start_count, size_t max_count)

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

        Функция Signal(), соответственно, увеличивает счётчик ресурсов. Если он дошёл до максимума — она вернёт ResultErrorInvalidState. Ещё раз напомним, что функция не может вызываться из прерываний с приоритетом выше, чем MAX_SYSCALL_INTERRUPT_PRIORITY.

        Функция Wait() пропустит задачу, если число ресурсов не равно нулю, при этом, уменьшив счётчик. А если ресурсов нет — задача будет заблокирована, пока их не вернут через функцию Signal(). Ещё раз напомним, что из прерывания эту функцию можно вызывать только с нулевым таймаутом.

        Теперь рассмотрим функции, которые не имели смысла в двоичном случае.

        GetCurrentCount() вернёт текущее значение счётчика ресурсов

        GetMaxCount() вернёт максимально возможное значение счётчика (если семафор создавала другая задача — может быть полезно, чтобы определиться с его характеристиками)

        Мьютекс


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

        Коротко суть мьютекса можно пояснить фразой «Кто первый встал — того и тапки». Есть защищаемый объект — «тапки». Муж проснулся, запросил их — система отдала их ему в безраздельное пользование. Жена и сын запросили — их заблокировали. Как только муж вернул тапки в систему — их получила жена. Вернула она — получил сын. Вернул он — объект перешёл в свободное состояние, следующий запросивший, получит их снова без ожидания.

        В микроконтроллерах замечательным ресурсом, который надо защищать таким способом, является порт (SPI, I2C и т.п.), если через него пытается работать несколько задач. Мы уже рассматривали, что к одному физическому каналу может быть подключено несколько разнородных устройств, например, в классическом телевизоре: на одной шине I2C могут быть видеопроцессор, аудиопроцессор, процессор телетекста, тюнер — они вполне могут обслуживаться разными задачами. Зачем тратить процессорное время, ожидая сброса бита BSY? Тем более, что всё равно, возможны коллизии. Рассмотрим работу трёх задач, исключительно анализирующих бит BSY порта, исполняя их по шагам:



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

        Иногда может получиться так, что разработчик слишком увлёкся мьютексами, и задача может захватить мьютекс несколько раз. Разумеется, скорее всего, это будет происходить во вложенных функциях. Функция 1 захватывает мьютекс, потом управление передаётся в функцию 2, оттуда — в функцию 3, оттуда — в функцию 4 (написанную год назад), которая также пытается захватить этот же мьютекс. Чтобы не возникло блокировки, в таких случаях следует создавать рекурсивные мьютексы. Одна задача сможет их захватывать многократно. Важно лишь освободить столько же раз, сколько он был захвачен. В ОС Windows все мьютексы являются рекурсивными, но такой подход на слабых микроконтроллерах привёл бы к неоправданному расходованию ресурсов, поэтому по умолчанию, в ОСРВ МАКС мьютексы рекурсивными не являются.

        Рассмотрим основные функции класса Mutex. В первую очередь — его конструктор

        Mutex(boolrecursive = false);

        Аргумент конструктора определяет тип — рекурсивный или нет.

        Функция Lock() захватывает мьютекс. В качестве аргумента передаётся значение таймаута. Как всегда, можно задать особые значения — нулевой таймаут (мгновенный выход без ожидания) или значение INFINITE_TIMEOUT (ждать до победы). Если мьютекс удалось захватить, будет возвращён результат ResultOk. При истечении таймаута, вернётся результат ResultTimeout. При попытке захвата нерекурсивного мьютекса, результат будет ResultErrorInvalidState. Мьютекс нельзя захватывать в прерывании. Если попытаться это сделать, результат будет ResultErrorInterruptNotSupported.

        Функция Unlock() — освобождает мьютекс. Соответственно, она должна вызываться по окончании исполнения защищаемой секции.

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

        Теперь стоит рассказать про такую вещь, как наследование приоритетов. Допустим, в системе имеются задачи A с нормальным приоритетом, B с повышенным приоритетом и C с высоким приоритетом. Предположим, что задачи B и C были заблокированы, а A в это время успела захватить мьютекс. Нарисуем это графически, расположив задачи друг над другом (чем выше приоритет, тем выше задача на рисунке)



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



        Но мьютекс находится во владении задачи A! По штатной логике, раз он занят, задача C блокируется до его освобождения. И вдруг неожиданно разблокировалась задача B (пусть она ждала какой-то другой ресурс, и тот освободился). Так как её приоритет выше, чем у A, то исполняться будет именно она (то есть, задача B)



        Что мы имеем? Высокоприоритетная задача C не может исполняться, так как она ждёт освобождения мьютекса. А дождаться она не может, так как его текущий владелец вытеснен более высокоприоритетной задачей относительно владельца мьютекса, но более низкоприоритетной — относительно несправедливо заблокированной задачи.

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



        Этот факт крайнее важно помнить, так как если задача A взаимодействует также с какой-либо задачей E, также имеющей нормальный приоритет, на время наследования приоритета, она это взаимодействие утеряет. Увы, с этим ничего сделать невозможно (в рамках стандартной концепции ОСРВ), это следует просто учитывать при проектировании программ.

        Мьютекс-страж


        Классическое алгоритмическое программирование подразумевает один вход и один выход в любой алгоритм. Однако, практика такова, что фанатичное обеспечение этого принципа приводит к неоправданному усложнению текста и снижению читаемости. Давайте рассмотрим следующий псевдокод:
        
        	m_mutex.Lock();
        	switch (cond)
        	{
        	   case 0x00:
           	      ....
           		return ResultCode1;
                   case 0x02:
           	      ....
                       return ResultCode2;
                   case 0x0a:
           	      ....
                       return ResultCode3;
                   case 0x15:
           	      ....
                       return ResultCode4;
        	}
                ....
                m_mutex.Unlock();
        

        Вообще-то, здесь перед каждым выходом из функции, следует методично расставить mutex.Unlock(). А таких участков в большом алгоритме может быть много. И они могут добавляться. Рано или поздно, программист где-нибудь забудет разблокировать мьютекс, и программа «зависнет». И это — при том, что человечество ночей не спало, изобретало ООП в целом и деструкторы классов в частности!

        Мьютиекс-страж как раз занимается тем, что использует деструкторы. У этого класса в интерфейсной части нет ничего, кроме конструктора. Скопируем его описание из Руководства Программиста:
        explicit MutexGuard(Mutex& mutex, bool only_unlock = false);
        Аргументы:
        • mutex – ссылка на мьютекс;
        • only_unlock–если значение – истина, захват мьютекса в конструкторе не производится. Подразумевается, что мьютекс уже был захвачен ранее явным вызовом метода Lock().


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


        Текстом
               {
        	MutexGuard (m_mutex);
        	switch (cond)
        	{
        	   case 0x00:
           	      ....
           		return ResultCode1;
                   case 0x02:
           	      ....
                       return ResultCode2;
                   case 0x0a:
           	      ....
                       return ResultCode3;
                   case 0x15:
           	      ....
                       return ResultCode4;
        	}
                ....
                }
        



        Событие


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

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

        Первое отличие: Событие воздействует только на того, кто его ждёт. Если в текущий момент у события нет получателя — оно уйдёт в никуда. Если через мгновение после возникновения события, кто-то начнёт ждать его — он окажется заблокирован. Разблокировка произойдёт только по следующему событию. Кто не успел — тот опоздал. Как мы помним, семафор же наоборот, ждал его кто-то или нет — всё равно откроется. И первый, кто будет проходить мимо семафора, будет пропущен.

        Второе отличие — если открытия семафора ждёт несколько задач, то разблокирована будет только одна из них. Остальные будут ждать следующего открытия. Событие же можно настроить на режим, когда оно разблокирует всех, кто ждал его возникновения. То есть, все ждущие задачи будут переведены из состояния «Заблокирована» кто-то в состояние «Активна», а самая везучая задача — в состояние «Исполняется».

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

        Конструктор класса:

        Event(bool broadcast = true);

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

        Функция Raise() посылает событие. Не может быть вызван из прерывания, с приоритетом выше, чем MAX_SYSCALL_INTERRUPT_PRIORITY.

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

        Примеры работы с синхронизирующими объектами


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

        В запланированной третьей части должно будет появиться описание адаптации большой программы для станка с ЧПУ, оставим примеры для неё. А для тех, кто жаждет практики, я могу порекомендовать модульные тесты для ОС. Они размещаются в каталоге ...\maksRTOS\Source\Tests\Unit tests. Вот перечень каталогов, размещённых там:

        BinarySemaphore
        Event
        MessageQueue
        Mutex
        MutexGuard
        Scheduler
        Semaphore


        Лучшие практические примеры сложно придумать. Приятного изучения (правда, есть мнение, что редкий читатель дойдёт до конца первой четверти тестов).
        Original source: habrahabr.ru (comments, light).

        https://habrahabr.ru/post/338682/


        Метки:  

        Национальный роуминг в разных странах мира

        Пятница, 29 Сентября 2017 г. 12:57 + в цитатник
        Yota4All сегодня в 12:57 Разное

        Национальный роуминг в разных странах мира

          Cкоро внутренний роуминг в России исчезнет – ФАС и Минкомсвязи подвели операторов к тому, чтобы убрать это понятие из своих тарифов. Yota же с момента запуска отказалась от идеи внутреннего роуминга.
          Мы узнали, как обстоят дела с национальным роумингом в других странах, и собираются ли они что-то менять.

          Евросоюз
          В июне 2017 года отменили внутренний роуминг для всех клиентов европейских сотовых операторов. До этого эпохального момента цены неуклонно падали – в 2007 году был принят закон о снижении тарифов на 60%, и планы о полной отмене дополнительных плат за сотовую связь внутри ЕС неоднократно обсуждались. Сейчас единые цены на услуги связи – смс, звонки и интернет действуют не только на территории 28 стран материка, но и в Великобритании, Лихтенштейне, Норвегии и Исландии.

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

          Сотовые операторы Финляндии, Литвы, Эстонии и Словакии воспользовались этим шансом, чтобы удержать цены на прежнем уровне. Что касается интернета, то полный безлимит отсутствует, а плата за передачу данных зависит от выбранного пакета услуг и страны. Учитывая то, что даже самый дешевый тариф в три раза дороже среднего российского, новая система — лишняя головная боль для россиян, путешествующих по Евросоюзу: перед поездкой придется промониторить предложения от операторов разных стран, чтобы найти самый выгодный для себя. Например, с итальянской SIM-карты звонить намного дешевле, чем с немецкой.


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

          При этом цены у различных операторов примерно одинаковые и за мобильную связь придется выложить не менее 30 долларов в месяц — это примерно 1800 рублей.
          За внутрисетевой роуминг платить не приходится, но операторы отыгрываются на межсетевых звонках и мобильном интернете: например, T-Mobile не позволяет бесплатно передавать больше 50 Мб данных. И входящие звонки далеко не всегда бесплатны.


          Канада
          В Канаде самые дорогие услуги мобильной связи в Северной Америке – пакет с голосовой связью, SMS и 3 Гб интернета обойдется в 40 канадских доллар ов — примерно 1900 рублей). При этом операторы предлагают широкий выбор SIM-карт и тарифов на звонки и мобильный интернет. Внутренний роуминг в стране отменили в 2014 году.

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

          В каждом индийском штате действуют разные ценовые предложения, и качество покрытия тоже отличается. Операторы Airtel и Idea отменили внутренние роуминговые тарифы с апреля 2017 года, а Jio, наоборот, славится своими высокими ценами.

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

          Китай
          Сегодня в Китае плата за внутренний роуминг есть, но, как заявляет Госсовет КНР, ради «развития национальных интернет-технологий» в республике вскоре откажутся от дополнительных поборов клиентов, которые находятся в разных провинциях, плюс будут отменены повышенные тарифы на интернет.

          Три крупнейших китайских сотовых оператора: China Unicom, Сhina Mobile и China Telecom собираются отменить роуминг уже в октябре этого года и запустить сети 5G в тестовом режиме.


          Выводы однозначные: почти во всех крупных странах внутренний роуминг как услуга отсутствует, а маленьким он просто не нужен из-за скромной площади. Но везде операторы по-разному решают проблему компенсации расходов за эту услугу: рост среднего ценника, абонентская плата, лимитный интернет и завышенные цены на внешний роуминг.
          Original source: habrahabr.ru (comments, light).

          https://habrahabr.ru/post/338974/


          Метки:  

          Как сделать gif-анимацию для Behance и Dribbble?

          Пятница, 29 Сентября 2017 г. 12:29 + в цитатник
          ilyasgaifullin сегодня в 12:29 Дизайн

          Как сделать gif-анимацию для Behance и Dribbble?



            Дизайнерам бывает сложно переводить анимированный ролик в gif и подстраиваться под ограничения анимации для Behance и Dribbble.

            Есть много статей о том, как быстро сделать анимацию в Principle, Flinto и других редакторах, но нет ничего о том, как её готовить конкретно для Behance и Dribbble.

            Мы протаптывали эту тропинку сами и поняли, какой способ для нас оптимальный. Статья будет полезна тем, кто ещё не разобрался.


            Ограничения


            Рассмотрим, какие требования предъявляют эти площадки.



            Dribbble
            Только gif-анимация, разрешение – 800 x 600. Размер – до 8 мб.

            Behance
            Gif-анимация / Embed-видео / Видео на хостинге Adobe. 50 мб для gif/картинок и 1 гб для видео/аудио. Максимальное разрешение для gif – 1400, для видео – 1200 в ширину.




            Подробнее о подготовке анимации


            Dribbble


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

            Наш воркфлоу такой:
            1. Делаем анимацию в Principle/Flinto/Framer и других.
            2. Решаем, как будет выглядеть шот, и оформляем его в мокап устройства или как-либо ещё. Обычно этот шаг мы делаем в After Effects, потому что там это сделать легче, чем в других программах.
              Чтобы видео весило немного и было качественным, нужно выбрать видеокодек H.264 для Mac (H.265, когда его можно будет использовать) и jpeg для Windows.

              Как выбрать кодек при рендере в After Effects
              Путь до выбора кодека: Output Module / Format Options / Video Codec



            3. Полученный файл mov. переводим gif и оптимизируем, если он выходит за рамки 8 мб.
              Проще всего переводить видео маленького размера в gif с помощью ezgif.

              Как сделать gif в Exgif
              Загрузить видео.

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

              Готово. Можно еще сжать gif по кнопке «Optimize», если он много весит.




            Behance


            Анимацию на Behance можно загрузить слудующими способами: видео с Adobe Cloud, Embed-видео с YouTube/Vimeo и gif-анимацией.
            Пользуемся всеми способами в зависимости от того, что надо получить.
            Рассмотрим плюсы и минусы каждого из подходов.

            Видео с Adobe
            + Можно загрузить какой угодно объём
            + Относительно без потери качества
            + Можно посмотреть в fullscreen
            + Звук
            – Нет автоплея в мобильной версии

            Embed-видео
            + Все плюсы из видео с Adobe
            – Логотип YouTube / Vimeo
            – Особенности настроек для loop видео
            – Нет автоплея в мобильной версии

            Gif-анимация
            + Можно сделать на всю ширину экрана
            + Есть автоплей
            – Нельзя промотать во время просмотра
            – Много весят и дольше загружаются

            Наш воркфлоу такой:
            1. Пункты идентичные с подготовкой видео шота на Dribbble.
            2. Конвертирование видео в gif в GifBrewery (пользовались бы и ezgif, но там нельзя сделать gif шириной в 1400px).

              Как сделать gif в GifBrewery
              Стартовый экран, выбрать Open Video.


              По дефолту GifBrewery делает нормальные гифки. Но если что-то пошло не так, то есть настройки по иконке справа. Обычно играемся только с алгоритмом выбора цвета.



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

            Что получается у нас, можно посмотреть в профилях на Behance, Dribbble.
            Original source: habrahabr.ru (comments, light).

            https://habrahabr.ru/post/338858/


            Метки:  

            Изменился способ создания чат-ботов в Viber

            Пятница, 29 Сентября 2017 г. 12:07 + в цитатник
            nllm сегодня в 12:07 Разработка

            Изменился способ создания чат-ботов в Viber

              С 26 сентября 2016 года сильно упростился способ получения токенов для чат-ботов в Viber.
              Ранее для этого необходимо было создать паблик аккаунт и только к нему можно было сделать привязку чат-бота. Примерно до января-февраля 2017 года все было достаточно просто — заполняешь простую форму — появляется возможность регистрации паблик аккаунта. Примерно весно форма усложнилась, всем подряд не давали такой возможности. Давали добро, в основном, крупным, известным брендам.

              26 сентября все упростили.

              В этот день паблик чаты и боты стали двумя отдельными каналами в Viber. Теперь можно управлять ими через Панель администратора Viber (https://partners.viber.com/).

              Что означает разделение Ботов и Паблик Чатов?
              • Боты больше не будут привязаны к Паблик Чатам, а интерфейс будет скорректирован соответствующим образом.
              • Боты по-прежнему будут доступны в рамках Паблик Аккаунтов.
              • Функции поиска и отображения в списке на Discover Screen останутся прежними.


              Что такое Панель администратора Viber?
              • Панель администратора Viber позволяет владельцам Паблик Аккаунтов и Ботов управлять своими кампаниями, видеть статистику и все данные Паблик аккаунта.
              • Кроме того, Панель администратора Viber теперь позволит пользователям создавать бот напрямую через Панель администратора Viber.


              Создание бота будет доступно для всех. Тем не менее, возможность отображения в поиске и в списке на Discover Screen будет рассматриваться командой Viber.

              Первичная регистрация
              partners.viber.com -> Create an account
              Регистрируемся. Вводим номер телефона, получаем код в viber для проверки и прочие поля.

              Создание бота (получение токена)
              Авторизуемся partners.viber.com -> Create Bot Account
              Заполняем форму (самое сложное это картинку подобрать)
              После заполнения получаем токен.
              Все.

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

              developers.viber.com/docs/api/rest-bot-api
              developers.viber.com/docs/api/python-bot-api
              developers.viber.com/docs
              Original source: habrahabr.ru (comments, light).

              https://habrahabr.ru/post/338970/


              Метки:  

              [Из песочницы] FlashMapper — альтернатива автомапперу

              Пятница, 29 Сентября 2017 г. 11:50 + в цитатник
              Birbone сегодня в 11:50 Разработка

              FlashMapper — альтернатива автомапперу

              Я даже не знаю, что такое автомаппер. Зачем мне его альтернатива?


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

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

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

              Если уже есть автомаппер, то зачем было делать еще одну подобную библиотеку?


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

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

              Синтаксис


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

              Модель с пользовательской формы
              public class UserForm
              {
                  public Guid? Id { get; set; }
                  public string FirstName { get; set; }
                  public string LastName { get; set; }
                  public string Town { get; set; }
                  public string State { get; set; }
                  public DateTime BirthDate { get; set; }
                  public string Login { get; set; }
                  public string Password { get; set; }
              }
              


              Модель базы данных
              public class UserDb
              {
                  public Guid Id { get; set; }
                  public string FirstName { get; set; }
                  public string LastName { get; set; }
                  public string Town { get; set; }
                  public string State { get; set; }
                  public DateTime BirthDate { get; set; }
                  public string Login { get; set; }
                  public string PasswordHash { get; set; }
                  public DateTime RegistrationTime { get; set; }
                  public byte[] Timestamp { get; set; }
                  public bool IsDeleted { get; set; }
              }
              


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

              public class FlashMapperInitializer : IInitializer // Не является частью FlashMapper'а
              {
                  private readonly IMappingConfiguration mappingConfiguration; // Синглтон объект, который хранит в себе все конфигурации
                  private readonly IPasswordHashCalculator passwordHashCalculator;
              
                  public FlashMapperInitializer(IMappingConfiguration mappingConfiguration, IPasswordHashCalculator passwordHashCalculator)
                  {
                      this.mappingConfiguration = mappingConfiguration;
                  }
              
                  public void Init() // Код, который запускается во время инициализации приложения
                  {
                      mappingConfiguration.CreateMapping(u => new UserDb
                      {
                          Id = u.Id ?? Guid.NewGuid(),
                          Login = u.Id.HasValue ? MappingOptions.Ignore() : u.Login,
                          RegistrationTime = u.Id.HasValue ? MappingOptions.Ignore() : DateTime.Now,
                          Timestamp = MappingOptions.Ignore(),
                          IsDeleted = false,
                          PasswordHash = passwordHashCalculator.Calculate(u.Password)
                      });
                      //mappingConfiguration.CreateMapping <...> (...); // Прочие конфигурации
                  }
              }
              

              Синтаксис напоминает создание нового объекта и инициализацию его свойств на основе объекта-источника. На деле же параметр метода CreateMapping — это лямбда-выражение, которое затем дополняется сопоставлением оставшихся свойств класса UserDb с аналогичными свойствами из класса UserForm. Также на основе этого выражения создается еще одно, которое не создает новый объект типа UserDb, но просто копирует данные в уже существующий объект. Оба выражения компилируются и сохраняются в mappingConfiguration для дальнейшего использования.

              Вот как затем можно использовать созданный маппинг:

              public class UserController : Controller
              {
                  private readonly IMappingConfiguration mappingConfiguration;
                  private readonly IRepository usersRepository;
              
                  public UserController(IMappingConfiguration mappingConfiguration, IRepository usersRepository)
                  {
                      this.mappingConfiguration = mappingConfiguration;
                      this.usersRepository = usersRepository;
                  }
              
                  [HttpPost]
                  public ActionResult Edit(UserForm model)
                  {
                      if (!ModelState.IsValid)
                          return View(model);
                      var existingUser = usersRepository.Find(model.Id);
                      if (existingUser == null)
                      {
                          var newUser = mappingConfiguration.Convert(model).To();
                          usersRepository.Add(newUser);
                      }
                      else
                      {
                          mappingConfiguration.MapData(model, existingUser);
                          usersRepository.Update(existingUser);
                      }
                      return View(model);
                  }
              }
              

              Маппинг из нескольких источников


              FlashMapper позволяет создавать маппинги с несколькими источниками (до 15):

              mappingConfiguration.CreateMapping((s1, s2, s3) => new Destination { ... });
              

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

              mappingConfiguration.CreateMapping((s1, s2, s3) => new Destination { ... }, o => o.CollisionBehavior(SelectSourceCollisionBehavior.ChooseAny));
              

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

              API для вынесения каждого маппинга в отдельный сервис


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

              Вот как при этом изменится конфигурация маппинга из примера выше:

              public interface IUserDbBuilder : IBuilder { }
              
              public class UserDbBuilder : FlashMapperBuilder, IUserDbBuilder
              {
                  private readonly IPasswordHashCalculator passwordHashCalculator;
              
                  public UserDbBuilder(IMappingConfiguration mappingConfiguration, IPasswordHashCalculator passwordHashCalculator) : base(mappingConfiguration)
                  {
                      this.passwordHashCalculator = passwordHashCalculator;
                  }
              
                  protected override void ConfigureMapping(IFlashMapperBuilderConfigurator configurator)
                  {
                      configurator.CreateMapping(u => new UserDb
                      {
                          Id = u.Id ?? Guid.NewGuid(),
                          Login = u.Id.HasValue ? MappingOptions.Ignore() : u.Login,
                          RegistrationTime = u.Id.HasValue ? MappingOptions.Ignore() : DateTime.Now,
                          Timestamp = MappingOptions.Ignore(),
                          IsDeleted = false,
                          PasswordHash = passwordHashCalculator.Calculate(u.Password)
                      });
                  }
              }
              

              Интерфейс IUserDbBuilder имеет два метода — UserDb Build(UserForm source) и void MapData(UserForm source, UserDb destination). Базовый класс FlashMapperBuilder их реализует, но оставляет для реализации метод void ConfigureMapping(IFlashMapperBuilderConfigurator configurator). Данный метод вызывается во время инициализации и создает маппинг в переданном mappingConfiguration. Заметьте, что последним generic-параметром абстрактного класса FlashMapperBuilder должна быть его реализация, в данном случае UserDbBuilder.

              Рассмотрим процесс настройки подробнее. Базовый класс FlashMapperBuilder<> также реализует интерфейс IFlashMapperBuilder, который в свою очередь предоставляет метод RegisterMapping, который должен быть вызван во время инициализации приложения. В итоге реализация UserDbBuilder должна быть зарегистрирована в IoC-контейнере для двух интерфейсов – IUserDbBuilder для дальнейшего использования, и IFlashMapperBuilder для регистрации маппинга.

              Как-то так для Ninject’а
              Kernel.Bind().To();
              


              Для упрощения этого синтаксиса, я использую небольшой extension-метод
              public static IBindingWhenInNamedWithOrOnSyntax AsFlashMapperBuilder(
                  this IBindingWhenInNamedWithOrOnSyntax builder) where T : IFlashMapperBuilder
              {
                  builder.Kernel.AddBinding(new Binding(typeof(IFlashMapperBuilder), builder.BindingConfiguration));
                  return builder;
              } 
              

              В результате регистрацию билдера в контейнере можно переписать так:
              Kernel.Bind().To().AsFlashMapperBuilder();
              


              В библиотеке имеется сервис IFlashMapperBuildersRegistrationService, с методом RegisterAllBuilders, этот метод должен быть вызван во время инициализации приложения. Сервис просто берет все реализации интерфейса IFlashMapperBuilder, которые переданы ему в конструктор, и вызывает у них метод RegisterMappings.

              Вот пример использования сервиса IUserDbBuilder
              public class UserController : Controller
              {
                  private readonly IUserDbBuilder userDbBuilder;
                  private readonly IRepository usersRepository;
              
                  public UserController(IUserDbBuilder userDbBuilder, IRepository usersRepository)
                  {
                      this.userDbBuilder = userDbBuilder;
                      this.usersRepository = usersRepository;
                  }
              
                  [HttpPost]
                  public ActionResult Edit(UserForm model)
                  {
                      if (!ModelState.IsValid)
                          return View(model);
                      var existingUser = usersRepository.Find(model.Id);
                      if (existingUser == null)
                      {
                          var newUser = userDbBuilder.Build(model);
                          usersRepository.Add(newUser);
                      }
                      else
                      {
                          userDbBuilder.MapData(model, existingUser);
                          usersRepository.Update(existingUser);
                      }
                      return View(model);
                  }
              }
              


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

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

              Именно из-за этого последним generic-параметром класса FlashMapperBuilder должен быть сам билдер. Конечно можно было бы и во время рантайма вычислить этот тип, но мне хотелось избежать любого late binding’а при использовании уже готовых скомпилированных маппингов.

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

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


              Меня особо никогда не беспокоила производительность автомаппера, но от коллег я слышал, что работает он раз в 10 медленнее аналогичного ручного маппинга. Я тогда решил, что умнее всех, и у меня-то уж получится производительность сопоставимая с ручным маппингом. Казалось бы, при использовании готового маппинга у меня уже есть скомпилированный метод, IL-код которого должен совпадать с IL-кодом метода, выполняющего аналогичную работу, но написанного руками, никакого late binding’а при использовании маппингов нет, нужно только получить указатель на метод и вызвать его. Но на деле производительность моего маппера получилась сопоставимой с производительностью автомаппера.

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

              Я начал гуглить, что вообще делает этот метод. В результатах поиска был некий файл jithelpers.h из сорцов дотнета на гихабе, а также вопросы на stackoverflow от людей, также обеспокоенных производительностью скомпилированных лямбда-выражений. Со stackoverflow я узнал, что этот метод как-то связан с CAS (code access security), а также нашел другой способ компиляции лямбда-выражений. Я решил просто слепо воспользоваться этим способом, посмотреть какая получится производительность. И, о чудо, мой скомпилированный из выражения метод стал работать с такой же скоростью, как и аналогичный ручной метод. Но, к сожалению, после этого у меня свалились два теста.

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

              Я стал разбираться, что к чему. Способ компиляции лямбда-выражений, который посоветовали на stackoverflow использует более старый и низкоуровневый API дотнета для генерации IL-кода. Это классы MethodBuilder, TypeBuilder и прочие. Нужно было руками их создать, определить параметры, передать экземпляр MethodBuilder’а в метод CompileToMethod лямбда-выражения. Данный метод записывал IL-код непосредственно того, что было в выражении, поэтому результирующий метод и работал быстро. После этого надо было создать динамический тип из инстанса класса TypeBuilder, и выдернуть из него нужный метод рефлексией. Понятное дело, что раз я создаю новый тип и метод определяется уже в нем, то у метода пропадает доступ к приватным полям класса, в котором было определено лямбда-выражение. Стандартный же способ компиляции лямбда-выражения – это простой вызов метода Compile, данный метод появился в более поздних версиях дотнета, и использует более новый API. Это класс DynamicMethod. Он не требует руками создавать динамические типы и прочее, а просто предоставляет методы для записи в него IL-кода и получения результата. Хотя внутри он точно также создает новый тип, но он умеет каким-то образом отключать проверку доступности членов класса во время выполнения, у него даже есть специальный флаг для этого в конструкторе. И, судя по всему, он и добавляет вызов JIT_ MethodAccessCheck в скомпилированный метод.

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

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

              image

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

              Заключение


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

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

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

              Сборка с АПИ для вынесения конфигураций в сервисы

              Исходники на гитхабе

              Документация
              Original source: habrahabr.ru (comments, light).

              https://habrahabr.ru/post/338968/


              Метки:  

              Мониторинг инженерной инфраструктуры в дата-центре. Часть 3. Система холодоснабжения

              Пятница, 29 Сентября 2017 г. 11:09 + в цитатник
              dataline сегодня в 11:09 Администрирование

              Мониторинг инженерной инфраструктуры в дата-центре. Часть 3. Система холодоснабжения


                Система охлаждения NORD-4.

                Мониторинг инженерной инфраструктуры в дата-центре. Часть 1. Основные моменты
                Мониторинг инженерной инфраструктуры в дата-центре. Часть 2. Система энергоснабжения

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

                Сегодня поговорим о том, как устроена система мониторинга холодоснабжения на примере дата-центра NORD-4. Перед прочтением рекомендуем ознакомиться со статьей о том, как создавалась система холодоснабжения.

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

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

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


                Дашборд с параметрами.

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


                Зеленым цветом обозначены штатно работающие чиллеры, белым – отключенные. Если что-то пошло не так, индикатор загорается красным цветом.

                Датчики температуры


                Параметр-фронтмен в системе мониторинга – это температура в холодных коридорах машинных залов. Средняя температура в залах колеблется в пределах от 23 до 27 Сo. При такой температуре оборудование еще не греется, но уже не покрывается инеем :). Этот параметр прописан в SLA, и за его несоблюдение придется платить штраф заказчику. От него мы и «пляшем», настраивая всю систему холодоснабжения в дата-центре.

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


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

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

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

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

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


                Температурный датчик, установленный на стойке.

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


                Взаимодействие наружного и внутреннего контуров в чиллерной схеме.

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

                Для регулировки температуры мы используем трехходовой клапан (ТХК). Он регулирует объем воды, выходящей из теплообменника. Если температура повышается, клапан открывается сильнее и подает больше воды в теплообменник. Текущий процент открытия ТХК выводится в интерфейс системы мониторинга.

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

                • на входе и выходе чиллера;
                • на входе и выходе теплообменника;
                • на входе и выходе из кондиционера.

                Температура «за бортом». Этот показатель не касается дата-центра напрямую, но он также важен в мониторинге. Мы не пользуемся средними показателями по Москве, так как температура воздуха на Боровой и на Коровинском может иметь перепад в несколько градусов. Нас интересует погода именно там, где стоит оборудование.

                На каждой локации установлена независимая метеостанция, считывающая температуру, влажность и скорость ветра. Эти данные показывают, как работает система кондиционирования в реальных погодных условиях на конкретном объекте. Поскольку в Москве годовой перепад может составлять от –35 Сo до +35Сo, мы обязаны следить за погодой и заранее готовиться к ее причудам.


                Так выглядит установленная на объекте независимая метеостанция.

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


                Данные температуры и влажности с метеостанции в дата-центре на дашборде системы мониторинга.

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

                Другие датчики


                Датчики протечек. В каждом машинном зале NORD-4 установлено по 14 кондиционеров Stulz. Они оснащены заводскими датчиками протечек, но для мониторинга их недостаточно. В местах задвижек, стыков труб, на теплообменнике, под кондиционерами и в других критичных узлах мы установили независимую сеть датчиков. Данные от них собираются и поступают в общую систему.

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


                Так на дашборд выводятся сработавшие датчики протечек.

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

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

                Сложности в построении системы мониторинга


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

                Запуская систему мониторинга, мы выставляли пороговые значения, руководствуясь проектным решением. Так, показатель WATER OUT (вода, которая поступает в кондиционеры в залах) должен составлять стабильные 18 Сo. Исходя из этого вычисляем остальные значения и создаем таблицу «идеальных» параметров.

                По мере заполнения ЦОДа контрольная панель может начать выдавать ложные предупреждения. Это крайне опасно, так как внимание специалиста рассеивается, и он может пропустить настоящую проблему. Мы называем это «перемониторингом»: на новом оборудовании показатели могут слегка плавать внутри пороговых значений, создавая варнинги.

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

                Пара советов


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

                • Звуковое оповещение в центре мониторинга.
                • Отображение на экране. За дашбордом круглосуточно следит минимум один инженер.
                • SMS- и email-уведомления ответственным специалистам.

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

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


                Дежурная смена за работой.

                Мониторинг N+1. Продумайте резервирование системы мониторинга, чтобы исключить утрату контроля над дата-центром. У нас большинство устройств последовательно соединены по протоколу ModBus RS-485, и на этапе проектирования дата-центра мы продумывали, как пойдут трассы системы мониторинга, и прокладывали резервные маршруты.

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

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

                На этом всё. В следующей статье серии мы расскажем о мониторинге сетевой инфраструктуры. Ждём ваших вопросов.
                Original source: habrahabr.ru (comments, light).

                https://habrahabr.ru/post/338966/


                Kotlin, puzzlers and 2 Kekses: Вы уверены, что знаете, как ведет себя Kotlin?

                Пятница, 29 Сентября 2017 г. 10:42 + в цитатник
                BigSolarWolf сегодня в 10:42 Разработка

                Kotlin, puzzlers and 2 Kekses: Вы уверены, что знаете, как ведет себя Kotlin?

                  Вначале была Java (ладно, не то чтобы в самом начале… но наша история начинается именно здесь), шло время, и спустя 20 с небольшим лет умные ребята из JetBrains спроектировали и зарелизили Kotlin, «более лучшую» Java, универсальный язык, понятный, мощный и прозрачный.

                  В свое время Андрей abreslav Бреслав говорил, что Kotlin разрабатывался как удобный и предсказуемый язык. Тогда же прозвучало мнение, что в этом языке вы не найдете паззлеров (коротких кусочков кода, результаты выполнения которых оказываются неожиданными, пугающими или разочаровывающими). Ну что же, Антон antonkeks Кекс поколдовал в IDEA и кое-что все-таки накопал, да еще на наглядных примерах рассказал о своих находках в паре с Филиппом Кексом. Смотрите сами:





                  Под катом — подборка таких пазлеров и развернутые комментарии к ним. В основе материала доклад Антона Кекса (Codeborne) и Филиппа Кекса (Creative mobile) на конференции Мобиус 2017 (Санкт-Петербург).

                  Начнем с Котлина. Все говорят, что на Яве куча проблем: на острове куча вулканов, там землетрясения. Ее нужно спасать.


                  Поэтому на ум приходит другой остров — Котлин.


                  Там спокойно, ничего не происходит. Он очень плоский, никаких вулканов. Находится здесь рядом. Поэтому Kotlin — это спаситель Java, особенно для Android-разработчиков — таких, как мы.

                  Несколько слов о Котлине


                  Что такое Kotlin, здесь более-менее все знают. Потому что какой дурак сегодня пишет под Android без Котлина? Это, мне кажется, уже мазохизм. Он отлично работает. Пару недель назад вышел первый билд Kotlin native. Скоро, может быть, будем и под iOS писать на Котлине.

                  Это прагматический язык, open-source, очень прикольный тулинг — он был задизайнен, чтобы хорошо работала IDE. Это камень в огород Apple-овского языка Swift и ему подобных. JetBrains хорошо push-ит Kotlin — специально дизайнит язык под свою IDE.

                  Все мы знаем, что Kotlin очень долго разрабатывался. Прошло шесть лет, прежде чем была выпущена версия 1.0. JetBrains очень старались, но, видимо, сделать новый язык не так просто. За прошедшие годы (2010 — 2016) они даже успели поменять логотип на более современный.


                  Учитывая, как долго его разрабатывали, язык должен быть превосходный. Это должен быть самый лучший язык в мире, так как многие другие языки девелопились гораздо быстрее. Например, всем известно, что JavaScript был сделан за две недели. Хотя, это, конечно, не rocket science (rocket science — это SpaceX, которые за четыре года научились садиться на платформу на настоящей ракете).

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


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

                  Пазлеры


                  Что такое пазлеры?

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

                  Первая половина пазлеров ориентирована на тех, кто не очень хорошо знаком с Kotlin; вторая половина — для хардкорных Kotlin-разработчиков.

                  Kotlin известен тем, что не повторяет некоторые известные пазлеры Java. Однако в идеальном языке программирования не должно быть пазлеров вообще. Получается, что и Kotlin не идеален — не бывает идеальных языков.

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

                  Все демонстрируемые пазлеры запускаются с Kotlin 1.1.1 — с последней стабильной версией. Исходные коды пазлеров находятся на GitHub — потом их можно посмотреть: https://github.com/angryziber/kotlin-puzzlers/tree/mobius.
                  У кого появятся идеи новых пазлеров, присылайте pull-реквесты. Ждем.

                  Пазлер 1


                  Котлин хорош тем, что он поддерживает nullability, точнее, он null safe — можно так сказать.

                  package p1_nullean
                  
                  val s: String? = null
                  if (s?.isEmpty()) println("true")
                  

                  У него есть различия между nullable и не nullable типами. Это значит, если мы хотим присвоить куда-то null, это должен быть nullable-тип (с вопросиком). Вероятно, эту идею предложил C#, но он ее не доделал — там только примитивы могут быть nullable. А в Котлине это уже сделано нормально для всех типов. В принципе, язык рассчитан на то, чтобы никогда вы не получали страшных NullPointerException в рантайме.

                  В данном примере Котлин перенял из Groovy отличный null-safe оператор s?, который позволяет на нуле вызвать какой-то метод и не схлопотать сразу в рантайме какие-то эксепшены.

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

                  • nothing
                  • true
                  • NullPointerException
                  • Will not compile

                  Запускаем. Смотрим.



                  Не скомпилировалось.

                  Почему?

                  Котлин — type safe язык, поэтому результат выражения s?.isEmpty() — null, поэтому он не кастится в false.

                  Исправить легко (надо написать так, чтобы Котлин вел себя так же, как Groovy):

                  if (s?.isEmpty() ?: false) println("true")
                  

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

                  Пазлер 2


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

                  package p2_nulleanExtended
                  val x: String? = null
                  print(x.isNullOrEmpty())
                  

                  Какой будет результат?

                  • true
                  • false
                  • NullPointerException
                  • не скомпилируется

                  Запускаем…
                  Ответ: true.



                  Почему? Это extension-функция из стандартной библиотеки Kotlin, которая «повешена» на nullable CharSequence. Поэтому в подобном кейсе она обрабатывается нормально.



                  Действительно, в Котлине можно некоторые функции запускать на null. Компилятор про это знает и позволяет это делать.

                  Если бы мы поставили знак вопроса, IDEA бы нам сказала, что он тут не нужен.

                  print(x?.isNullOrEmpty())
                  

                  Хорошо, что функция названа по-человечески (о результате можно догадаться по названию).

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

                  Пазлер 3


                  package p3_platformNulls
                  
                  class Kotlin {
                      fun hello(name: String) = print("Hello $name")
                  }
                  
                  fun main(args: Array) {
                      val prop = System.getProperty("key")
                      Kotlin().hello(prop)
                  }
                  

                  В Котлине есть такая интересная фича — третий стейт nullability. Посмотрим, что будет, если мы вызовем:

                  val prop = System.getProperty("key")
                  

                  и передадим это в метод hello у класса Kotlin, который должен его распечатать:

                  Kotlin().hello(prop)
                  

                  Что получится на выходе?

                  • hello
                  • hello null
                  • не скомпилируется
                  • ни один из приведенных вариантов

                  Вообще type inference — отличная тема.

                  Запускаем. Получаем IllegalStateExeption.



                  Почему?

                  Значение prop будет null, тип — String!.. Он идет в hello, и в рантайме будет проверка, что должен быть не null, а он null.

                  На самом деле в начальной версии Котлина действительно сделали, что когда из Java приходит String, он всегда по умолчанию nullable.

                  val prop: String? = System.getProperty("key")
                  


                  Это привело к тому, что стало очень неудобно писать код, когда идет интероп с Java. И решили сделать виртуальный тип String! (с восклицательным знаком). Это как раз третий вариант nullability — называется «я не знаю».

                  val prop: String! = System.getProperty("key")
                  

                  Однако такой код не компилируется, поскольку тип String! нельзя объявить самостоятельно. Он может прийти только из Java.

                  Поэтому такие штуки лучше заранее объявлять как nullable или не nullable (как правило, вы из API знаете, может там null когда-нибудь прийти или нет).

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

                  val prop: String = System.getProperty("key")
                  

                  IDEA всегда знает, где какой тип. Можно нажать на переменной Ctrl+q и выяснить.
                  Но закончим с nullability, перейдем к другой теме.

                  Пазлер 4


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

                  package p4_kotlinVsScala
                  
                  fun main1() = print("Hello")
                  
                  fun main2() = {
                      print("Hello2")
                  }
                  
                  main1()
                  main2()
                  

                  Что будет на выходе?

                  • Hello
                  • Hello2
                  • HelloHello2
                  • не скомпилируется

                  Запускаем… Получаем Hello.



                  Почему?

                  Main1 вернет юнит, но при этом вызовет print(«Hello»). А main2 всего лишь вернет лямбду, которая не будет выполняться.

                  Исправить можно так:

                  main2()()
                  

                  Второй, на мой взгляд, лучший вариант исправления — убрать знак равно у main2, поскольку он всех только смущает:

                  fun main2() {
                  print("Hello 2")
                  }
                  

                  Почему я назвал этот пример Котлин vs Scala? Те, кто писал на Scala, знают, что там этот код — абсолютно валидное объявление функции, которая что-то возвращает:

                  fun main2() = { }
                  

                  Бедные Scala-девелоперы, которые будут писать на Котлине. Они, наверное, постоянно будут возвращать лямбды без запуска.

                  Пазлер 5


                  У нас есть list из цифр, мы его перебираем методом forEach. ForEach, как и в Groovy, если параметр лямбда не объявлен, знает it. И мы проверяем, что он не больше 2, и печатаем.

                  package p5_sneakyReturn
                  
                  fun main(args: Array) {
                      listOf(1, 2, 3).forEach {
                          if (it > 2) return
                          print(it)
                      }
                      print("ok")
                  }
                  

                  Какой будет итог?

                  • 123ok
                  • 12ok
                  • 12
                  • бесконечный цикл

                  Запускаем…



                  12
                  Что за ерунда?

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

                  if (it > 2) return@forEach
                  

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

                  На самом деле return в Котлине работает так, как он должен работать. Если бы мы до этого не писали бы на C# и Java, наверное, и не ошиблись бы, потому что return возвращается из функции main. Все логично. И нет никаких странных фич с лямбдами, из которых тоже почему-то нужно выйти.

                  Почему это так работает?



                  Функция forEach объявлена как inline функция. В Kotlin компилятор не вызывает эту функцию в скомпилированном коде, а берет код этой функции и вставляет на то место, где был call. В результате здесь получается обычный for-цикл и, естественно, тогда return выходит из функции main.

                  Как понять, что это Inline функция? Во-первых, в IDEA есть Ctrl+p. А во-вторых, если вызвать return, а функция окажется не inline, то компилятор скажет: «Извини, нельзя это делать». То есть компилятор не позволит нам сделать какую-то ерунду.

                  Есть еще один вариант, как можно исправить этот код, чтобы он возвращал «12ok». Нужно это объявить как функцию, а не лямбду.

                  fun main(args: Array) {
                      listOf(1, 2, 3).forEach(fun() {
                          if (it > 2) return
                          print(it)
                      })
                      print("ok")
                  }
                  

                  Единственное отличие в Котлине анонимной функции и лямбды в том, что первая ведет себя именно как функция, а значит — return будет возвращать из ближайшего «веселья» (fun). Поэтому с таким исправлением оно будет работать как надо.

                  Чтобы было еще интереснее, я подготовил несколько примеров. В Котлине бывают разные кейворды:

                  • fun
                  • inline fun
                  • inline fun с лямбдой noinline
                  • inline fun с лямбдой crossinline

                  Некоторые из них позволяют использовать return, а некоторые — нет.

                  package p5_sneakyReturn
                  
                  fun hello(block: () -> Unit) = block()
                  
                  inline fun helloInline(block: () -> Unit) = block()
                  
                  inline fun helloNoInline(noinline block: () -> Unit) = hello(block)
                  
                  inline fun helloCrossInline(crossinline block: () -> Unit) = runnable { block() }.run()
                  
                  fun main(args: Array) {
                      hello {
                          println("hello")
                          //return - impossible
                      }
                  
                      hello(fun() {
                          println("hello")
                          return
                      })
                  
                      helloInline {
                          println("hello")
                          return
                      }
                  
                      helloNoInline {
                          println("hello")
                          //return - impossible
                      }
                  
                      helloCrossInline {
                          println("hello")
                          //return - impossible
                      }
                  
                  

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

                  Когда я только начал писать на Котлине, я тоже подумал, что это нечто сложное. Но когда ты понимаешь, что такое inline-функция (почти все extension-функции для коллекции — Inline для performance), все становится очень логичным.

                  Пазлер 6


                  Нам нужно получить John или Jaan.

                  У нас есть простой класс Person. В Котлине очень удобно: можно при декларации класса сразу продекларировать конструктор. Мы получаем переменную конструктора name, забиваем ее в property. В Котлине нет field — есть только property, что очень круто, так как не нужно писать геттер, сеттеры и всякую ерунду (или геты и сеты, как в C#). Отличный красивый синтаксис.

                  В итоге мы создаем Person с именем John и смотрим, превратится ли он у нас в эстонскую локализацию Jaan:

                  package p6_getMeJohn
                  
                  class Person(name: String) {
                      var name = name
                          get() = if (name == "John") "Jaan" else name
                  }
                  
                  println(Person("John").name)
                  

                  • John
                  • Jaan
                  • не скомпилируется
                  • ни один из вариантов

                  Запускаем…



                  Это stack overflow.
                  Почему?
                  Мы берем name, делаем ему if-else и вызываем его же по get. Чтобы исправить, нужно обратиться к полю, а не к property. Можно использовать кейворд field:

                  class Person(name: String) {
                      var name = name
                          get() = if (field == "John") "Jaan" else field
                  }
                  

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

                  Говорят, что по перформансу все это круто, потому что Java Hotspot компилятор это хорошо оптимизирует, в отличие от виртуальных машин .NET, и все работает очень быстро.

                  Пазлер 7


                  Снова смотрим на офигенную фичу языка — type inference — нас не волнует, какого типа whatAmI, мы его можем все равно использовать. Но компилятор знает, что это такое. Посмотрим, знаем ли мы.

                  package p7_whatAmI
                  
                  val whatAmI = {}()
                  println(whatAmI)
                  

                  Какой вариант будет в итоге?

                  • kotlin.jvm.functions.Function0
                  • () -> kotlin.Unit
                  • kotlin.Unit
                  • ничего

                  Запускаем… Получаем kotlin.Unit.



                  Почему?

                  Здесь объявляется лямбда, потом происходит вызов лямбды. Так как лямбда ничего не возвращает (точнее, возвращает kotlin.Unit), именно это и выводится. А самое лучшее определение unit — это void.

                  Откуда вообще пришел Unit? По-моему, даже в математике (или в computer science) есть такое понятие как теория типов. И там описано, что Unit — это один элемент, который означает «ничего». Поэтому некоторые более академические языки программирования используют термин Unit. Котлин был задизайнен как прагматичный язык, но, тем не менее, его разработчики решили выбрать не прагматичный void, а придумали сделать Unit.

                  Чтобы вам было еще интереснее, в Котлине есть еще один тип: kotlin.Nothing.
                  Чем они отличаются? Пусть ответ на этот вопрос будет вам домашним заданием.

                  Пазлер 8


                  Мы посмотрели whatAmI, а теперь у нас будет iAmThis.

                  Здесь все немного усложняется: у нас есть класс IAm, он — data class (это офигенная фича в Kotlin, которая за нас автоматически генерирует equal, hashCode, toString и весь этот boiler plate, который мы все так ненавидим писать на Java). В Scala это case class — там название для этого хуже, хотя на самом деле все используют его именно как data class.

                  У класса IAm есть конструктор, в котором объявляем поле foo. Foo одновременно является property, поэтому его можно использовать с функцией hello().

                  Мы передаем туда String «bar», вызываем функцию hello и смотрим, что она нам возвращает.

                  package p8_iAmThis
                  
                  data class IAm(var foo: String) {
                      fun hello() = foo.apply {
                          return this
                      }
                  }
                  
                  println(IAm("bar").hello())
                  

                  Что получим на выходе?

                  • IAm
                  • IAm(foo=bar)
                  • bar
                  • не скомпилируется

                  Запускаем… Получаем bar



                  Почему?

                  Apply — хитрая extension-функция. Она принимает лямбду и позволяет внутри нее с объектом, на котором она вызвана, выполнять какие-то действия по this. Соответственно, this — это bar. И Hello — это bar.

                  В этом Kotlin похож на JavaScript. Как в JavaScript, в Kotlin можно достичь того состояния, когда вы уже не знаете, что такое this.

                  Вообще там есть много полезных функций: also, let, with.



                  В принципе, они все отличаются достаточно мало.

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

                  Есть и другие функции. Код можно исправить следующим образом:

                  package p8_iAmThis
                  
                  data class IAm(var foo: String) {
                      fun hello() = foo.let {
                          return it
                      }
                  }
                  
                  println(IAm("bar").hello())
                  

                  Тогда это, может быть, станет менее непонятно.

                  Для некоторых кейсов Apply — очень полезная функция. Но если вы очень быстро смотрите на код (и при этом используется первый вариант записи), можно запутаться.

                  В первом варианте можно сократить код так (функция apply и сама возвращает this, поэтому ничего не меняется):

                  data class IAm(var foo: String) {
                      fun hello() = foo.apply {
                      }
                  }
                  

                  Пазлер 9


                  Посмотрим на уже известную нам функцию let.

                  Этот пазлер прислал Kevin Most из Канады. У него есть простая функция, которая печатает знак аргумента (Int).

                  package p9_weirdChaining
                  // by Kevin Most @kevinmost
                  
                  fun printNumberSign(num; Int) {
                      if (num < 0) {
                          "negative"
                      }  else if (num > 0) {
                          "positive"
                      } else {
                          "zero"
                      }.let { println(it) }
                  }
                  printNumberSign(-2)
                  printNumberSign(0)
                  printNumberSign(2)
                  

                  Что такой код будет печатать?

                  • negative; zero; positive
                  • negative; zero
                  • negative; positive
                  • zero; positive

                  Запускаем… На выходе — zero; positive.



                  В чем же дело?

                  If — это на самом деле выражение. То есть получается два выражения, и let применяется только ко второму.

                  Я много писал на Kotlin, но этот пазлер сам не решил. Это какая-то адская тема. На предыдущей конференции JPoint мы даже думали, что это баг в компиляторе. Но я спросил у Андрея Бреслава, и выяснилось, что это просто нюанс парсера.

                  Как исправить? Легко — достаточно поставить скобки:

                  fun printNumberSign(num; Int) {
                      (if (num < 0) {
                          "negative"
                      }  else if (num > 0) {
                          "positive"
                      } else {
                          "zero"
                      }).let { println(it) }
                  }
                  

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

                  fun printNumberSign(num; Int) {
                      if (num < 0) {
                          "negative"
                      }  else (if (num > 0) {
                          "positive"
                      } else {
                          "zero"
                      }).let { println(it) }
                  }
                  

                  При этом верхний expression идет отдельно — к нему функция let не применяется.
                  Оператора elseif в Котлине нет (если бы он был, тогда бы этого пазлера бы и не было).

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

                  Пазлер 10


                  Еще более интересный пазлер. Тут много кода.

                  Этот пазлер засабмиттил Даниил Водопьян. Это пазлер на очень классную фичу в Kotlin — delegate properties. В Котлине мы можем объявить, например, что в классе есть несколько properties, и они имплементируются не как field, а как лукапы из map.

                  У нас есть класс Population — население. А cities нам передает (var cities: Map) и мы делегируем их в этот map.

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

                  Потом мы создаем инстанс класса Population и передаем ему для всех городов население.

                  Теперь представим, что прошло много лет. Люди загадили Землю — улетели жить на Марс. Поэтому мы сбрасываем map с населением.

                  Здесь есть функция with, которую мы смотрели до этого. Она берет population и ресолвит относительно него имеющиеся field-ы (в принципе, точно также, как и apply).

                  package p10_mappedDelegates
                  // by Daniil Vodopian @voddan
                  
                  class Population(var cities: Map) {
                      val tallinn by cities
                      val kronstadt by cities
                      val st_petersburg by cities
                  }
                  
                  val population = Population(mapOf(
                      "st_petersburg" to 5_281_579,
                      "tallinn" to 407_947,
                      "kronstadt" to 43_005
                  ))
                  
                  // Many years have passed, now all humans live on Mars 
                  population.cities = emptyMap()
                  
                  with(population) {
                      println("$tallinn; $kronstadt; $st_petersburg")
                  }
                  

                  Все легко. Осталось только понять, что станет с нашей Землей, когда все улетят на Марс. Что такой код выдаст?

                  • 0; 0; 0
                  • 407947; 43005; 5281579
                  • NullPointerException
                  • NoSuchElementException

                  Запускаем… Оказывается, люди никуда не исчезли (на Марсе жить очень сложно, поэтому мы, скорее всего, останемся на Земле).



                  Почему?

                  Неверно сказать, что population.cities = emptyMap() сделает пустую map у класса, но не у его экземпляра. Если мы изменим код так (сделаем MutableMap и обнулим Кронштадт — population.kronstadt = 0):

                  class Population(var cities: MutableMap) {
                      val tallinn by cities
                      var kronstadt by cities
                      val st_petersburg by cities
                  }
                  
                  val population = Population(mutablemapOf(
                      "st_petersburg" to 5_281_579,
                      "tallinn" to 407_947,
                      "kronstadt" to 43_005
                  ))
                  
                  // Many years have passed, now all humans live on Mars 
                  population.kronstadt = 0
                  
                  

                  Код выведет: 407947; 0; 5281579

                  Но обсуждаем мы все-таки первый вариант (c population.cities = emptyMap()).

                  Когда мы исполняем delegate, ссылка на map запоминается внутри геттера (для каждого из них). И если мы меняем ссылку на cities, это уже не меняет ссылки внутри геттеров. Но мы можем даже в cities положить в map другое, и все будет работать, поскольку это все равно остается ссылка на тот же самый map. Но если мы меняем референс на другой map, то он перестает действовать.

                  Пазлер 11


                  У нас в Эстонии есть отличная поговорка: «У хорошего ребенка есть много имен».

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

                  В Котлине есть такой странный нюанс: классы по умолчанию final — их нельзя проэкстендить. Есть кейворд open, который все-таки позволяет их экстендить.

                  В этом пазлере в классе C у нас есть open-метод (тоже, чтобы мы могли его заоверрайдить). Здесь мы берем x и y (у них есть дефолтные значения — это очень классная фича в языке).

                  У нас есть класс D, который экстендит класс C и оверрайдит функцию sum, но в принципе ничего полезного не делает, кроме того, что вызывает супер-имплементацию.

                  Дальше у нас есть переменная d — мы создаем инстанс класса D; у нас есть переменная c и туда мы присваиваем тот же самый инстанс (получаем 2 референса на один и тот же инстанс класса D). И мы вызываем один и тот же метод по сути на одном и том же объекте.

                  package p11_goodChildHasManyNames
                  
                  open class C {
                    open fun sum(x: Int = 1, y: Int = 2): Int = x + y
                  }
                  
                  class D : C() {
                    override fun sum(y: Int, x: Int): Int = super.sum(x, y)
                  }
                  
                  val d: D = D()
                  val c: C = d
                  print(c.sum(x = 0))
                  print(d.sum(x = 0))
                  println()
                  

                  Что получим в итоге?

                  • 22
                  • 11
                  • 21
                  • не скомпилируется

                  Запускаем… Правильный ответ — 21.



                  Здесь еще есть некоторые warning-и, которые помогают понять, что происходит.

                  В обоих случая вызывается переопределенная функция, потому что полиморфизм. В рантайме выбирается, какая функция вызывается, потому что в реальности и c, и d — это инстанс класса D. Но так как у JVM нет такой фичи, как именные параметры, их ресолвит компилятор от compile-time. Т.е. получается, что функция выбирается и вызывается в рантайме, а параметры выбираются в compile-time. Поэтому какие параметры он подставляет, зависит от типа переменной, а не объекта, получающегося в рантайме. Это косяк. Warning-и предупреждают, что не следует путать свои названия — когда вы оверрайдите функцию, ее надо назвать иначе.

                  Хорошая новость в том, что примерно для половины представленных пазлеров в IDEA уже есть warning. Благодаря тому, что JetBrains сами занимаются еще и инструментами, они достаточно хорошо помогают избегать многих ошибок. Но не всех. Для некоторых из пазлеров warning сделать попросту невозможно.

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

                  Вместо заключения хочу сказать: переходите на Kotlin.

                  • Под Android до сих пор нет нормальной Java 8, а в Котлине вы получаете все фичи Java 8 и даже еще больше. Можно гораздо лучше себя выражать.
                  • Котлин — язык без большого хайпа. Это тоже его плюс.
                  • Его часто называют «Swift» для Android. Но со Swift есть небольшая проблема — когда выходит новая версия, приходится постоянно переписывать весь код. С Котлиным такой проблемы нет — нам обещают обратную совместимость, как и source-level, так и binary-level.
                  • Kotlin компилируется гораздо быстрее, чем Scala. Он гораздо проще Scala.
                  • Он гораздо быстрее в рантайме, чем Groovy. Если вы добавляете свое приложение на Android, то размер по-моему увеличивается всего на 600 Кб по сравнению с Java — и это очень мало по сравнению со Scala. Поэтому есть смысл на нем писать.
                  • Когда я на него перешел, я начал быть продуктивным уже с первого дня.
                  • Про Kotlin говорят, что это «более хороший Groovy», там есть хорошие фичи.
                  • И ваш самый главный друг в IDEA — это Ctrl+Alt+Shift+K, который сконвертирует любой класс Java сразу в Kotlin (as is). При этом нет Ctrl+Alt+Shift+J, поэтому вы не можете уже вернуться — это дорога в один конец. Да вы и не захотите возвращаться.
                  • Также переходит Gradle.

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



                  Если любите нутрянку программирования так же, как и мы, и хотите основательнее погрузиться в Kotlin, рекомендуем обратить внимание вот на эти доклады, которые будут на грядущей конференции Mobius 2017 Moscow:
                  Original source: habrahabr.ru (comments, light).

                  https://habrahabr.ru/post/338924/


                  Метки:  

                  Действительно, а что такое CRM-система?

                  Пятница, 29 Сентября 2017 г. 10:37 + в цитатник
                  Axelus сегодня в 10:37 Управление

                  Действительно, а что такое CRM-система?

                    Этот вопрос мы слышим на протяжении 11 лет, от владельцев бизнеса, сотрудников, разработчиков, праздно шатающихся по сайту людей. И вот он нам был вновь задан уже здесь, на Хабре. Мы изменили привычке отвечать на все комментарии и проигнорировали вопрос jt3k, чтобы ответить на него максимально развёрнуто. Оказалось не так-то просто. А вы знаете, что такое CRM?



                    Пытаемся сформулировать определение


                    Единого канонического определения не существует — и это исходит из логики самого типа программного обеспечения — CRM-системы. Дело в том, что изначально CRM — это концепция управления взаимоотношениями с клиентами, а она у каждой компании своя. Значит, и определение CRM как софта у всех тоже своё, что мы как вендоры по полной и получаем на входе. Вот неполный список того, как обзывают систему: «программка», «софтинка», «программа для продаж», «ERP», «диаграмма Гантта», «планер», «контакт-менеджер». А между тем, практически любая CRM сочетает в себе все эти именования (разве кроме ERP — это уже более замороченная история).

                    Поэтому начнём с ответа на вопрос jt3k — что такое CRM, чтобы это объяснить программисту.



                    Если говорить грубо, то это что-то вроде Jira для коммерческой службы: продажники и маркетологи ведут в системе юзеров (клиентов), заводят инциденты (сделки), прописывают бизнес-процессы (как мы это делаем в рамках ITSM), потом строят отчётность и считают деньги. А если рассуждать профессионально, то CRM-система — это центральное хранилище информации о клиентах, сделках и иных воздействиях, у которого есть пользовательский интерфейс с набором функций. Соответственно, CRM выполняет самую привычную работу: накапливает данные, хранит данные, обменивается ими, автоматизирует процессы и логирует действия.

                    Если говорить с точки зрения руководителя компании, то CRM-система — инструмент управления клиентами и связанной с ними информацией, а также способ ускорить и упорядочить все бизнес-процессы. Заметьте, ни слова о контроле! Вся контролирующая функция CRM — это отчёты по итогам месяца, аналитика и KPI (если они внедрены в компании). CRM не следит за сотрудниками, не контролирует трафик, не палит пиратские программы, не сечёт бездействие работника — для этого есть ПО другого типа.

                    Но больше всего нам нравится определение, которое мы вычитали у коллег из Salesforce: CRM — это нервная система компании.

                    Откуда такая многоликость?


                    Она возникла не так давно — уже в начале 2000-х, когда рынок переполнился решениями всех типов, которые разработчики предпочли назвать CRM. На самом деле, всё и есть CRM, только они разного типа и разных возможностей.



                    Главная классификация, которую вы можете встретить, это разделение всех CRM-систем на три типа


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

                    Аналитические CRM — системы, в которых превалирует аналитика. Это могут быть системы с гибкими отчётами и дизайнерами отчётов, они могут включать модули BI и OLAP. Как правило, это дорогие решения, которые подходят для сложной интеграции, например, с биллингом сотового оператора или платёжной системой интернет-магазина. Бывает, что  аналитическая CRM не содержит части функций управления продажами, маркетингом и процессами, а служит для целей сегментации, управления ассортиментом и т.д.

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

                    Если речь идёт просто об управлении продажами (клиенты+сделки+аналитика), то это раньше называли SFA (sales force automation). Сейчас трудно найти ПО, относящееся к этому классу, а в России так просто невозможно. Да и ни к чему.

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

                    По степени готовности к внедрению


                    Готовые решения — вы приобретаете готовые компоненты, ставите их как есть и адаптируете бизнес-модель к установленному ПО. Такой вариант относительно бюджетен и подходит небольшим компаниям со стандартными процессами или компаниям, желающим автоматизировать один-два аспекта работы (например, вести клиентов сделки и совершать звонки). Хотя сегодня в большинстве решений есть возможности настройки системы клиентом и даже инструменты для самостоятельной доработки (например, в RegionSoft CRM это дизайнер отчётов, шаблоны документов, конструктор бизнес-процессов, в некоторых CRM — своя платформа разработки или API).

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

                    Полностью заказная разработка — долгий, тернистый и дорогой путь внедрения CRM в компании. Вы собираете требования, описываете необходимые процессы и функциональность, вендор (или компания заказной разработки) создаёт детальное ТЗ и разрабатывает CRM-систему под вас. При этом может оказаться, что вы напрасно настояли на таком раскладе, и вполне могли обойтись доработкой базового решения. При всей привлекательности заказного ПО «именно под компанию» у него есть ощутимый минус — в случае изменения бизнеса или требований в дальнейшем каждое софтверное изменение будет также заказной разработкой — долгой и дорогой.

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

                    По лицензированию


                    SaaS — решения, лицензии которых вы арендуете и платите за них ежемесячно, как за интернет или телефон. Это выгодная вендору и не очень выгодная компании схема: пока у вас есть CRM, вы за неё платите — год, два, пять… Цена владения выходит просто космической. Так, например, если у вас всего 10 сотрудников и платите вы всего 990 рублей за сотрудника в месяц, цена владения за 5 лет составит 594 000 рублей. В общем, считайте. Расчёт, в основном, идёт на то, что современные пользователи привыкли к абонентской плате во всём и сочтут такой вариант удобным. Хотя вам, конечно, расскажут про масштабирование и гибкость. Кстати, у нас тоже есть аренда нашей CRM-системы, но она не такая кабальная — просто для тех, кому позарез нужна CRM-ка, но нет денег на оплату сразу всей покупки.

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

                    Свободное ПО (опенсорс) — самая интересная форма существования CRM-системы. Можно побыть хардкорной ИТ-компанией, форкнуть известный проект, запилить свои фичи и брать деньги с других за техническую поддержку (а у нее работы будет по горло, деньги не маленькие). Можно купить готовый форк и платить за техническую поддержку, пользуясь «бесплатным ПО». Можно форкнуть, нанять программиста и сделать идеальную CRM-систему под себя — это недёшево и сложно, но бывает эффективно. Главное, всегда внимательно смотрите, по какой лицензии распространяется свободное ПО и за какой стек вам придется платить программисту.

                    По включённости вендора


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

                    Партнёрские решения — CRM-систему продаёт системный интегратор или сертифицированный партнёр. Актуально, в основном, для зарубежных CRM-систем и продуктов 1С. Партнёры могут просто выступать посредником, а могут дорабатывать системы, делать сложные внедрения, разрабатывать коннекторы, плагины и т.д. Хороши, если у вас сложное внедрение, невозможна удалённая установка и доработка, а в городе нет вендора. В целом, это больше история для крупных компаний.

                    Решения «под крылом сильной компании» — ультра актуальные события CRM-рынка. Сперва свою CRM выпустил Билайн (со скандалом, невнятной функциональностью и странным продвижением), на днях о CRM-системе сообщил Сбербанк (на самом деле, это красивое переработанное решение одного из игроков российского рынка CRM). По сравнению с Билайном, система Сбера вполне удобоваримая, но продвижение на бесплатности, конечно же, всего лишь маркетинговый ход. Такой тип систем плох по ряду причин: во-первых, это не профильный бизнес компании, во-вторых, это не CRM на благо бизнеса, а способ держать своих клиентов при себе, дав им ещё один инструмент, который их привяжет к компании, в-третьих, это потребность компаний в ваших данных (вполне возможно, они будут анализироваться и использоваться для создания продуктов и услуг — поживём-увидим, это только наше предположение).

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

                    По принципу развёртывания


                    Десктопные — CRM ставится на сервер компании (может быть просто современный ПК), а пользователям ставятся рабочие приложения. Возможен терминальный удалённый доступ с любого устройства при наличии интернет-соединения. Вопреки расхожему мифу, у десктопного софта нет никаких ограничений для филиальной или холдинговой структуры компании. Это самый безопасный способ развёртывания.

                    Облачные — пользователи работают через веб-интерфейс или с мобильного, а вся информация хранится в облаке (по сути, на чужих физических серверах в удалённому дата-центре). Считается, что это самый удобный вариант с точки зрения администрирования (не совсем, но сейчас не об этом). Действительно, вам не нужно заботиться о состоянии сервера, хранилище данных и т.д. Однако за это вы платите кроме всего прочего безопасностью — данные в любом облаке можно скомпрометировать, было бы желание. Кстати, покупая облачную CRM-систему, не забудьте уточнить, взимается ли дополнительная плата за бэкапы, как они делаются, сколько хранятся, какой аптайм у сервера. Скорее всего, вы откроете для себя новые финансовые нюансы.

                    Серверные с веб-интерфейсом — все данные хранятся в вашем офисном облаке (у вас на серверах), а пользователи заходят через привычный веб-интерфейс. Это более безопасное  решение чем облако, но и более уязвимое, чем десктоп.

                    Зоопарк бизнес-софта


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



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

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

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

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

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

                    CMS — система управления контентом сайта и/или блога. Не имеет никакого отношения к CRM, путают по созвучию.

                    BPM-системы — системы проектирования и моделирования бизнес-процессов компании. Позволяют автоматизировать буквально всё: от мелкой рутины до крупных производственных процессов. В чистом виде нужны только крупным специализированным компаниям. В центре таких систем — внутренние бизнес-процессы компании, их отладка и безупречное функционирование. Как модуль CRM — очень интересная и нужная штука даже для небольших фирм. Обязательно настраивайте бизнес-процессы, полезно их пересмотреть и автоматизировать.

                    Виртуальные АТС и комплексный VoIP — тот тип ПО, про который говорят «нам звонилки хватит». Действительно, многие операторы IP-телефонии имеют собственные контакт-менеджеры и даже минимальные CRM. Как правило, функциональности начинает быстро не хватать, поэтому лучше брать нормальную развитую CRM и интегрировать её с телефонией и виртуальной АТС. Получается хорошо. А главное, востребовано почти всеми клиентами — настолько, что, например, мы разработали и собственный SIP-фон, и свой VoIP-коннектор для маршрутизации телефонии.

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

                    Аналитические системы — встречаются, в основном, в крупных компаниях. Это системы обработки поступающих от клиентов данных. могут быть вендорскими, а могут самописными. Самый простой вариант — все данные накапливаются в СУБД, и программисты готовят заказные выгрузки для сотрудников: пишется SQL-запрос, выгружается массив, передаётся заинтересованному менеджеру. Самый сложный — дорогие импортные решения типа SAP.

                    ECM-системы (они же СЭД) — системы электронного документооборота с формированием, хранением, согласованием и архивированием документов. Обычно используются сами по себе, крайне редко бывают интегрированы с CRM или входят в её состав.

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

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

                    Так что с определением? Мы не претендуем стать истиной в последней инстанции, поэтому просто предложим свой вариант. CRM-система — это программное обеспечение, служащее инструментом накопления, хранения и развития клиентской базы, а также проектирования и автоматизации бизнес-процессов в компании. Хотя, повторимся, для каждой компании цели внедрения свои и в итоге определение тоже своё. Поэтому дадим неформальное, но глобальное определение: CRM — это нужная бизнесу программа, которая ускорит процессы, поможет обойти конкурентов и сохранит ваш ценный актив — клиентскую базу. Конечно, если вы сами этого захотите. Ну или давайте своё определение в комментариях — если что-то коллективно надумаем, сделаем апдейт статьи.

                    P.S.: да завтра включительно у нас идёт акция «Осеннее ускорение», так что если кому-то нужна скидка 15% на разный бизнес-софт (а мы делаем не только CRM-системы), то рады вам помочь, рассказать, провести бесплатную онлайн-презентацию — вы можете всё посмотреть и расспросить, не вставая с любимого кресла. Отвечаем даже на неудобные вопросы (по делу, конечно).
                    Original source: habrahabr.ru (comments, light).

                    https://habrahabr.ru/post/338918/


                    Метки:  

                    Apache® Ignite™ + Persistent Data Store — In-Memory проникает на диски. Часть I — Durable Memory

                    Пятница, 29 Сентября 2017 г. 09:06 + в цитатник

                    Атакуем DHCP часть 3. DHCP + Apple = MiTM

                    Пятница, 29 Сентября 2017 г. 09:01 + в цитатник
                    vladimir-ivanov сегодня в 09:01 Администрирование

                    Атакуем DHCP часть 3. DHCP + Apple = MiTM

                      LOGO


                      В данной статье я расскажу о том как осуществить MiTM любого устройства компании Apple в WiFi сети. Прежде чем читать эту статью настоятельно рекомендую ознакомиться с первой и второй частью.


                      Ограничения все те же:


                      1. Мы должны быть подключены к атакуемой точке доступа.
                      2. У нас должна быть возможность прослушивать широковещательный трафик в сети.

                      Как оказалось, macOS и iOS переплюнули всех в плане получения сетевых настроек по протоколу DHCP. Когда эти операционные системы отправляют DHCPREQUEST, DHCP-сервер отвечает им DHCPACK, и они выставляют сетевые настройки из ответа сервера. Вроде пока все как у всех:
                      MacOS legal DHCPACK


                      Но проблема в том, что DHCPREQUEST широковещательный и злоумышленник, как правило, без особых проблем может его перехватить извлечь из него поля xid и chaddr, что бы сформировать правильный DHCPACK. Но злоумышленник отправит DHCPACK, конечно, позднее легитимного DHCP-сервера, то есть его ответ придет вторым. Все остальные DHCP-клиенты на других ОС просто проигнорируют второй DHCPACK, но не macOS и iOS.


                      Как вы думаете, какие сетевые настройки выставляют данные операционные системы? Ответ: те настройки, которые будут содержаться во втором DHCPACK (в DHCPACK злоумышленника).
                      MacOS attacker DHCPACK


                      Видео демонстрации бага в DHCP-клиенте на macOS:





                      Как Вы думаете баг это или фича? Я подумал баг и на всякий случай завел заявку на Apple Bug Reporter этой заявке уже больше месяца, но ни одного комментария от специалистов Apple я так и не получил.
                      Apple bug reporter


                      На заявке в Apple Bug Reporter я не остановился и написал письмо в product-security@apple.com
                      Apple email


                      Специалисты Apple совсем не быстро, но все же ответили и сказали, что их DHCP-клиент работает в соответствии с RFC 2131. То есть это вовсе не баг, это фича. У меня все.
                      Thats all folks


                      Послесловие


                      Для самых ленивых я подготовил скрипт apple_wifi_mitmer.py, который в автоматическом режиме находит все устройства компании Apple в Wi-Fi-сети, деаутентифицирует их и производит MiTM.


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


                      Видео демонстрации работы скрипта apple_wifi_mitmer.py:





                      Работает ли это на новой iOS 11? Ответ: Да, работает.

                      Original source: habrahabr.ru (comments, light).

                      https://habrahabr.ru/post/338864/


                      Метки:  

                      Kubernetes 1.8: обзор основных новшеств

                      Пятница, 29 Сентября 2017 г. 08:02 + в цитатник
                      distol сегодня в 08:02 Администрирование

                      Kubernetes 1.8: обзор основных новшеств



                        Большое и хорошо организованное Open Source-сообщество, стоящее за разработкой Kubernetes, приучило нас ждать значимых и многочисленных изменений от каждого релиза. И Kubernetes 1.8 не стал исключением, представив на радость DevOps-инженерам и всем сочувствующимучастникам улучшения и новые возможности практически во всех своих компонентах.

                        Официальный релиз Kubernetes 1.8 был запланирован ещё на минувшую среду, однако официальные анонсы (в блоге проекта и CNCF) пока не состоялись. Тем не менее, сегодня в 3:35 ночи по MSK в Git-репозитории проекта было замечено изменение в CHANGELOG, которое сигнализирует о готовности Kubernetes 1.8 для скачивания и использования:



                        Итак, что же нового принёс релиз Kubernetes 1.8?

                        Сеть


                        В kube-proxy добавлена альфа-версия поддержки режима IPVS для балансировки нагрузки (вместо iptables). В этом режиме kube-proxy следит за сервисами и endpoints в Kubernetes, создавая netlink-интерфейс (virtual server и real server соответственно). Кроме того, он периодически синхронизирует их, поддерживая консистентность состояния IPVS. При запросе на доступ к сервису трафик перенаправляется на один из подов бэкенда. При этом IPVS предлагает различные алгоритмы для балансировки нагрузки (round-robin, least connection, destination hashing, source hashing, shortest expected delay, never queue). Такую возможность часто запрашивали в тикетах Kubernetes, и мы сами тоже очень её ждали.

                        Среди других сетевых новшеств — бета-версии поддержки политик для исходящего трафика EgressRules в NetworkPolicy API, а также возможность (в том же NetworkPolicy) применения правил по CIDR источника/получателя (через ipBlockRule).

                        Планировщик


                        Главное новшество в планировщике — возможность задавать подам приоритеты (в спецификации пода, PodSpec, пользователи определяют поле PriorityClassName, а Kubernetes на его основе выставляет Priority). Цель банальна: улучшить распределение ресурсов в случаях, когда их не хватает, а требуется одновременно выполнить по-настоящему критичные задачи и менее срочные/важные. Теперь поды с высоким приоритетом будут получать больший шанс на исполнение. Кроме того, при освобождении ресурсов в кластере (preemption) поды с меньшим приоритетом будут затронуты скорее подов с высоким приоритетом. В частности, для этого в kubelet была изменена стратегия по выборке подов (eviction strategy), в которой теперь учитываются одновременно и приоритет пода, и потребление им ресурсов. Реализация всех этих возможностей имеет статус альфа-версии. Приоритеты Kubernetes и работа с ними подробно описаны в документации по архитектуре.

                        Ещё одно интересное новшество, представленное в альфа-версии, — более сложный механизм обработки поля условий (Condition, см. документацию) на узлах. Традиционно в этом поле фиксируются проблемные состояния узла — например, при отсутствии сети условие NetworkUnavailable ставится в True, в результате чего поды перестанут назначаться на этот узел. С помощью нового подхода Taints Node by Condition такая же ситуация приведёт к пометке узла определённым статусом (например, node.kubernetes.io/networkUnavailable=:NoSchedule), на основе которого (в спецификации пода) можно решить, что делать дальше (действительно ли не назначать под такому проблемному узлу).

                        Хранилища


                        Указание опций монтирования для томов стало стабильным, а одновременно с этим:

                        • в спецификации PersistentVolume появилось новое поле MountOptions для указания опций монтирования (вместо annotations);
                        • в спецификации StorageClass появилось аналогичное поле MountOptions для динамически создаваемых томов.

                        В API метрики Kubernetes добавлена информация о доступном пространстве в постоянных томах (PV), а также метрики успешности выполнения и времени задержки для всех вызовов mount/unmount/attach/detach/provision/delete.

                        В спецификации PersistentVolume для Azure File, CephFS, iSCSI, GlusterFS теперь можно ссылаться на ресурсы в пространствах имён.

                        Среди нестабильных нововведений (в статусах альфа и бета):

                        • в StorageClass добавлена бета-версия поддержки определения reclaim policy (аналогично PersistentVolume) вместо применения политики delete всегда по умолчанию;
                        • в Kubernetes API добавлена возможность увеличения размера тома — альфа-версия этой фичи увеличивает размер только для тома (не делает resize для файловой системы) и поддерживает только Gluster;
                        • началась работа над изоляцией/ограничениями для хранилищ данных — в статусе альфа представлен новый ресурс ephemeral-storage, который включает в себя всё дисковое пространство, доступное контейнеру, и позволяет устанавливать ограничения на возможный объём (quota management) и запросы к нему (limitrange) — подробнее см. в текущей документации;
                        • новое поле VolumeMount.Propagation для VolumeMount в контейнерах пода (альфа-версия) позволяет устанавливать значение Bidirectional для возможности использования того же примонтированного каталога на хосте и в других контейнерах;
                        • доступен ранний прототип создания снимков томов (volume snapshots) через Kubernetes API — пока эти снапшоты могут быть неконсистентными, и ответственный за них код вынесен из ядра Kubernetes во внешний репозиторий.

                        kubelet


                        В kubelet появилась альфа-версия нового компонента — CPU Manager, — взаимодействующего напрямую с kuberuntime и позволяющего назначать контейнерам подов выделенные ядра процессоров (т.е. CPU affinity policies на уровне контейнеров). Как уточняется в документации, его появление стало ответом на две проблемы:

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

                        Динамическая конфигурация kubelet — ещё одна фича в альфа-статусе, позволяющая обновлять конфигурацию этого агента во всех узлах «живого» кластера. Доведение её до стабильного состояния (GA) ожидается только в релизе 1.10.

                        Метрики


                        Поддержка пользовательских метрик в Horizontal Pod Autoscaler (HPA) получила статус бета-версии, и связанные с ней API переведены на v1beta1.

                        metrics-server стал рекомендованным способом предоставления API для метрик ресурсов. Деплоится как дополнение по аналогии с Heapster. Прямое получение метрик из Heapster объявлено устаревшим.

                        Cluster Autoscaler


                        Утилита Cluster Autoscaler, созданная для автоматического изменения размера кластера Kubernetes (когда есть поды, которые не запускаются из-за недостатка ресурсов, или некоторые узлы плохо используются долгое время), получила стабильный статус (GA) и поддержку до 1000 узлов.

                        Кроме того, при удалении узлов Cluster Autoscaler теперь даёт подам по 10 минут для корректного завершения работы (graceful termination). В случае, если под так и не остановлен за это время, узел всё равно удаляется. Раньше этот лимит составлял 1 минуту или корректного завершения не дожидались вообще.

                        kubeadm и kops


                        В kubeadm появилась альфа-реализация деплоя кластера (control plane) типа self-hosted (kubeadm init с флагом --feature-gates=SelfHosting=true). Сертификаты при этом могут храниться на диске (hostPath) или в секретах. А новая подкоманда kubeadm upgrade (находится в бета-статусе) позволяет автоматически выполнять обновление кластера self-hosted, созданного с помощью kubeadm.

                        Другая новая возможность kubeadm в статусе альфа — выполнение подзадач вместо всего цикла kubeadm init с помощью подкоманды phase (на текущий момент доступна как kubeadm alpha phase и будет приведена в официальный вид в следующем релизе Kubernetes). Основное предназначение — возможность лучшей интеграции kubeadm с provisioning-утилитами вроде kops и GKE.

                        В kops, тем временем, представлены две новые фичи в статусе альфа: поддержка bare metal-машин в качестве целевых и возможность запуска как сервера (см. Kops HTTP API Server). Наконец, поддержку GCE в kops «повысили» до статуса бета-версии.

                        CLI


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

                        Команды rollout и rollback в kubectl теперь поддерживают StatefulSet.

                        API


                        Изменения в API включают в себя APIListChunking — новый подход к выдаче ответов на запросы LIST. Теперь они разбиваются на небольшие куски и выдаются клиенту в соответствии с указанным им лимитом. В результате, сервер потребляет меньше памяти и CPU при выдаче очень больших списков, и такое поведение станет стандартным для всех инфомеров в Kubernetes 1.9.

                        CustomResourceDefinition API научился валидировать объекты, основываясь на JSON-схеме (из CRD-спецификации) — альфа-реализация доступна как CustomResourceValidation в kube-apiserver.

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

                        Workload API


                        Так называемый Workload API — это базовая часть Kubernetes API, относящаяся к «рабочим нагрузкам» и включающая в себя DaemonSet, Deployment, ReplicaSet, StatefulSet. На данный момент эти API перенесены в группу apps и с релизом Kubernetes 1.8 получили версию v1beta2. Стабилизация же Workload API предполагает вынесение этих API в отдельную группу и достижение максимально возможной консистентности с помощью стандартизации этих API путём удаления/добавления/переименования имеющихся полей, определения однотипных значений по умолчанию, общей валидации. Например, стратегией spec.updateStrategy по умолчанию для StatefulSet и DaemonSet стал RollingUpdate, а выборка по умолчанию spec.selector для всех Workload API (из-за несовместимости с kubectl apply и strategic merge patch) отключена и теперь требует явного определения пользователем в манифесте. Обобщающий тикет с подробностями — #353.

                        Другое


                        Среди прочих (и весьма многочисленных!) изменений в релизе Kubernetes 1.8 отмечу:

                        • управление доступом на основе ролей (RBAC), использующее группу API rbac.authorization.k8s.io для возможности конфигурации динамических политик, переведено в стабильный статус (GA), а также получило бета-версию нового API (SelfSubjectRulesReview) для просмотра действий, которые пользователь может выполнить с пространством имён;
                        • представлена альфа-версия механизма для хранения ключей шифрования ресурсов в сторонних системах (Key Management Systems, KMS), и одновременно с этим появился плагин Google Cloud KMS (#48522);
                        • в PodSecurityPolicies добавлена поддержка белого списка разрешённых путей для томов хоста;
                        • поддержка CRI-O (Container Runtime Interface) на базе стандарта от Open Container Initiative объявлена стабильной (прошла все тесты e2e) [CRI-O — связующее звено между kubelet и исполняемыми средами, совместимыми с OCI, такими как runc; подробнее см. в GitHub], а также проект cri-containerd достиг статуса альфа-версии;
                        • поддержка Multi-cluster, ранее известная как Federation, готовится к стабильному выпуску (GA) в следующих релизах Kubernetes, а пока стали доступны альфа-реализации Federated Jobs, которые автоматически деплоятся на множество кластеров, и Federated Horizontal Pod Autoscaling (HPA), работающих аналогично обычным HPA, но, опять же, с распространением на множество кластеров;
                        • команда, ответственная за масштабируемость, формально зафиксировала процесс своего тестирования, создала документацию для имеющихся пороговых значений, определила новые наборы по уровням обслуживания (Service Level Indicators и Service Level Objectives).

                        P.S.


                        Во время подготовки Kubernetes 1.8 проект собирался со следующими версиями Docker: 1.11.2, 1.12.6, 1.13.1, 17.03.2. Список известных проблем (known issues) для них см. здесь. В том же документе, озаглавленном как «Introduction to v1.8.0», можно найти и более полный список всех крупных изменений.

                        Сами мы затянули с обновлением обслуживаемых кластеров Kubernetes с релиза 1.6 до 1.7 и провели основную миграцию только 2 недели назад (на данный момент осталось несколько инсталляций с версией 1.6). Повсеместное обновление до нового релиза — 1.8 — планируем уже в октябре.

                        Читайте также в нашем блоге:

                        Original source: habrahabr.ru (comments, light).

                        https://habrahabr.ru/post/338230/


                        Метки:  

                        Как я искал (и нашел!) баги в смартконтракте проекта kickico

                        Пятница, 29 Сентября 2017 г. 07:51 + в цитатник
                        quantum сегодня в 07:51 Разработка

                        Как я искал (и нашел!) баги в смартконтракте проекта kickico

                          В августе я, неожиданно для себя, поучаствовал в bugbounty проекта kickico. Я уже рассказал об этом на митапе Atlas Blockchain в прошлую пятницу. Статья — текстовая версия этого доклада с дополнением и небольшим пятничным конкурсом :)



                          Kickico — краудфандинговая платформа, kickstarter с приемом криптовалют. Платформа уже готова, на ней прошло собственное preico (собрано 5000 эфира при софткапе 2000), preico игры Magister of Magic (собрано 280 эфира при софткапе 200), и недавно завершилось ICO (собрано >80000 эфира при софткапе 50000).

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

                          Тогда я еще ничего не знал о смартконтрактах и solidity, как и о том, что загруженный контракт просто так не обновить, и сразу создал ишью на то, что были использованы magic numbers c предложением заменить на dividents.length.



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



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



                          После этого я скинул AntiDanilevski ссылки на ишьюс, чтобы не потерялись в суматохе preico, получил обещание проверить и забыл на некоторое время о Kickico. Через 2 недели мне пришло уведомление с гитхаба о новом коммите, все мои ишьюсы были исправлены. Я написал Анти и получил свою награду в токенах.

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

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

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



                          Эти баги исправили в то же день и я получил еще вознаграждение.

                          В тот же день команда kickico закоммитила поддержку протокола bankor и Анти еще раз мне предложил поискать баги. Сразу я увидел только то, что не реализован вызов события NewSmartToken, но опять же это не критично.

                          Следующим вечером я еще раз взглянул на код и увидел, что в методе issue, не хватало добавления адреса, для которого выпускались токены, в список на начисление дивидентов. Я сразу же сообщил об этом Анти, но выяснилось, что об этом баге уже сообщили нанятые за деньги аудиторы.

                          Что было сделано хорошо?
                          • Багбаунти. Мало какие проекты проводят багбаунти.
                          • Выложили код на гитхабе. Я, например, совсем не собирался участвовать в багбаунти, зашел посмотреть код из любопытства, а вот как получилось :)
                          • Работа с участниками багбаунти. Анти сообщал мне о всех обновлениях и просил искать еще.
                          • Найм сторонних аудиторов.


                          -----Бонусная часть-----

                          На днях kickico выпустили обновленную версию токена, в которой, кроме прочего, был исправлен эпик критикал баг, который не увидел ни я до ICO, ни сторонние аудиторы. Что подтверждает тот факт, что если у вас более менее сложный контракт, то никто не сможет дать гарантии отсутствия в нем багов. Всегда нужно иметь план Б на этот случай. Хорошим вариантом для этого может быть механизм обновления кода без смены адреса.

                          Ну и собственно конкурс: кто опишет этот баг получит 500 KICK от проекта kickico. На гитхабе версия с багом, смотреть дифф с текущей версией неспортивно :) Описание бага прячьте пожалуйста под спойлер.
                          Original source: habrahabr.ru (comments, light).

                          https://habrahabr.ru/post/338960/


                          Метки:  

                          Классический 2д квест или как прошли наши два года разработки. Часть 2

                          Пятница, 29 Сентября 2017 г. 06:47 + в цитатник
                          MaikShamrock сегодня в 06:47 Разработка

                          Классический 2д квест или как прошли наши два года разработки. Часть 2

                            Продолжение рассказа про то, как ковался «Swordbreaker The Game», в этой части будет рассказ про запуск первой демки, локализацию, про то как мы пытались податься на indie-gogo, и немножечко котиков. :)

                            image



                            Продолжение истории разработки нашей первой игры, начало вот здесь: habrahabr.ru/post/338074

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


                            Правка багов перед запуском демки

                            По прошествии примерно месяца результаты оказались вполне неплохими, в целом игра понравилась людям, но некоторые счастливчики, которые умудрились с первого раза проскочить все верные решения доходили до конца буквально за 10-12 сцен, а это всего минут 10-15 геймплея, если прочитывать тексты. Мы конечно понимали, что формат игры таков, что имеются ветки по которым можно быстро дойти к финалу, но идея была в том, что игроку будет интересно исследование всех сцен игры. Реклама же принесла нам первый 1$.


                            Демка в супер-топе

                            Тем временем Женька нарисовал нам лого команды, который мы и решили взять за основной (картинка в начале статьи).

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

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

                            • Саня пытался рассказать об игре везде где только можно, на всех знаменитых форумах по гейм-девелопменту (gcup, gamedev – одни из самых здоровых), на формах игромании, на форумах текстовых квестов, не обошли также стороной канобу.ру и прочие-прочие)
                            • Сделали группы в VK, facebook, tweeter
                            • Сделали сайт (думаю это было лишним, но в целом мы посчитали что он должен у нас быть)
                            • Ну и также некоторые ютуб-каналы любезно согласились рассказать об игре
                            • Кроме этого Саня даже пытался объять необъятное и писать на иностранных форумах (об этом чуть-чуть попозже)


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

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


                            Сашкин котёнок

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


                            Не самая крутая статистика, но мы-таки прошли greenlight


                            Финальный результат

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


                            М…меценатство)

                            Вообще перевод на иностранные языки стал для нас небольшой проблемой – текста в игре действительно немало, и учитывая русскую специфику, то перевести его с той же идеей на другие языки вовсе не так-то просто, а что еще важнее – не так-то и дешево. Всего текста в игре ~100kk символов, стоимость перевода нам была озвучена примерно 1000 убитых енотов в расчётах того времени. Мы уже успели поработать над переводом части текстов для демки, с некоторыми переводчиками, которых пытались найти через группы VK, однако перевод оставлял желать лучшего… В целом – для демки мы решили взять то, что есть, пусть это был вообще не идеальный перевод, но другого варианта у нас просто не было. Для полной версии игры поиски переводчика продолжались еще долго…

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

                            Но в дальнейшем мы-таки нашли выход, опять же не без помощи коммьюнити, один из знакомых девелоперов из компании Krealit дал нам контакты переводчика с которым они сами работали, и, о чудо, он оказался русским, который на тот момент учился в европейском институте иностранных языков и подрабатывал фрилансом, в итоге он выполнил нам перевод всех текстов в игре на довольно качественном уровне. Как мы это проверили? Всё просто – поскольку Саня строчил на всех форумах, на которых можно было строчить, то он попросил зарубежных коллег по гейм-деву прочитать кусок текста и дать свою оценку качеству. В итоге всё было реально круто. Также благодаря нашему переводчику у нас появилось слово “Timbernator”, которое он придумал чтобы обозвать робота-буратино в игре.


                            Наш лайк за перевод

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

                            Тем временем Женька продолжал рисовать-рисовать-рисовать и еще рисовать, я постепенно правил код и мы всё это тестили, собирали и двигались потихоньку к релизу.

                            На этом заканчивается вторая часть рассказа, в следующей постараюсь рассказать непосредственно про программирование и после этого, что же получилось после полного релиза.
                            Original source: habrahabr.ru (comments, light).

                            https://habrahabr.ru/post/338938/


                            Метки:  

                            Тайм-менеджмент для кинестетиков

                            Пятница, 29 Сентября 2017 г. 05:00 + в цитатник
                            Himura сегодня в 05:00 Разработка

                            Тайм-менеджмент для кинестетиков

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


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


                              Сегодня мы будем интегрировать клиент Tomighty с устройстовм "Большая Красная Кнопка". Нам для этого понадобится:


                              • Большая Красная Кнопка (со светодиодом). У меня оказалась не очень большая, но очень красная.
                              • ESP8266 — один из наиболее оптимальных микроконтроллеров по соотношению удобство/цена. Это даже не микроконтроллер вовсе, но как микроконтроллер он абсолютно прекрасен!
                              • MicroPython. Я не знаю языка удобнее чем Python, а вы? Разве что Ruby, но его вроде бы не портировали на ESP8266.
                              • Протокол MQTT для связи между компом и девайсом.
                              • Visual Studio.
                              • Опционально, расширение CodeRush for Roslyn. Оно сильно упрощает работу с как со своим так и с незнакомым кодом.


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


                              Welcome!


                              Большая Красная Кнопка


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


                              1. Берём ESP8266. У меня ESP-12-Q, но подойдет любой модуль с памятью 1МБ или более.
                              2. Прошиваем MicroPython по инструкции.
                              3. Качаем официальную реализацию MQTT и сохраняем её как mqtt.py. На ESP через WebREPL.
                              4. Находим бесплатный MQTT-брокер в Интернетах, или поднимаем локальный Mosquitto. Я использовал CloudMQTT, но это вовсе не единственный вариант.
                              5. Изучаем и улучшаем код для ESP8266, а потом заливаем на плату под имененм main.py.
                              6. Подключаем светящуюся кнопочку из ближайшего магазина электроники.
                              7. Reboot.
                              8. Debug via Serial Port.

                              Доработка клиента Tomighty


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


                              Для того чтобы собрать проект Tomighty.Windows, необходимо устанвоить в него пакет UWPDesktop через NuGet. Это совершенно не очевидное действие, до которого мы относительно долго пытались додуматься и чуть менее долго догуглиться. Возможно это тривиально для тех кто имел дело со старомодными WinForms приложениями, зовущими новомодный UWP API, но для тех кто таким не занимался — не очень.


                              Таким образом, на данном этом этапе у меня получилось собираемое и запускаемое приложение, так что я приступил к поиску мест, в которые можно внедриться со своими костылями. С помощью CodeRush for Roslyn это оказалось совсем не сложно.


                              Задачи такие:


                              1. Добавить в меню иконки трэя пункт "Connect to the Red Button"
                              2. Запускать уведомление (их Toast с иконкой) при успешном соединении с MQTT-брокером или неудачной попытке
                              3. Запускать период Pomodoro при получении сообщения по MQTT
                              4. Отправлять сообщение по MQTT при фактическом старте периода Pomodoro и другое сообщение при старте прерывов

                              Для начала, выясним как запустить период Pomodoro, скорее всего этот путь приведет нас к основным архитектурным элементам приложения быстрее всего. Пробный запуск показал, что, похоже, основным источником управления тут является икнока в трэе, так что попробуем найти точку входа где-нибудь в папке Tomighty.Windows\Tray\. Действительно, в интерфейсе ITrayMenu есть похожий на правду метод, посмотрим где он используется.



                              Нашёлся очень мясистый файлик TrayMenuController.cs, а в нём и нужный метод


                              private void OnStartPomodoroClick(object sender, EventArgs e) => StartTimer(IntervalType.Pomodoro);
                              
                              // ...
                              
                              private void StartTimer(IntervalType intervalType) {
                                  Task.Run(() => pomodoroEngine.StartTimer(intervalType));
                              }

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


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



                              Отлично, мы нашли точку входа. Она выглядит как-то так:


                              internal class TomightyApplication : ApplicationContext {
                                  public TomightyApplication() {
                                      var eventHub = new SynchronousEventHub();
                                      var timer = new Tomighty.Timer(eventHub);
                                      var userPreferences = new UserPreferences();
                                      var pomodoroEngine = new PomodoroEngine(timer, userPreferences, eventHub);
                              
                                      var trayMenu = new TrayMenu() as ITrayMenu;
                                      var trayIcon = CreateTrayIcon(trayMenu);
                                      var timerWindowPresenter = new TimerWindowPresenter(pomodoroEngine, timer, eventHub);
                              
                                      new TrayIconController(trayIcon, timerWindowPresenter, eventHub);
                                      new TrayMenuController(trayMenu, this, pomodoroEngine, eventHub);
                                      // ...
                              
                                      new StartupEvents(eventHub);
                                  }
                                  // ...
                              }

                              Время совершить небольшую интервенцию: создадим еще один объект несуществующего класса, а потом с помощью фичи Declare Class добавим сам класс.



                              Я сразу передал еще и eventHub, потому что заметил что в TrayMenuController через него можно подписаться на ивенты старта и окончания таймера. Пригодится.


                              Сразу можно сделать два филда из автоматически сгенерированных параметров конструктора фичей Declare Field with Initializer:



                              Чтож, теперь мы можем подписываться на ивенты и управлять таймерами. Попробуем добавить пункт меню в трэй, который будет вызывать метод RedButtonController.Connect().


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


                              var redButton = new RedButtonController(eventHub);
                              // ...
                              new TrayMenuController(trayMenu, this, pomodoroEngine, eventHub, redButton);

                              Чтобы пункт меню появился в списке, надо создать TrayMenu.redButtonConnectItem и везде его прокинуть по аналогии с теми что рядом. В поиске таких мест хорошо поможет Tab to Next Reference: Можно просто поставить курсор на любой референс, нажать Tab и перейти к следующему, при этом все референсы в поле зрения подсвечиваются.



                              Никаких подводных камней замечено не было, всё заработало довольно быстро. redButtonConnectItem вызывает RedButtonController.Connect() через хэндлер TrayMenuController.OnRedButtonConnect()



                              (таскбар слева экономит вертикальное пространство и круче чем таскбар снизу)


                              А теперь, попробуем вызвать Toast (это такие новомодные нотификации). Когда я впервые запустил приложение, один такой прилатал с предложением настроиться после первого запуска. Попробуем его отыскать. Думаю, надо начать со строчки new StartupEvents(eventHub) в конце конструктора TomightyApplication. Пара нажатий на F12 (перейти к декларации) приводят в файл Tomighty.Windows\Events.cs с двумя пустыми ивентами:


                              namespace Tomighty.Windows.Events {
                                  public class FirstRun { }
                                  public class AppUpdated { }
                              }

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



                              Далее, пришлось пройтись по всем местам где что-то происходило с ивентом FirstRun и добавить подобные действия для нашего ивента RedButtonConnectionChanged.



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



                              Соединение с MQTT


                              Окей, у нас есть pomodoroEngine, eventHub, пункт меню и нотификации, вроде бы всё что нужно, можно соединяться с MQTT и пробывать общаться с кнопкой. Для MQTT будем использовать самый гуглящийся клиент M2Mqtt:


                              PM> Install-Package M2Mqtt

                              У меня уже был простенький класс, для M2Mqtt, так что я его просто подключил и наслаждался ну-совсем-простым API:


                              public void Connect() {
                                  mqtt = new MQTTClient("m10.cloudmqtt.com", 13633);
                                  mqtt.Connect("%LOGIN%", "%PASSWORD%");
                                  if (!mqtt.client.IsConnected) {
                                      eventHub.Publish(new RedButtonConnectionChanged(false));
                                      return;
                                  }
                                  eventHub.Publish(new RedButtonConnectionChanged(true));
                              
                                  mqtt.client.MqttMsgPublishReceived += onMsgReceived;
                                  mqtt.Subscribe("esp");
                              }

                              Добавить хэндлер можно с помощью Declare Method:



                              Осталось подписаться на TimerStarted и TimerStopped, и можно писать логику. А логика у меня в первом приближении получилась такая:



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


                              Original source: habrahabr.ru (comments, light).

                              https://habrahabr.ru/post/338948/


                              Метки:  

                              NetApp ONTAP - разложим все по полочкам

                              Пятница, 29 Сентября 2017 г. 01:16 + в цитатник
                              Orest_ua сегодня в 01:16 Администрирование

                              NetApp ONTAP - разложим все по полочкам

                                NetApp ONTAP Cloud является программным подписным решением. Хранилище NetApp ONTAP storage сочетает средства управления данными клиента с мощным программным решением хранилища данных корпоративного уровня. Достаточно много и подробно за последний год описывались версии ONTAP 9 и 9.2. Однако чувствуется определенная нехватка базовых сведений о самих технологиях, - особенно для новых пользователей. Их краткое описание приведено в данной статье.





                                Что означает Data ONTAP

                                Прежде, чем обратиться к техническим подробностям, любопытно рассмотреть некоторые версии происхождения самого «буквосочетания» ONTAP. В своем сообществе на сайте NetApp некто, укрывшись за ником NetApp_Staff, предпочел отшутиться, хотя, как известно, в каждой шутке… Вот как он объяснил происхождение термина.

                                «Data ONTAP является видом акронима. Некоторые акронимы просто симпатичны. Ясно, что цель всегда состоит в том, чтобы изобрести особое слово, и во многих случаях они выбираются почти наугад. Что-то в этом роде произошло и с ONTAP.

                                Сам акроним не имел никакого смысла. Более того, сочетание Data ONTAP было вдохновлено… пивом. Идея состояла в том, что данные должны течь свободно, - точно так же, как пиво из бочонка, проткнутого краном.

                                Нам, в NetApp, нравится это название, - потому что оно отлично характеризует то, что люди думают о данных. Они должны быть там и тогда, когда вы нуждаетесь в них». Добавим к этому, что «on tap» также означает «готовый к немедленному употреблению или использованию; находящийся под рукой».

                                В комментариях к этому пояснению кто-то сведущий заметил, что в офисе компании был обычай выпивать кружку-другую по пятницам. Но, если серьезно, некоторые написали, что акроним расшифровывается не более, чем «Open Network Technology for Appliance Products».

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


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

                                Что такое ONTAP Cloud

                                Среда облачного хранения (Cloud Storage Environment, CSE) на ONTAP Cloud реализует следующие возможности.

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

                                — Используются системы хранения NAS и SAN, с поддержкой NFS, CIF и iSCSI.

                                — Используются технологии дедупликации данных, «тонких томов» (thin provisioning) и сжатия данных.

                                — Технология Zero-impact NetApp Snapshot почти мгновенно создает резервные копии данных, не требуя дополнительных ресурсов хранения.

                                — Технология NetApp SnapMirror объединяет гибридную среду облака, привязывая локальные хранилища FAS к среде ONTAP Cloud.

                                — Расширенные средства управления данными (OnCommand Management Suite) и их мониторинга (OnCommand Insight) управляются из OnCommand System Manager.

                                — Доступность приложений обеспечивается их копированием через специальные «зоны доступности» (availability zones).

                                Как ONTAP Cloud работает на ресурсах вычислений и хранения AWS

                                ONTAP Cloud - версия виртуальной машины Data ONTAP. Она разработана для того, чтобы работать поверх Amazon Elastic Cloud Compute (EC2) в различных вариантах вычислительных систем (compute instance). Подробнее см. здесь. В русскоязычной IT литературе часто используется калька «инстансы». На сайте Amazon Web Services они определяются следующим образом.

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

                                В качестве хранилища типично используется AWS Elastic Block Storage (EBS). Тома хранилища EBS создают эквивалент узла хранилища Data ONTAP, который работает в облаке.

                                ONTAP Cloud - это вариант машины Amazon (Amazon Machine Instance, AMI), который через OnCommand Cloud Manager развертывается из AWS Marketplace*.


                                Фрагмент начальной страницы AWS Marketplace

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

                                ONTAP Cloud vs. EBS

                                Хранилище EBS может рассматриваться как подобное хранилищу DAS для стандартных серверов. Фактически, EBS предоставляет «сырую емкость хранилища» (raw storage capacity) для серверов Amazon EC2.

                                У EBS есть некоторые средства и возможности, которые несколько шире того, что типично предлагают DAS, но они все же ограничены, и не содержат никаких инструментов репликации данных.

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

                                Хранилище ONTAP Cloud - это управляемая окружающая среда хранения, которая использует «сырое» хранение EBS как основу для своей работы. ONTAP Cloud принимает на себя хранилище EBS, управляет им с помощью ОС хранения Data ONTAP и, кроме собственно хранения, предоставляет следующие дополнительные возможности.

                                — Использование нескольких протоколов (NFS, CIF, iSCSI).

                                — Защита данных (Data Protection) - Snapshot copies, SnapMirror, SnapVault.

                                — Эффективность работы хранилища (Storage efficiency) - Data deduplication, compression, thin provisioning. Эти возможности могут уменьшить количество единиц хранения EBS, которые необходимо покупать.

                                — Управление (Management) - Unified Manager, System Manager, WFA, Insight и др…

                                — Технологию FlexClone.


                                Как работает технология FlexClone

                                В NetApp Snapshots используется технология zero-copy. Она предполагает операции, в ходе которых процессор не выполняет задачу копирования данных из одной области памяти в другую. Термин применяется для описания технологий, которые помогают уменьшить количество копирований между промежуточными буферами. Подробнее см. здесь.

                                NetApp Snapshots не оказывают влияния на работу. Копии хранят только данные об изменениях, минимизируя потребляемый объем хранения.

                                Защита данных в ONTAP Cloud

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

                                Клиенту для этого необходимо иметь другие окружающие среды Data ONTAP во вторичной зоне доступности. Это вторичное местоположение может быть другим местом размещения ONTAP Cloud, или это может быть место размещения NetApp Private Storage.

                                SnapMirror может использоваться для перемещения данных между платформами Data ONTAP, включая направления «data center to cloud», «cloud to data center» и «between clouds» (или «between cloud regions»).

                                OnCommand Cloud Manager


                                Принцип работы NetApp Cloud ONTAP


                                OnCommand Cloud Manager обеспечивает простой, основанный на «волшебниках» интерфейс для развертывания и мониторинга Storage Virtual Machines в Cloud ONTAP и NetApp Private Storage

                                OnCommand Cloud Manager обеспечивает управление окружающей средой хранения в гибридном облаке, включая ONTAP Cloud и кластеризованные платформы на основе Data ONTAP.

                                Он упрощает оперативное управление «тканью данных» (Data Fabric), включая конфигурирование, поддержку и мониторинг каждого узла хранения данных, - как виртуальных устройств, так и реальных физических аппаратных средств.

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

                                Развертывая ONTAP Cloud, Cloud Manager использует AWS credentials, чтобы выбрать виртуальную машину EC2 и хранилище EBS, соответствующие запросу клиента.

                                Различные типы AWS credentials включают пароли (passwords), ключи доступа (access keys), многофакторную аутентификацию (multi-factor authentication), парные ключи (key pairs), сертификаты (certificates). Подробнее см. здесь.

                                ONTAP Cloud может быть развернуто только через Cloud Manager. Он обрабатывает все производимые действия, чтобы удостовериться, что все ресурсы AWS, - как вычислительные, так и хранения, - должным образом назначены для конкретного размещения ONTAP Cloud. Попытка развернуть ONTAP Cloud любым другим методом приведет к отказу с самого начала.

                                Cloud Manager - центральный пункт управления облачной гибридной средой и может управлять кластеризованным окружением ONTAP, независимо от его местоположения. Он может работать как в облаке AWS, так и на решении on-premise в датацентре на площадке клиента.

                                Есть два основных отличия между версиями ONTAP Cloud, и они зависят от того, каким способом получено и инсталлировано ПО. OnCommand Cloud Manager доступен как AMI на AWS Marketplace (версия AMI развертывается для облака EC2), или может быть загружен с веб-сайта поддержки NetApp. Cloud Manager доступен клиентам NetApp бесплатно.

                                Cloud Manager может использоваться с любыми кластеризованными платформами на основе Data ONTAP. Он обеспечивает конфигурирование, управление и мониторинг для этой окружающей среды, но это требует развертывания ONTAP Cloud и включает расширенные возможности, которые относятся только к ONTAP Cloud (такие, как инсталляция, приобретение облачных ресурсов, установка сети для работы в облаке и др.). Cloud Manager также управляет взаимоотношениями SnapMirror между ONTAP Cloud и on-premises решениями FAS.

                                Модели покупки ONTAP Cloud

                                ONTAP Cloud доступно на AWS Marketplace в нескольких вариантах приобретения. Его можно оплачивать почасово с предоплатой (pay-as-you-go), или купить подписку от NetApp.

                                Согласно почасовой модели, покупка совершается непосредственно в AWS Marketplace и состоит из платы за ПО ONTAP Cloud, поддержку и ресурсы AWS. В подписной модели клиент покупает шестимесячную или годовую подписку от торгового представителя NetApp и затем активирует подписку и поддержку через Cloud Manager, чтобы получить доступ в ONTAP Cloud. Этот подход называют BYOL (Bring Your Own License). Amazon в этом случае выставляет счет только за использование и обеспечение бесперебойной работы EC2 и ресурсы EBS.

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

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


                                Модели покупки сервисов Cloud ONTAP

                                Explore. Обеспечивает полную мощность Data ONTAP в предварительно сконфигурированном решении для приложений, требующих относительно небольших ресурсов. Предоставляет до 2TB емкости устройств хранения данных. Соответствует инстансам M4.XL и M3.XL для EC2.

                                Standard. Обеспечивает полную мощность Data ONTAP с гибкими производительностью и ресурсами. Имеет более широкую поддержку приложений и до 10 TB емкости. Соответствует инстансам M4.2XL, M3.2XL и R3.XL для EC2.

                                Premium. Обеспечивает полную мощность Data ONTAP для приложений, требующих больших ресурсов. Поддерживает до 368 TB емкости. Соответствует инстансу R3.2XL для EC2.

                                High Availability (HA). В конфигурации высокой доступности ONTAP Cloud поддерживает до 360 TB.

                                BYOL. В этой конфигурации ONTAP Cloud имеет расширенные опции, поддерживает до 360TB и все типы инстансов EC2 - M3.XL, M3.2XL, R3.XL, R3.2XL, M4.XL и M4.2XL.

                                ONTAP Cloud Explore и Standard являются полными версиями NetApp ONTAP. В покупку включены многие основные и наиболее важные возможности особенности Data ONTAP (SnapShot Copies, тома FlexClone, репликация данных SnapMirror, дедупликация данных, и др.). Также сюда включены OnCommand Cloud Manager и план поддержки корпоративного уровня NetApp Software Support Plan.

                                ONTAP Cloud High Availability (HA) - версия ONTAP Cloud, которое может быть сконфигурировано как двухузловое (two node) решение в облаке.

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

                                За ценой обращайтесь в netapp@muk.ua
                                Original source: habrahabr.ru (comments, light).

                                https://habrahabr.ru/post/338954/


                                Метки:  

                                Symfony + RabbitMQ Быстрый старт для молодых

                                Пятница, 29 Сентября 2017 г. 00:47 + в цитатник
                                php_freelancer сегодня в 00:47 Разработка

                                Symfony + RabbitMQ Быстрый старт для молодых

                                • Tutorial
                                Всем доброго времени суток, друзья.
                                Сегодня захотелось поговорить о том, как можно работать с RabbitMQ в Symfony и совсем чуть-чуть о некоторых подводных комнях. В конце я напишу парочку интересных моментов о кролике (рус. перевод «rabbit») для тех, кто совсем в танке.

                                Я не буду рассказывать про сам RabbitMQ, поэтому если вы пока и этого не знаете, почитайте следующие переводы:
                                habrahabr.ru/post/149694
                                habrahabr.ru/post/150134
                                habrahabr.ru/post/200870
                                habrahabr.ru/post/201096
                                habrahabr.ru/post/201178
                                Не бойтесь примеров на перле или пайтоне — это не страшно, все достаточно понятно из исходного кода.
                                + Все достаточно подробно описано, когда я читал это в свое время, достаточно было интерпретировать код мысленно, чтобы понять как что и зачем.

                                Если вы уже знаете, что такое консумер и почему в нем нужно делать $em->clear() + gc_collect_cycles, а после закрывать соединение с базой данных, то, скорее всего, вы ничего нового для себя не узнаете. Статья скорее для тех, кто не хочет разбираться с AMQP протоколом, но которым нужно прямо сейчас применять очереди и выбор почему-то бездумно пал на RabbitMQ, а не тот же легковестный beanstalkd.
                                Если же у вас микросервисная архитектура и вы ждете, что я расскажу вам как сварить коммуникацию между компонентами через AMQP, как красиво делать RPC, то я сам чего-то подобного очень давно жду на хабре…

                                Перед нами задача: отправлять сообщения на Email в очереди, используя RabbitMQ, а так же обеспечить отказоустойчивость: если почтовый сервер ответил таймаутом или что-то ещё сломалось — нужно попробовать выполнить задачу через 30 секунд ещё раз.

                                Итак, устанавливаем наш бандл:
                                github.com/php-amqplib/RabbitMqBundle
                                Я слишком ленив, чтобы описывать вам, как нужно копировать composer require команду и строку в AppKernel.
                                Я очень надеюсь, что вы сами это сделали и готовы приступать к конфигурированию нашего бандла.

                                Если нет, то вот вам полный гайд для самых маленьких:
                                Установка RabbitMQ:
                                echo 'deb http://www.rabbitmq.com/debian/ testing main' | sudo tee /etc/apt/sources.list.d/rabbitmq.list
                                wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add -
                                sudo apt-get update
                                sudo apt-get install rabbitmq-server
                                sudo rabbitmq-plugins enable rabbitmq_management
                                


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

                                Теперь устанавливаем сам бандл:
                                composer require php-amqplib/rabbitmq-bundle


                                И регистрируем его в нашем приложении:
                                // app/AppKernel.php
                                
                                public function registerBundles()
                                {
                                    $bundles = array(
                                        new OldSound\RabbitMqBundle\OldSoundRabbitMqBundle(),
                                    );
                                }
                                


                                Вот и всё.


                                Конфигурация бандла для нас:
                                old_sound_rabbit_mq:
                                    connections:
                                        default:
                                            host:     'localhost'
                                            port:     5672
                                            user:     'guest'
                                            password: 'guest'
                                            vhost:    '/'
                                            lazy:     false
                                            connection_timeout: 3
                                            read_write_timeout: 3
                                            keepalive: false
                                            heartbeat: 0
                                            use_socket: true
                                    producers:
                                        send_email:
                                            connection:       default
                                            exchange_options: { name: 'notification.v1.send_email', type: direct }
                                
                                    consumers:
                                        send_email:
                                            connection:       default
                                            exchange_options: { name: 'notification.v1.send_email', type: direct }
                                            queue_options:    { name: 'notification.v1.send_email' }
                                            callback:         app.consumer.mail_sender
                                


                                Здесь огромное внимание следует обратить на producers и consumers. Если очень коротко и просто: producer — это то, что отправляет сообщения через RabbitMQ в consumer, а consumer в свою очередь — та вещь, которая получает и обрабатывает эти сообщения. Здесь же exchange_options — опции для обменника (вы же прочитали статьи про rabbitmq, которые были в начале статьи?), queue_options — опции для очереди (аналогично). Так же стоит обратить внимание на callback в consumer — здесь указывается ID сервиса, который расширяет ConsumerInterface (execute метод с аргументом сообщения).

                                Т.к. пока что у вас его нету, при запуске приложения или компиляции контейнера мы получим какое-то DI исключение, что сервис не найден, но мы его запрашиваем. Поэтому давайте создавать наш сервис:
                                #app/config/services.yml
                                services:
                                    app.consumer.mail_sender:
                                        class: AppBundle\Consumer\MailSenderConsumer 
                                


                                И сам класс:
                                namespace AppBundle\Consumer;
                                
                                use OldSound\RabbitMqBundle\RabbitMq\ConsumerInterface;
                                use PhpAmqpLib\Message\AMQPMessage;
                                
                                /**
                                 * Class NotificationConsumer
                                 */
                                class MailSenderConsumer implements ConsumerInterface
                                {
                                    /**
                                     * @var AMQPMessage $msg
                                     * @return void
                                     */
                                    public function execute(AMQPMessage $msg)
                                    {
                                        echo 'Ну тут типа сообщение пытаюсь отправить: '.$msg->getBody().PHP_EOL;
                                        echo 'Отправлено успешно!...';
                                    }
                                }
                                


                                Ну вы же не обиделись, что я не включил в статью как работать со SwiftMailer? :) Нам важно, чтобы сюда асинхронно доставлялась строка через очередь сообщений, то, как мы будем обрабатывать эту строку — дело наше. Почта — всего лишь пример кейса.
                                Как же нам передать строку в наш консьюмер? Для этого давайте создадим тестовую команду:
                                namespace AppBundle\Command;
                                
                                use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
                                use Symfony\Component\Console\Input\InputInterface;
                                use Symfony\Component\Console\Output\OutputInterface;
                                
                                class TestConsumerCommand extends ContainerAwareCommand
                                {
                                    /**
                                     * {@inheritdoc}
                                     */
                                    protected function configure()
                                    {
                                        $this
                                            ->setName('app:test-consumer')
                                            ->setDescription('Hello PhpStorm');
                                    }
                                
                                    /**
                                     * {@inheritdoc}
                                     */
                                    protected function execute(InputInterface $input, OutputInterface $output)
                                    {
                                        $this->getContainer()->get('old_sound_rabbit_mq.send_email_producer')->publish('Сообщенька для отправки на мыло...');
                                    }
                                

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

                                Теперь запускаем наш consumer и приказываем ему ждать сообщения из RabbitMQ:
                                bin/console rabbitmq:consumer send_email -vvv

                                И отправим ему сообщение из нашей тестовой команды:
                                bin/console app:test-consumer

                                И вот сейчас, в процессе rabbitmq:consumer, мы можем увидеть наше сообщение! И что псевдо отправка завершилась успехом.

                                А теперь давайте посмотрим, как можно реализовать отложенную обработку сообщений в случае ошибок. Я не буду использовать плагин RabbitMQ для отложенных сообщений. Мы будем достигать этого путем создания новой очереди, в которой укажем время жизни сообщений 30сек и установим настройку: после смерти — перекладываться в основную очередь.
                                Достаточно лишь добавить новый producer:
                                producers:
                                        send_email:
                                            connection:       default
                                            exchange_options: { name: 'notification.v1.send_email', type: direct }
                                            
                                        delayed_send_email:
                                            connection: default
                                            exchange_options:
                                                name: 'notification.v1.send_email_delayed_30000'
                                                type: direct
                                            queue_options:
                                                name: 'notification.v1.send_email_delayed_30000'
                                                arguments:
                                                    x-message-ttl: ['I', 30000]
                                                    x-dead-letter-exchange: ['S', 'notification.v1.send_email']
                                


                                Теперь давайте изменим логику консумера:
                                namespace AppBundle\Consumer;
                                
                                use OldSound\RabbitMqBundle\RabbitMq\ConsumerInterface;
                                use OldSound\RabbitMqBundle\RabbitMq\ProducerInterface;
                                use PhpAmqpLib\Message\AMQPMessage;
                                
                                /**
                                 * Class NotificationConsumer
                                 */
                                class MailSenderConsumer implements ConsumerInterface
                                {
                                    private $delayedProducer;
                                
                                    /**
                                     * MailSenderConsumer constructor.
                                     * @param ProducerInterface $delayedProducer
                                     */
                                    public function __construct(ProducerInterface $delayedProducer)
                                    {
                                        $this->delayedProducer = $delayedProducer;
                                    }
                                
                                    /**
                                     * @var AMQPMessage $msg
                                     * @return void
                                     */
                                    public function execute(AMQPMessage $msg)
                                    {
                                        $body = $msg->getBody();
                                
                                        echo 'Ну тут типа сообщение отправляю '.$body.' ...'.PHP_EOL;
                                
                                        try {
                                            if ($body == 'bad') {
                                                throw new \Exception();
                                            }
                                
                                            echo 'Успешно отправлено...'.PHP_EOL;
                                        } catch (\Exception $exception) {
                                            echo 'ERROR'.PHP_EOL;
                                            $this->delayedProducer->publish($body);
                                        }
                                    }
                                }
                                

                                А вообще для вывода полезно использовать LoggerInterface — и красиво и масштабируется.
                                Но нам же лень и мы не хотим создавать дополнительные «думки», верно? Просто знайте.

                                Теперь мы должны прокинуть producer для отложенной очереди:
                                #app/config/services.yml
                                services:
                                    app.consumer.mail_sender:
                                        class: AppBundle\Consumer\MailSenderConsumer
                                        arguments: ['@old_sound_rabbit_mq.delayed_send_email_producer']
                                


                                И изменим команду:
                                namespace AppBundle\Command;
                                
                                use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
                                use Symfony\Component\Console\Input\InputInterface;
                                use Symfony\Component\Console\Output\OutputInterface;
                                
                                class TestConsumerCommand extends ContainerAwareCommand
                                {
                                    /**
                                     * {@inheritdoc}
                                     */
                                    protected function configure()
                                    {
                                        $this
                                            ->setName('app:test-consumer')
                                            ->setDescription('Hello PhpStorm');
                                    }
                                
                                    /**
                                     * {@inheritdoc}
                                     */
                                    protected function execute(InputInterface $input, OutputInterface $output)
                                    {
                                        $this->getContainer()->get('old_sound_rabbit_mq.send_email_producer')->publish('Ура, сообщенька...');
                                        $this->getContainer()->get('old_sound_rabbit_mq.send_email_producer')->publish('bad');
                                    }
                                }
                                

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

                                Если мы запустим, то увидим следующий вывод:
                                Ну тут типа сообщение отправляю Ура, сообщенька...
                                Успешно отправлено...
                                
                                Ну тут типа сообщение отправляю bad...
                                ERROR
                                


                                Спустя 30 секунд еще раз появится сообщение об обработке:
                                Ну тут типа сообщение отправляю bad...
                                ERROR
                                


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

                                Теперь советы для вашего прода:
                                1)Не отходя от темы с максимальными попытками обработки: знайте на все 102% все возможные исключения контекста с которым вы работаете! Умейте представлять, когда повторная обработка требуется, а когда нет, иначе — привет мусорке из логов и отсутствия понимания что происходит. В случае, если битая задача будет крутится в RabbitMQ, с реальными данными, нормальными задачами, вы вряд ли сможете выкинуть сломанные задачи без костылей, не обновляя код консьюмера и не перезапуская его. Поэтому продумывайте это сразу. В данном случае правильным было бы ловить только лишь SMTPTimeOutException какой-нибудь.
                                Так же с такой моделью важно понимать, что: на 1 очередь — одна «глобальная ответственность смены состояния чего либо». Не стоит давать слишком много рискованных задач своему воркеру. Если рассмотреть вариант с 1С, то проблема может быть в следующем: допустим при успешном или неуспешном изменении\добавлении товара в 1С мы записываем в базу данных что-нибудь, например, дату последней удачной синхронизации или неудачной. Т.е. тут обновляются сразу 2 базы данных: бд 1С и бд вашего приложения. Допустим в 1С все успешно создалось, далее идет обновление в базе данных поля «дата последней удачной синхронизации» — хоп, вылезла ошибочка, опять же, сервер бд не отвечает — задача откладывается на «потом» и повторяется, пока база данных не начнет отвечать. И при этом каждый раз «подзадача» связанная с созданием сущности в 1С будет успешно выполняться, каждый раз при неудачной попытке записи в базу данных сайта, что неправильно.

                                2)Прочитайте про durable, раз уж мы с вами используем RabbitMQ. P.S: это заводится как true\false флаг «durable» в конфиге бандла, конкретно — в exchange_options и queue_options

                                3)Всю свою жизнь закрывайте соединение к базе данных после выполнения работы программы. А так же запускайте очистку EM и после сборщик мусора для чистки ссылок. Т.е. в конце концов наш консьюмер должен выглядеть как-то так:
                                class MailSenderConsumer implements ConsumerInterface
                                {
                                    private $delayedProducer;
                                
                                    private $entityManager;
                                
                                    /**
                                     * MailSenderConsumer constructor.
                                     * @param ProducerInterface      $delayedProducer
                                     * @param EntityManagerInterface $entityManager
                                     */
                                    public function __construct(ProducerInterface $delayedProducer, EntityManagerInterface $entityManager)
                                    {
                                        $this->delayedProducer = $delayedProducer;
                                        $this->entityManager = $entityManager;
                                        
                                        gc_enable();
                                    }
                                
                                    /**
                                     * @var AMQPMessage $msg
                                     * @return void
                                     */
                                    public function execute(AMQPMessage $msg)
                                    {
                                        $body = $msg->getBody();
                                
                                        echo 'Ну тут типа сообщение отправляю '.$body.' ...'.PHP_EOL;
                                
                                        try {
                                            if ($body == 'bad') {
                                                throw new \Exception();
                                            }
                                
                                            echo 'Успешно отправлено...'.PHP_EOL;
                                        } catch (\Exception $exception) {
                                            echo 'ERROR'.PHP_EOL;
                                            $this->delayedProducer->publish($body);
                                        }
                                        
                                        $this->entityManager->clear();
                                        $this->entityManager->getConnection()->close();
                                
                                        gc_collect_cycles();
                                    }
                                }
                                

                                Консьюмер работает как демон, поэтому постоянно копить в нем ссылки и держать соединение с бд — это плохо. В случае с MySQL вы получите MySQL server has gone away.

                                4)Много думайте, почему ваша модель отложенных сообщений может неожиданно убить ваш бизнес. Например у нас есть механизм, который при изменении товара в админке заливает эти изменения через очередь в 1С. Теперь представим ситуацию: администратор меняет товар -> создается задача #1 на попытку изменить те же данные в базе 1С. Сервер 1С не отвечает, поэтому задачка просто перекладывается постоянно, пока все не заработает. За это время администратор решил еще кое-что подправить в том же товаре, что он и делает. Регистрируется задача #2.
                                А теперь представьте ситуацию, когда поочередно выполняются и откладываются задачи #1 и #2.
                                Что если 1С заработает к моменту выполнения задачи #2? Задача выполнится и зальёт последние изменения. Далее пойдет в ход задача #1 и затрет собой стабильные изменения :)
                                Выход: отправляем timestamp в качестве version, и, если задача «из прошлого» — выкидываем её.

                                5)Идешь в асинхронность — прочитай про многие архитектурные проблемы, а также race condition, несогласованность консумеров на разных машинах и прочее.

                                6)Пишите версии вашим очередям… Ух как помогает на реальном проде. В принципе мы так и сделали в этом примере.

                                7)Возможно тебе не нужен RabbitMQ и целый AMQP протокол. Посмотри в сторону beanstalkd.

                                8)Запускайте консумеры и всякое прочее демоническое на php через supervisor и подключите полное логирование падения процессов в нём. У него так же есть web интерфейс для управления всем этим делом, что так же очень удобно. Проблемы будут всегда.
                                Original source: habrahabr.ru (comments, light).

                                https://habrahabr.ru/post/338950/


                                Метки:  

                                Поиск сообщений в rss_rss_hh_new
                                Страницы: 1437 ... 1165 1164 [1163] 1162 1161 ..
                                .. 1 Календарь