...

суббота, 2 июля 2016 г.

Разрабатываем систему real-time fulltext-поиска по error-логам на основе ClickHouse от Яндекса

В этой статье я расскажу о том, как разработать систему для индексирования и полнотекстового поиска error-логов (или любых других логов) на основе СУБД от Яндекса под названием ClickHouse. Про саму базу Яндекс писал на Хабре сначала когда база была закрытой, а потом когда они её заопенсорсили. База данных в первую очередь предназначена для аналитики и для реализации сервиса Яндекс.Метрика, но может на самом использоваться для чего угодно, если вам подходит загружать данные пачками, удалять их тоже огромными пачками и никогда не обновлять отдельные строки.

Что мы будем делать


Мы будем реализовывать систему для индексирования и поиска по error-логам. При этом, считается, что сами логи вы уже сумели доставить на центральный сервер (или несколько серверов) и уже засунули сами тексты сообщений в базу, то есть у вас уже есть таблица в какой-нибудь базе данных примерно следующего вида:
CREATE TABLE Messages (
    message_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    created_ts DATETIME,
    message_text BLOB
)

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


Почему не ElasticSearch / Sphinx / MySQL / другое_решение?


Мне кажется интересным посмотреть, что из себя представляет ClickHouse, и какие задачи с его помощью можно решать. Целью статьи является дать людям обзор и пищу для размышления, нежели дать готовое решение. Elastic, Sphinx и прочие представляют из себя готовые движки для поиска, тогда как ClickHouse является базой данных общего назначения, из которой можно слепить всё, что угодно. Также, у меня есть мнение, что система поиска, представленная в статье на основе ClickHouse будет справляться с задачей поиска по логам лучше, чем Sphinx, и при этом не нужно будет использовать 2 вида индексов (реалтайм и обычный). Ваш опыт может отличаться, поэтому рекомендую сначала попробовать сделать прототип перед тем, как внедрять такую систему в продакшен.

Установка сервера


Поручите задачу установки ClickHouse (github) вашему системному администратору или поставьте его сами из докера, если вы не хотите ничего решать, или вам просто лень. Если будете собирать сами из исходных кодов, вам потребуется до 30 гб места, имейте это в виду.

Установка клиента


Если у вас в системе почему-то нет curl или php, установите их. Дальнейшие примеры будут пользоваться curl в качестве API к базе и PHP для написания системы индексации и поиска.

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


Как правило, структуры для полнотекстового поиска в движках для поиска весьма простые. Структура называется Inverted Index, и мы с вами её реализуем, в немного упрощенном виде. Мы будем пользоваться движком «по умолчанию», рекомендуемым для данных, имеющих как первичный ключ, так и дату — MergeTree:
CREATE TABLE FT (
EventDate Date,
word_id UInt32,
message_id UInt64
) ENGINE=MergeTree(EventDate, (word_id, message_id), 8192);

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

$ cat create.sql | curl 'http:/hostname:8123/?query=' --data-binary @-


В этой команде в файле create.sql должен лежать запрос, который нужно выполнить, а hostname — это хост с поднятым ClickHouse, 8123 — дефолтный порт.

В указанной выше структуре word_id — это id слова в словаре (который мы создадим позже, в словаре хранится соответствие word_text => word_id), а message_id — это id соответствующей записи в таблице с логами (аналог document_id для Sphinx).

Параметры для MergeTree движка: первое поле EventDate означает имя колонки с датой события, вторая колонка (word_id, message_id) определяет первичный ключ (по сути, обычный индекс) и 8192 — это настройка, влияющая на гранулярность индекса, мы её оставим по умолчанию.

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

Создаем структуры для словаря


Для того, чтобы заполнить этот индекс, нам нужна структура типа «словарь», которая нужна для того, чтобы хранить числа в ClickHouse вместо строк. Словарь можно создать в базе данных, и если это MySQL, то структура будет выглядеть следующим образом:
CREATE TABLE Words (
  id int(11) unsigned NOT NULL AUTO_INCREMENT,
  word varchar(150) COLLATE ascii_bin NOT NULL DEFAULT '',
  PRIMARY KEY (id),
  UNIQUE KEY word (word)
) ENGINE=InnoDB DEFAULT CHARSET=ascii COLLATE=ascii_bin;


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

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


Для того, чтобы управлять процессом индексации и чтобы инициировать начальное индексирование, можно завести отдельную таблицу в MySQL с очередью для сообщений, которые мы ещё не проиндексировали:
CREATE TABLE IndexQueue (
  message_id bigint(20) unsigned NOT NULL DEFAULT '0',
  shard_id int(11) NOT NULL,
  PRIMARY KEY (shard_id,message_id)
);

Чтобы наполнить эту таблицу в первый раз, можно использовать следующий запрос:

INSERT IGNORE INTO IndexQueue (message_id, shard_id) SELECT message_id, message_id % 4 FROM Messages

Здесь 4 — это количество потоков индексатора, которые мы будем использовать. На PHP7 код из примера ниже дает производительность примерно 3,5 мб/сек на один процесс, в 4 потока соотвественно получается 14 мб/сек. Если вы пишете больше error-логов, чем 14 мб/сек, то вероятно вам нужно срочно чинить ваш продакшен и вам не до того, что полнотекстовый поиск немного отстает :).

Алгоритм индексатора будет следующим:

  1. Посмотреть записи в очереди (IndexQueue) для указанного шарда
  2. Выбрать пачку записей и выделить в каждом сообщении слова и сложить в массив $index вида message_id => array(word1, ..., wordN)
  3. Для каждого слова найти соответствующий word_id в словаре, и если такого слова ещё нет, то добавить
  4. Вставить в индекс в ClickHouse записи по всем словам всех сообщений

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

Упрощенная реализация индексатора на PHP
const CH_HOST = '<hostname>:8123';
const MAX_WORD_LEN = 150; // должно соответствовать тому, что в таблице Words
$mysqli = mysql_connect(...); // коннект к базе
$limit = 10000; // максимальный размер пачки сообщений при индексации
$shard_id = intval($argv[1] ?? 0); // номер шарда (указывается первым аргументом скрипту, если не указан, то будет 0)
echo "Indexing shard $shard_id\n";

while ($mysqli->query('SELECT MAX(message_id) FROM IndexQueue WHERE shard_id = ' . $shard_id)->fetch_row()[0]) {
    $index = "";
    $start = microtime(true);

    $ids = [];
    foreach ($mysqli->query('SELECT message_id FROM IndexQueue WHERE shard_id = ' . $shard_id . ' ORDER BY message_id LIMIT ' . $limit)->fetch_all() as $row) {
        $ids[] = $row[0];
    }

    if (empty($ids)) {
        break;
    }

    $message_texts = $mysqli->query('SELECT message_id, `message_text` FROM Messages WHERE message_id IN(' .  implode(', ', $ids) . ')')->fetch_all(MYSQLI_ASSOC);

    $unknown_words = [];
    $msg_words = [];

    $total_length = 0;

    foreach ($message_texts as $msg) {
        $msg_id = $msg['message_id'];
        $text = $msg['message_text'];

        $total_length += strlen($text);

        $words = array_unique(
            array_filter(
                preg_split('/\W+/s', $text),
                function($a) {
                    $len = strlen($a);
                    return $len >= 2 && $len <= MAX_WORD_LEN;
                }
            )
        );

        foreach ($words as $word) {
            $unknown_words[$word] = true;
        }

        $msg_words[$msg_id] = $words;
    }

    if (!count($message_texts)) {
        $mysqli->query('DELETE FROM IndexQueue WHERE shard_id = ' . $shard_id . ' AND message_id IN(' . implode(', ', $ids) . ')');
        continue;
    }

    if (!count($unknown_words)) {
        var_dump($message_texts);
        die("Empty message texts!\n");
    }

    $words_res = $mysqli->query('SELECT word, id FROM Words WHERE word IN(' . INstr(array_keys($unknown_words)) . ')')->fetch_all(MYSQLI_ASSOC);

    $word_ids = [];
    foreach ($words_res as $row) {
        $word_ids[$row['word']] = $row['id'];
        unset($unknown_words[$row['word']]);
    }

    if (count($unknown_words)) {
        echo "Inserting " . count($unknown_words) . " words into dictionary\n";

        $values = [];
        foreach ($unknown_words as $word => $_) {
            $values[] = "('" . $mysqli->escape_string($word) . "')";
        }
        $mysqli->query('INSERT IGNORE INTO Words (word) VALUES ' . implode(',', $values));
        $words_res = $mysqli->query('SELECT word, id FROM Words WHERE word IN(' . INstr(array_keys($unknown_words)) . ')')->fetch_all(MYSQLI_ASSOC));

        foreach ($words_res as $row) {
            $word_ids[$row['word']] = $row['id'];
            unset($unknown_words[$row['word']]);
        }
    }

    if (count($unknown_words)) {
        die("Could not fill dictionary\n");
    }

    foreach ($msg_words as $msg_id => $words) {
        foreach ($words as $word) {
            // здесь неявно предполагается, что unix timestamp из message_id можно вычислить путем отрезания последних 32 бит
            $index .= date('Y-m-d', $msg_id >> 32) . "\t" . $word_ids[$word] . "\t" . $msg_id . "\n";
        }
    }

    $ch = curl_init('http://' . CH_HOST . '/?query=' . rawurlencode('INSERT INTO FT FORMAT TabSeparated'));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $index);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

    $res = curl_exec($ch);

    if ($res !== "") {
        die($res . "\n");
    }

    $mysqli->query('DELETE FROM IndexQueue WHERE shard_id = ' . $shard_id . ' AND message_id IN(' . implode(', ', $ids) . ')');
    echo "Speed " . round($total_length / 1024 / (microtime(true) - $start), 2) . " KiB/sec\n";
}

function INstr(array $values) {
    global $mysqli;
    $res = [];
    foreach ($values as $v) $res[] = "'" . $mysqli->escape_string($v) . "'";
    return implode(',', $res);
}


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


Нам не нужны алгоритмы ранжирования при поиске, которыми так богаты Elastic, Sphinx и другие решения, и нам нужна просто сортировка по дате, поэтому поиск будет исключательно прост. По сути, чтобы найти что-либо по запросу «hello world 111», нам нужно сначала найти word_id в словаре (предположим, что это будет 1, 2 и 3 соответственно) и выполнить следующий запрос:
SELECT message_id FROM FT
WHERE word_id IN(1, 2, 3)
GROUP BY message_id
HAVING uniq(word_id) = 3
ORDER BY message_id DESC
LIMIT 50

Обратите внимание на то, что в каждом документе, который мы ищем, должны присутствовать все слова из запроса, поэтому мы пишем HAVING uniq(word_id) = 3 (uniq(word_id) — это аналог COUNT(DISTINCT word_id) в обычных SQL-базах), где 3 — это количество различных слов в запросе.

Мы предполагаем, что сортировка по message_id будет означать сортировку по времени. Этого можно добиться, если в первые 32 бита message_id записывать UNIX TIMESTAMP события в секундах, а во вторую половину — микросекунды события (если есть) и случайные числа.

Результаты


Для тестирования производительности этого решения, я взял базу данных error-логов с нашего девел-сервера объемом в 3 Гб (1,6 млн событий) и проиндексировал. Индексатор показал скорость индексации в 3,5 Мб/сек на один поток, что для моего случая было более, чем достаточно. В данный момент мы используем Sphinx для полнотекстового поиска по error-логам, поэтому я могу примерно сравнить производительность этих двух решений, поскольку работают они примерно в одинаковых условиях на одном и том же железе. Индексация у Sphinx (по крайней мере, построение не realtime-индекса) в несколько раз быстрее в расчете на одно ядро, но учитывайте, что индексатор сфинкса написан на C++, а наш — на PHP :).

Чтобы вычислить самый тяжелый запрос для ClickHouse (и, очевидно, для Sphinx тоже), я решил найти самые популярные слова в индексе:

$ echo 'SELECT word_id, count() AS cnt FROM FT GROUP BY word_id ORDER BY cnt DESC LIMIT 5' | curl 'http://hostname:8123/?query=' --data-binary @-
5       1669487
187     1253489
183     1217494
159     1216255
182     1199507

Запрос занял 130 мс при общем количестве записей в 86 млн, впечатляет! (на тестовой машине 2 ядра).

Итак, если взять топ-5 и превратить word_id в нормальные слова, то запрос для исполнения получится следующий: «php wwwrun _packages ScriptFramework badoo». Эти слова встречаются у нас почти в каждом сообщении и их можно спокойно выкинуть из индекса, но я их оставил для проверки производительности поиска.

Выполняем запрос в ClickHouse:

SELECT message_id FROM FT WHERE word_id IN(189, 159, 187, 5, 183) GROUP BY message_id HAVING uniq(word_id) = 5 ORDER BY message_id DESC LIMIT 51

И похожий запрос в Sphinx:

SELECT message_id FROM FT WHERE MATCH('php wwwrun _packages ScriptFramework badoo') ORDER BY message_id DESC LIMIT 51

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

ClickHouse: 700 мс
Sphinx: 1500 мс

Учитывая, что Sphinx умеет ранжировать результаты, а наша система нет, время у Sphinx весьма неплохое. Не забывайте, что за время выполнения запроса оба демона должны были объединить результаты для ~6 млн документов (по 1,2 млн документов на слово) и делали это на скромных 2 ядрах. Вполне возможно, что при должной настройке, времена, указанные в этом (немного синтетическом) тесте, поменяются местами, но тем не менее, результатами лично я очень доволен и можно смело сказать, что для построения реалтайм-поиска по логам ClickHouse подходит очень хорошо.

Спасибо за то, что прочли статью до конца и надеюсь, что она вам понравилась.

P.S. Я не являюсь сотрудником Яндекса и с Яндексом никак не связан, я просто хотел попробовать их базу данных для реальной задачи :).

Ссылки


  1. Сайт ClickHouse
  2. Статья на Хабре до open-source
  3. Опен-сорс статья на Хабре
  4. Github
  5. ClickHouse Docker

Комментарии (0)

    Let's block ads! (Why?)

    systemd: getty-подобный сервис для htop

    Пакет-географ – первая рабочая версия

    Фейковая Новела: новый опыт и работа над ошибками

    [Перевод] Учите детей программировать — они изменят мир

    SuggestBundle или выпадающие ajax списки в Symfony

    Практически во всех проектах возникает необходимость отобразить выпадающий список в форме, данный в котором грузились бы с помощью ajax. В связи с этим еще 2 года назад я написал SuggestBundle для симфони, который содержит дополнительный тип формы, который может использоваться вместо стандартных типов entity и document. Ключевая особенность бандла в том, что выпадающий список подгружается с помощью ajax (при помощи библиотеки select2). На днях я наконец собрался с силами, чтобы написать документацию по бандлу и решил поделиться разработкой с сообществом.


    Кроме того бандл можно использовать и для построения выпадающих список, которые никак не связаны с Doctrine ORM и Doctrine ODM.


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



    Ссылка на бандл: http://ift.tt/29e53cz.


    1. Установка


    Добавьте sirian/suggest-bundle пакет в секцию require в файле composer.json.


    $ composer require sirian/suggest-bundle
    

    Добавьте SuggestBundle в ваш AppKernel.php:


    <?php
    public function registerBundles()
    {
        $bundles = array(
            // ...
            new Sirian\SuggestBundle\SirianSuggestBundle(),
            // ...
        );
        ...
    }
    

    2. Конфигурация


    После утсановки бандла, добавьте следующие строки в конфигурацию роутинга:


    # app/config/routing.yml
    
    _sirian_suggest:
        resource: "@SirianSuggestBundle/Resources/config/routing.yml"
        prefix:   /suggest
    

    И выберите версию виджета, которая будет использоваться по-умолчанию для форм (зависит от версии библиотеки select2, которую вы будете использовать в проекте). Допустимые значения select2_v3, select2_v4. Также вы можете указать другие опции формы, которые будут использоваться по умолчанию при использовании типа формы SuggestType::class


    # app/config/config.yml
    
    ...
    sirian_suggest:
        form_options:
            widget: select2_v4
            attr:
                placeholder: "Search..."
    

    3. Настройка саггестеров


    Для документов из Doctrine ODM и сущностей Doctrine ORM вы можете легко описать необходимые саггестеры в файле config.yml вашего проекта.


    3.1. Doctrine ODM Document (Mongodb)


    # app/config/config.yml
    
    ...
    sirian_suggest:
        odm:
            category:
                class: "MainBundle:Category"
                property: name
    
            user:
                class: "MainBundle:User"
                property: username
                search:
                    email: ~
                    username: ~
    

    3.2. Doctrine ORM Entity


    # app/config/config.yml
    
    ...
    sirian_suggest:
        orm:
            category:
                class: "MainBundle:Category"
                property: name
    
            user:
                class: "MainBundle:User"
                property: username
                search:
                    email: ~
                    username: ~
    

    3.3. Произвольные саггестеры


    В случае если вам необходима дополнительная логика для построения выпадающего спика — вы можете создать свой собственный саггестер. Например, давайте создадим AdminSuggester который будет содержать только тех пользователей, которые имеют роль ROLE_ADMIN. Для простоты мы может унаследовать класс от абстрактного класса DocumentSuggester (или EntitySuggester для Doctrine ORM).


    <?php
    
    namespace App\MainBundle\Suggest;
    
    use App\MainBundle\Document\User;
    use Doctrine\Common\Persistence\ManagerRegistry;
    use Sirian\SuggestBundle\Suggest\DocumentSuggester;
    use Sirian\SuggestBundle\Suggest\Item;
    use Sirian\SuggestBundle\Suggest\SuggestQuery;
    
    class AdminSuggester extends DocumentSuggester
    {
        public function __construct(ManagerRegistry $registry)
        {
            $options = [
                'class' => User::class,
                'id_property' => 'id',
                'property' => 'username', // Свойство или метод, который будет использоваться для текстового представления в выпадающем списке
                'search' => ['name' => 1, 'username' => 1, 'email' => 1]
            ];
    
            parent::__construct($registry, $options);
        }
    
        protected function createSuggestQueryBuilder(SuggestQuery $query)
        {
            $qb = parent::createSuggestQueryBuilder($query);
    
            $qb->field('roles')->equals('ROLE_ADMIN');
    
            return $qb;
        }
    }
    
    

    Опишите новый сервис в services.yml с тегом sirian_suggest.suggester


        app.suggester.admin:
            class: App\MainBundle\Suggest\AdminSuggester
            arguments: ["@doctrine_mongodb"]
            tags: 
                - {name: 'sirian_suggest.suggester', alias: 'admin'}
    

    Алиас admin будет использоваться в параметре suggester для типа формы SuggestType::class и в url-адресе /suggest/admin .


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


    $formBuilder->add('category', SuggestType::class, [
        'suggester' => 'category'
    ])
    

    P.S.Больше информации по использованию бандла и дополнительному функционалу вы найдете на странице http://ift.tt/29e53cz.

    Комментарии (0)

      Let's block ads! (Why?)

      PowerShell для исследователя

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

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

      Strings


      Не могу сказать, что strings из набора Sysinternals одна из самых востребованных утилит и все же время от времени она используется. Соорудить ее аналог на PowerShell можно буквально за пять минут.
      function Get-Strings {
        param(
          [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
          [ValidateScript({Test-Path $_})]
          [String]$FileName,
          
          [Alias('b')]
          [UInt32]$BytesToProcess = 0,
          
          [Alias('f')]
          [UInt32]$BytesOffset = 0,
          
          [Alias('n')]
          [Byte]$StringLength = 3, #длина строки по умолчанию
          
          [Alias('o')]
          [Switch]$StringOffset,
          
          [Alias('u')]
          [Switch]$Unicode
        )
        
        begin {
          $FileName = Resolve-Path $FileName
          
          $enc = switch ($Unicode) {
            $true  {[Text.Encoding]::Unicode}
            $false {[Text.Encoding]::UTF7} #кодировка по умолчанию
          }
          #вспомогательная функция, преобразующая байты в строки
          function private:Read-Buffer([Byte[]]$Bytes) {
            ([Regex]"[\x20-\x7E]{$StringLength,}").Matches(
              $enc.GetString($Bytes)
            ) | ForEach-Object {
              if ($StringOffset) {'{0}:{1}' -f $_.Index, $_.Value} else {$_.Value}
            }
          }
        }
        process {
          try {
            $fs = [IO.File]::OpenRead($FileName)
            #количество байтов для обработки, ровно как и смещение, не могут быть
            #больше длины самого файла, иначе данные находятся за пределами потока
            if ($BytesToProcess -ge $fs.Length -or $BytesOffset -ge $fs.Length) {
              throw New-Object InvalidOperationException('Out of stream.')
            }
            #если указано смещение, прыгаем в нужное место
            if ($BytesOffset -gt 0) {[void]$fs.Seek($BytesOffset, [IO.SeekOrigin]::Begin)}
            #количество байтов для обработки, по умолчанию считываются все байты
            $buf = switch ($BytesToProcess -gt 0) {
              $true  {New-Object Byte[] ($fs.Length - ($fs.Length - $BytesToProcess))}
              $false {New-Object Byte[] $fs.Length}
            }
            [void]$fs.Read($buf, 0, $buf.Length)
            Read-Buffer $buf #выводим данные в хост
          }
          catch { $_.Exception }
          finally {
            if ($fs) {
              $fs.Dispose()
              $fs.Close()
            }
          }
        }
        end {}
      }
      
      Set-Alias strings Get-Strings #псевдоним для нашей функции
      
      

      Примеры:
      PS E:\proj> strings -b 100 -f 20 debug\app.exe
      !This program cannot be run in DOS mode.
      PS E:\proj> strings -u debug\app.exe
      ...
      
      

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

      Обертки kd.exe


      А вот это уже крайне полезные в быту вещи, так как вместо того, чтобы вызывать всякий раз kd.exe для того, чтобы посмотреть, скажем, на то как выглядит та или иная структура, достаточно «скормить» PowerShell команду-обертку.
      function Invoke-DisplayType {
        param(
          [Parameter(Mandatory=$true, Position=0)]
          [ValidateNotNullOrEmpty()]
          [String]$Module,
          
          [Parameter(Mandatory=$true, Position=1)]
          [ValidateNotNullOrEmpty()]
          [String]$DataType,
          
          [Parameter(Position=2)]
          [String]$Arguments = $null
        )
        
        begin {
          #если путь к Debugging Tools не прописан в переменной PATH, задаем его
          #для текущей сессии автоматически, извлекая оный до утилит из ярлыка
          #WinDbg, так как установочный пакет не пишет его в реестр
          if ((Get-Command -CommandType Application kd.exe -ea 0) -eq $null) {
            $env:path += ";$(Split-Path (
              New-Object -ComObject WScript.Shell
            ).CreateShortcut((
              Get-ChildItem $env:allusersprofile -Include WinDbg.lnk -Recurse
            ).FullName).TargetPath)"
          }
          
          if (!$Module.EndsWith('.dll')) {
            $BaseName = $Module
            $Module += '.dll'
          }
          else {
            $BaseName = $Module.Substring(0, $Module.LastIndexOf('.'))
          }
          $Module = "$([Environment]::SystemDirectory)\$Module"
          
          if (!(Test-Path $Module)) {
            throw "Модуль $Module не найден."
          }
        }
        process {
          #вызываем kd, попутно отсеивая лишнее из вывода
          kd -z $Module -c "dt $BaseName!_$DataType $Arguments;q" -r -snc |
          Select-String -Pattern '\A\s+'
        }
        end {}
      }
      
      Set-Alias dt Invoke-DisplayType
      
      

      Примеры:
      PS E:\proj> dt ole32 system_information_class
      
         SystemBasicInformation = 0n0
         SystemProcessorInformation = 0n1
         SystemPerformanceInformation = 0n2
         SystemTimeOfDayInformation = 0n3
         SystemPathInformation = 0n4
         SystemProcessInformation = 0n5
      ...
      
      PS E:\proj> dt ole32 io_status_block
      
         +0x000 Status           : Int4B
         +0x000 Pointer          : Ptr32 Void
         +0x004 Information      : Uint4B
      
      PS E:\proj> dt ole32 system* /t
      ...
      
      

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

      Продолжение следует...

      Комментарии (0)

        Let's block ads! (Why?)

        пятница, 1 июля 2016 г.

        Вышел официальный HTTP клиент для Yii 2

        NW.js или Electron?

        [Перевод] ZigBee и Intel Edison: практика автоматизации переговорных комнат

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


        Мы создали интеллектуальную систему бронирования переговорных комнат (Smart Conference Room System, SCR) для того, чтобы помочь всем желающим с этими проблемами справиться.

        Общий обзор


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

        Система построена на основе следующих аппаратных компонентов:

        • Плата Intel Edison.
        • Плата расширения Arduino.
        • Модуль XBee ZB S2 ZigBee.
        • Android-смартфон.
        • Push-сервер.
        • Датчик освещённости ZigBee.
        • Инфракрасный датчик присутствия ZigBee.
        • Интеллектуальная розетка ZigBee.
        • Сигнализация ZigBee.

        ZigBee – это спецификация набора высокоуровневых коммуникационных протоколов, которые используются для создания персональных сетей, в основе которых – небольшие маломощные цифровые передатчики. Протоколы ZigBee базируются на стандарте IEEE 802.15.4 и предназначены для использования во встраиваемых устройствах, которые требуют низкого энергопотребления и способны нормально работать при невысоких скоростях передачи данных. Сеть, построенная на основе ZigBee, будет весьма экономичной в плане потребления энергии. Для того, чтобы пройти сертификацию ZigBee, устройство должно работать от батарей не менее двух лет. Типичные применения таких устройств – это домашняя автоматизация. Например, интеллектуальные датчики присутствия, системы освещения, терморегуляторы.

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

        Важный аспект платформы Arduino – это стандартизированные коннекторы, которые позволяют подключать к основному блоку с микроконтроллером множество взаимозаменяемых модулей расширения, известных как шилды (shields). Плата Intel Edison также поддерживает Arduino. Это делает Edison совместимым с тысячами модулей для Arduino, например, с XBee ZigBee.

        XBee – это название марки семейства радиомодулей от Digi International, выпускаемых в стандартных форм-факторах. XBee ZB поддерживает протокол ZigBee PRO для создания сетей с ячеистой топологией.

        С программной точки зрения система состоит из следующих частей:

        • Серверное ПО.
        • Приложение для Android-смартфона.
        • Прошивка для Intel Edison.

        Вот как, в самых общих чертах, выглядит работа Smart Conference Room System.

        Схема работы SCR

        Аппаратное обеспечение


        Остановимся подробнее на аппаратном обеспечении, на котором основан наш проект.

        Intel Edison с коммутационной платой Arduino – это ядро системы. Так как Edison совместим с Arduino, устройства, которые могут работать с Arduino, подходят и для Edison. Например, это радиомодуль XBee ZB S2, который подключается к Edison с помощью платы расширения Arduino.


        Intel Edison и радиомодуль XBee

        В качестве Push-сервера мы использовали Windows-планшет Fujitsu STYLISTIC Q702 с процессором Intel Core i5-3427U (частота 1.80 ГГц), оснащённый 4 Гб оперативной памяти.


        Планшет Fujitsu STYLISTIC Q702

        Для того, чтобы с нашей системой было удобнее работать, мы создали для неё мобильное Android-приложение. Его испытания проводились на смартфоне Lenovo K900. Он оснащён процессором Intel Atom Z2580 (частота 2 ГГц) и 2 Гб оперативной памяти.


        Смартфон Lenovo K900

        Роль координатора ZigBee играет радиомодуль XBee ZB S2, выполненный в форм-факторе платы расширения для Arduino и соответствующий требованиям протокола ZigBee. Он управляет подключёнными к нему ZigBee-датчиками.


        Радиомодуль XBee ZB S2

        В проекте задействован датчик освещённости Netvox Z311X. Он соответствует требованиям стандарта ZigBee и отвечает за измерение уровня освещённости в помещении.


        Датчик освещённости

        В качестве датчика присутствия используется инфракрасный сенсор Netvox ZB11D. Он так же поддерживает работу по протоколу ZigBee и играет роль конечного сетевого устройства.


        Датчик присутствия

        В системе используется модуль сигнализации Netvox Z602A. Это устройство совмещает в себе средства звукового и светового оповещения и применяется в экстренных ситуациях. Модуль основан на стандарте ZigBee HA.


        Сигнализация

        Интеллектуальная ZigBee-розетка, применяемая в проекте, это Netvox Z809AG. Устройство совмещает функции учёта электроэнергии и управления электрическими цепями. С его помощью можно включать и выключать питание различных электроприборов в комнате.


        Интеллектуальная розетка

        Аппаратная инфраструктура


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

        Схема устройства системы

        Аппаратную архитектуру системы можно разбить на следующие четыре части:

        • Смартфон.
        • Push-сервер.
        • Шлюз на базе Intel Edison.
        • Набор ZigBee-датчиков.

        Эти аппаратные блоки ориентированы на три основные функции SCR.
        1. Наблюдение за комнатой и оценка ситуации. ZigBee-датчики освещённости и присутствия, в режиме реального времени, поставляют сведения о ситуации в комнате шлюзу на базе Intel Edison. Для связи датчиков и шлюза используется сеть ZigBee. Edison анализирует показания, определяет, есть ли кто-нибудь в комнате, а затем отправляет свои выводы push-серверу по Wi-Fi.
        2. Бронирование комнаты и интеллектуальное расписание. Сотрудники могут резервировать переговорные, которые, по оценке системы, свободны. Делается это с помощью Android-смартфона. Если доступных переговорных нет, сотрудник может выбрать занятую комнату и включить режим ожидания освобождения переговорной. Как только система, в частности – Intel Edison, сочтёт, что выбранная комната свободна, сообщение об этом поступит на сервер, который отправит уведомление приложению на смартфоне.
        3. Удалённый доступ и управление. Приложения могут запрашивать сведения о состоянии зарезервированной комнаты по Wi-Fi через push-сервер для того, чтобы управлять устройствами в комнате, например, включать и выключать свет, и получать, в режиме реального времени, сведения с ZigBee-датчиков.

        Программное обеспечение


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

        На рисунке ниже показана структурная схема используемого программного решения.


        Архитектура программного решения

        Push-сервер


        В качестве push-сервера мы использовали GlassFish 4.0. Вот, как выглядит архитектура сервера.

        Архитектура сервера

        Системная диаграмма сервера показана ниже. Когда Edison, пользуясь сведениями с ZigBee-датчиков, определит, что состояние помещения изменилось со «свободно» на «занято», или наоборот, он оповестит об этом сервер. Сервер, в свою очередь, изменит соответствующую запись в базе данных, а затем, если комната пуста, отправит сообщение Android-приложению.


        Рабочий процесс Push-сервера

        Intel Edison и ZigBee


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

        Устройство стека ZigBee в Arduino

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

        Классы стека ZigBee

        Класс
        Функция
        XBeeAddress
        Базовый класс адреса устройства ZigBee
        XBeeAddress64
        64-битный IEEE-адрес устройства ZigBee
        XBeeAddress16
        16-битный сетевой адрес устройства ZigBee
        Payload
        Полезная нагрузка командного кадра ZigBee
        ExplicitAddressCommand
        Командный кадр ZigBee, используемый в спецификации домашней автоматики
        ExplicitAddressCommandResponse
        Ответ на команду на явно заданный адрес
        XBeeSensor
        Базовый класс ZigBee-датчика
        XBeeLightSensor
        Датчик освещённости ZigBee
        XBeeInfraSensor
        Датчик присутствия ZigBee
        XBeeAlarm
        Сигнализация ZigBee

        Основные функции ArduinoXBee


        Главный класс ArduinoXBee – это XBeeCoordinator. Этот класс отвечает за управление ZigBee-датчиками. Его основные задачи заключаются в сборе сведений с датчиков и в отправке им команд для удалённого управления их поведением. Вот описание функций, применяемых для работы с датчиками.

        Int getLightValue(XBeeLightSensor lightSensor). Эта функция используется для получения сведений с датчика освещённости. На её вход подаётся объект датчика освещённости, на выходе получаем целое число в диапазоне от 0 до 65535.

        bool getInfraValue(XBeeInfraSensor infraSensor). Функция получает показания заданного датчика присутствия. При вызове ей передаётся объект датчика присутствия, а возвращает она логическое значение. True указывает на то, что в комнате кто-то есть, False – на то, что комната пуста.

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

        void turnOnSwitch(XBeeSmartPlug plug) и void turnOffSwitch(XBeeSmartPlug plug). Эти функции позволяют включать и отключать подачу электричества интеллектуальными розетками. При вызове функций им передают объект, соответствующий розетке, они не возвращают ничего.

        Приложение для Android


        Когда пользователь запускает приложение, у него есть возможность создать новое расписание или управлять существующим, а именно – удалить его. Создавая новое расписание, пользователь может выполнять поиск переговорных по времени и расположению. Если комната свободна, её можно тут же зарезервировать. Если комната занята, пользователь может выбрать вариант ожидания освобождения комнаты. Как только датчики ZigBee зафиксируют освобождение комнаты в пределах заданного временного диапазона, push-сервер отправит уведомление приложению, и пользователь, который это уведомление получит, может зарезервировать комнату.

        Системная диаграмма работы с Android-приложением

        Пользовательский интерфейс Android-приложения


        Приложение для Android мы назвали «Smart Conference Room System». Пользуясь им, сотрудник организации может забронировать переговорную и управлять зарезервированными комнатами. Вот, как выглядит главный экран приложения.

        Главный экран приложения

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

        Используя команду My Scheduling, пользователи могут бронировать переговорные (синий цвет используется для указания на то, что комната свободна, серый – на то, что уже забронирована). Если комната не свободна, пользователь может включить режим ожидания её освобождения.


        Бронирование комнаты

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


        Управление расписанием

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


        Уведомление об освобождении комнаты

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

        Выводы


        Мы разработали интеллектуальную систему управления комнатами для переговоров, в основе которой – простой ZigBee-стек для Arduino. Система может определять, в режиме реального времени, свободна комната или занята. Это позволяет упростить бронирование помещений, сделать его удобнее, повысить эффективность использования переговорных комнат.

        Многое уже сделано, но мы, однако, всё ещё сталкиваемся с некоторыми сложностями. Это касается разных аспектов работы системы. Так, если говорить о её внутреннем устройстве, о стеке, на котором она основана, это ограничения функций API стека и всего комплекса. Кроме того, нужно улучшить стабильность работы системы и другие аспекты её функционирования. Мы планируем создание следующей версии Smart Conference Room System, которая будет более функциональной, стабильной и удобной, а значит позволит лучше решать её основную задачу: оптимизацию использования переговорных комнат в организациях.

        Комментарии (0)

          Let's block ads! (Why?)

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

          От автора: В статье приведены мои слайды и заметки с выступления от 25 июня 2016 года на конференции WordCamp Europe. Говорил я примерно то же самое.

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

          Программное обеспечение поедает мир


          Известна фраза от Марка Андреессена, создателя браузера Netscape: «программное обеспечение поедает мир». Я хочу уточнить, что мир поедает ПО с открытым кодом, и у меня есть парочка аргументов в пользу моей версии.

          Во-первых, результаты опроса 2015 года «будущее открытого ПО»: «78% анкетируемых сказали, что частично работают при поддержке ОПО, и 66% сказали, что создают ПО для клиентов на основе ОПО. Эти цифры почти удвоились с 2010 года».

          Во-вторых, Надья Эгбал [Nadia Eghbal], проводящая интересные исследования экономики ОПО, подсчитала, что «Доля ОПО в приобретённом за $1 миллиард Instagram составляла не менее $143 миллионов».

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

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

          Так что, ОПО – это очень круто. Но что это такое?

          Открытый код – это больше, чем просто лицензия


          Сейчас это понятие означает две вещи.

          Понятно, что есть официальное определение – лицензия, позволяющая некоторые вольности пользователю ПО. Но обычно сейчас это понятие используется для обозначения совместного и публичного создания ПО. Лицензия вообще мало кого интересует – у 80% проектов с Github лицензия отсутствует.

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

          В создании ОПО можно участвовать по-разному.

          Поддержка — это больше, чем вклад


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

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

          Мне стыдно за мой код


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

          Публикация проекта – отличный повод для обучения

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

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

          Меня бесит, что я не могу закончить релизы


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

          Релизы по календарю; отмечайте всё подряд


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

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

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

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


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

          Сортируйте, расставляйте приоритеты, принимайте решения


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

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

          Меня бесит, когда обсуждения задач превращаются в споры


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

          Будьте сочувствующим, твёрдым и уважайте других


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

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

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

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

          Важно отметить, что вам всё равно придётся отрастить толстую шкуру.

          Я слишком увлечён своей работой над ОПО


          При поддержке проекта ОПО вы можете почувствовать, что слишком увлеклись участием в нём. Люди просят вас сделать что-то, и вы соглашаетесь. Слишком много «yes» – и вот он, стресс.

          Добейтесь счастья и установите границы


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

          Важно отметить, что уровень вовлечённости приходится постоянно подстраивать.

          Я одинок


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

          Сконцентрируйтесь на руководстве и приглашайте других участников


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

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

          И к чему я это веду?

          WP-CLI поедает WordPress


          WP-CLI становится всё более включённым в разработку WordPress. Такое ощущение, что на каждом мероприятии WordCamp есть сессия, посвящённая WP-CLI. Командная строка позволяет делать больше, прикладывая меньше усилий. Вы также можете помочь WP-CLI поедать WordPress, добавляя и поддерживая новые команды.

          Вы также можете стать человеком, поддерживающим WP-CLI!


          У WordPress есть плагины, и у WP-CLI в будущем появятся пакеты команд. И ради него я пытаюсь решать проблемы, которые наблюдаются у WordPress с плагинами:

          В WordPress плагины имеют второстепенный статус, в отличие от включённого в ядро кода. Я хочу, чтобы пакеты WP-CLI имели такой же статус, как и команды.
          Слишком часто у плагинов WordPress автор один. Я хотел бы, чтобы у каждого из пакетов WP-CLI было два-три активных автора.

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

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

          Комментарии (0)

            Let's block ads! (Why?)

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

            Генерирование паролей на Super Castlevania IV и Rock n' Roll Racing

            Финтех: почему программистов здесь ждут

            Уже через несколько дней начнется летняя школа и хакатон по финтеху, который проводит Финуниверситет и Московский политех при поддержке акселератора Numa и НП РТС. Событие объединит программистов и финансистов. После двухнедельного обучения участники получат сертификаты о повышении квалификации и множество знакомств с экспертами, и, под конец, примут участие в хакатоне. Для его проведения VoxImplant засылает технического евангелиста компании Григория Петрова.

            О том, как будет проходить школа, куда движется финтех, и как разработчику принимать участие в школе (БЕСПЛАТНО), мы поговорили с организаторами школы Тимофеем Головиным и Александром Диденко.

            Почему вы выбрали именно такой формат – школа плюс хакатон?

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

            Александр: Программист – это человек с молотком. Для него все выглядит как гвоздь, и он готов забить блокчейн даже туда, где он совершенно не нужен. А финансист, вообще, про молоток ничего не знает. Во время школы мы будем объединять их в команды, чтобы они вместе смогли сделать чудо-продукт. Неплохо, если, например, финансисты возьмут на школе анализ данных на Python’e или мастер-класс в HP IDOL, а программисты узнают о кейсах в телематическом страховании и поведенческих финансах. Мы сделаем так, чтобы и финансисты, и программисты на школе нашли друг друга и взяли те знания, которых им действительно не хватает для проектов – у них будет возможность выбирать.

            Как помочь финансистам и программистам лучше понимать друг друга?

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

            Александр: Мы постараемся разбивать команды таким образом, чтобы участники школы смогли поработать как можно с большим количеством людей, такой speed dating на две недели. На мой взгляд, идея в стартапе – далеко не самое главное, в стартапах важнее химия между людьми. Поэтому к началу хакатона мы хотим, чтобы образовались команды, в которых участниками комфортно работать друг с другом. А наши друзья из brain-to-brain помогут с социальной химией, они в этом эксперты.

            Кто оставляет сейчас заявки на участие? С каким бэкграундом?

            Александр: Мы получили свыше 50 заявок, и можем легко принять ещё около пятнадцати. Хотелось бы, чтобы финансистов и программистов было поровну, но пока видим перевес в сторону первых процентов на двадцать. Правда, нужно отметить, что мода на машинное обучение не обошла стороной никого: среди финансистов, которые отправили заявки на школу, кто-то уже разрабатывал на Python, кто-то прошел школу анализа Яндекса или Центра математических финансов. Но мы готовы ещё взять сильных разработчиков. И для них возможно бесплатное участие и даже проживание, если они иногородние. Наш генеральный спонсор, Некоммерческое партнерство РТС, готов покрыть расходы.

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

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

            Александр: Совершенно верно. Красивые логотипы и картинки необязательны, должен быть просто вменяемый интерфейс. Продукт должен работать, там должно что-то происходить, какие-то вычисления, какое-то взаимодействие с сервером. И еще, «финансисты» – термин условный, под ними мы понимаем и продуктологов тоже. А когда говорим о «разработчиках», предполагаем, что они же и дизайнеры, на первом этапе. Плюс, у нас будет отличный мастер-класс по такому модному способу заменить фронт-энд, как чатботы, от Chatbots Community. Так что будет возможность обойтись вообще без дизайнера.

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

            Александр: Мы, конечно (смеется)! Финансовый университет, на самом деле, один из лидирующих вузов про технологии в мире финансов. Мы регулярно проводим лектории, приглашаем коллег из различных компаний: Qiwi, ВТБ-24, Лидер, 2can, – да каких угодно. Далее, Агентство стратегических инициатив, где работает группа FinNet – они проводят регулярно хакатоны, форсайты. Акселераторы ФРИИ и Numa. Кстати, они и АСИ стали нашими партнерами по Школе. Вообще, 2016 год стал годом финтеха. Мы задумали школу в апреле, и попали в яблочко: сейчас события про финтех полезли изо всех щелей.

            Тимофей: Кроме прочего, есть много ресурсов, начиная от специализированных, заканчивая TechCrunch. Есть междунарордные компании-лидеры в плане инноваций в финтехе: Citigroup, Deutche Bank, JPMorgan, EY. Они все часто публикуют исследования на эту тему. Но чтобы приземлять всё на российский рынок, нужно читать законодательство РФ.

            Александр: И, кстати, поэтому у нас на школе будут люди из Банка России – вот еще кто устанавливает тренды! Мы ожидаем Вадима Калухова, главу департамента финтеха, который расскажет слушателям о планах Банка России на блокчейн и том, какие проекты наших ребят точно «не взлетят» – просто потому что Банк России будет против. Нас интересует реалистичность – еще и потому, что на выходе проекты будут смотреть Numa и ФРИИ.

            В каких технологиях и разработках нуждается финансовая отрасль?

            Александр: Банки и все финансовые посредники сидят на огромном количестве данных о своих клиентах. И только немногие из банков смогли этим воспользоваться, поставить в центр своей бизнес-модели. Мы можем посмотреть на Тинькофф Кредитные Системы, который свою экономику строит вокруг больших данных. Если у вас хороший скоринг, вы можете совсем по-другому занимать деньги, по-другому их давать. А это же всего-навсего формула, модель. Это значит, что в разработку этой формулы нужно вкладывать деньги. Следующая истори-я интернет вещей. Это next big thing в финтехе. У нас будет очень модный кейс по телематическому страхованию, причем его будем решать с разных сторон, и как кейс, и как некоторую задачу по анализу данных, и даже на машинах прокатимся. Телематическое страхование на Западе уже лет 10 известно. Это когда вам в автомобиль встраивается коробочка, которая сообщает вашей страховой компании, какая у водителя модель вождения. Далее страховая динамически меняет размер взноса, исходя из того, как клиент водит. Если в шашечки играет на МКАД, то будет одна ставка, если спокойно ездит, значит, совсем другая. Тут выход на очень интересные задачи есть.

            Что может препятствовать развитию финтех-компаний?

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

            Тимофей: Боль на мой взгляд состоит в том, что при постоянном усложнении системы, мы становимся зависимы от большего числа факторов. В первую очередь, зависимы от инфраструктурных элементов. А если сервер упадет? Хранилась информация о человеке, сделках, произошел сбой – всё. Где сделки? Второй вопрос важный – безопасность, которая касается всего. Чтобы нельзя было влезть в систему и поменять. Сейчас это все стало более актуальным, учитывая последнюю историю с эфирами.

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

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

            Александр: Мне лично кажется, что сейчас есть незанятая ниша в пенсионных фондах. Нужны технологии, которые позволили бы обеспечивать достойную старость — что-то вроде private wealth, но для обычных людей, без тридцатиметровой яхты в Ницце. Идеальный маркетплейс. Не помочь людям заработать вот прямо сейчас, это древняя история, и призрачная к тому же. Но как сделать, чтобы у человека были деньги на старость – чтобы он свое качество жизни не снизил когда на пенсию пойдет, вот это актуально не только для России. Люди стареют по всему миру, и дальше будет только хуже. Финтех, связанный с их пенсионными накоплениями – это очень интересная история. Поэтому мы дадим поведенческие и классические количественные финансы, вооружим людей перед хакатоном. А дальше пусть они сами выберут, чем займутся.

            Тимофей Головин

            Открывал и руководил московским филиалом Dnevnik.ru (крупнейшая школьная система и система электронных журналов/дневников в РФ), руководил международным платёжным стартапом EasyPay-TravelDestination. Работал советником Председателя Правления банка по ИТ, участвовал в подготовке концепций инновационного развития нескольких банков. Опыт в стратегическом развитии ИТ-компаний, инвестиционном анализе, предпринимательской деятельности. Член рабочей группы FinNet при Агентстве Стратегических Инициатив. Директор по корпоративным сервисам в NUMA Moscow.

            Александр Диденко

            Декан факультета МЭО (Финансовый университет при Правительстве РФ). До того, как присоединиться к команде факультета, работал в брокерских компаниях и банках (Газпромбанк, Финам и др.), где торговал ценными бумагами и деривативами, был корпоративным блоггером и лектором, а также выполнял административные функции. Также Александр основал несколько стартапов, написал кандидатскую по экономике, пишет докторскую и воспитывает двух дочерей.

            Комментарии (0)

              Let's block ads! (Why?)

              Нейронные сети на Javascript

              Драм-машина на нейронной сети

              [Перевод] Как HTTP/2 сделает веб быстрее

              Пишем текстовую игру на Python/Ren'Py ч.2: мини-игры и подводные камни

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


              Прошлая статья закончилась на том, что игрок может читать текст, смотреть картинки и влиять на развитие сюжета. Если бы мы собирались портировать книги-игры Браславского или писать CYOA в духе Choice of Games – этого бы хватило. Но в игре про мореходство нужен хотя бы простенький интерфейс собственно мореходства. Собрать полноценный симулятор, как в Sunless Sea, в Ren'Py можно, но трудозатратно и в целом бессмысленно. К тому же мы не хотим сделать своё Sunless Sea с бурятками и буузами, поэтому ограничимся чем-то вроде глобальной карты в “Космических Рейнджерах”: десяток-другой активных точек, из каждой из которых можно отправиться в несколько соседних. Примерно вот так:

              image

              Displayables, экраны и слои

              Для самого интерфейса нам нужно просто вывести на экран кучу кнопок (по одной на каждую точку) и фоновый рисунок; для этого хватит одной Displayable типа Imagemap. Принципиально она не сильно отличается от своего аналога в HTML: это картинка, на которой определены активные зоны, каждая со своим действием. Однако работать с голой Displayable неудобно, да и с архитектурной точки зрения это некрасиво. Поэтому, прежде чем браться за код, стоит разобраться с иерархией элементов, которые Ren'Py выводит на дисплей.

              Самый нижний уровень – это Displayables, то есть виджеты, встроенные или созданные разработчиками конкретной игры. Каждый из них выполняет какую-то базовую функцию: показывает картинку, выводит текст, обеспечивает ввод и т.п. Displayables обычно группируются в экраны, которые обеспечивают уже более абстрактные взаимодействия: например, вывод текста и внутриигровые меню выполняются экраном nvl, который включает собственно текст, фон, рамку и кнопки меню. Одновременно можно показывать сколько угодно экранов: на одном, к примеру, может быть текст, на другом кнопки, регулирующие громкость музыки, а на третьем какие-нибудь летающие поверх всего этого снежинки. По мере необходимости можно показывать или убирать отдельные экраны с мини-играми, меню сохранений и всем остальным, что нужно для игры. И, наконец, существуют слои. Работать напрямую со слоями нужно нечасто, но знать об их существовании необходимо. Фоновый рисунок, например, не имеет отношения к системе экранов и выводится ниже них в слое master.

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

              screen map_screen():
                  tag map 
                  modal True 
                  zorder 2 
                  imagemap: 
                      auto 'images/1129map_%s.png' 
                      #  Main cities 
                      hotspot monet.hotspot action Travel(monet) 
                      hotspot tartari.hotspot action Travel(tartari) 
                      #  …
                      #  More of the same boilerplate
                      #  …
                  add 'images/1024ship.png': 
                      at shiptransform(old_coords, coords) 
                      anchor (0.5, 1.0) 
                      id 'ship'
              transform shiptransform(old_coords, coords): 
                  pos old_coords 
                  linear 0.5 pos coords 
              
              

              Сперва объявляется экран map_screen(). Тэг опциональный; он просто позволяет группировать экраны для команд типа “Убрать все экраны, связанные с перемещением”. Zorder – это “высота” экрана, по которой решается, какие элементы будут друг друга заслонять. Поскольку мы задали zorder=2, а у экрана nvl дефолтный zorder=1, во время путешествия игрок не будет видеть окна с текстом. Но самое главное – этот экран модальный, то есть никакие элементы ниже него не будут получать события ввода. Получается, что экран nvl с появлением карты блокируется, поэтому нам не нужно самим отслеживать game state и следить, чтобы клик по карте заодно не промотал текст.

              Сам Imagemap состоит из двух основных элементов: тэга auto и списка активных зон. Auto содержит ссылку на набор файлов, которые будут использоваться в качестве фона. Именно набора, а не единой картинки: отдельно лежит файл, в котором все кнопки нарисованы как неактивные, отдельно – в котором все они нажаты и так далее. А Ren'Py уже сам выбирает нужный фрагмент из каждого файла и составляет картинку на экране. И, наконец, активные зоны (hotspot). Они описываются кортежем из четырёх целых чисел (координаты и размер) и объектом действия. Кортеж мы не хардкодим, а используем атрибут объекта; в описании экрана вместо значения всегда можно вставить переменную или атрибут. Объект действия описывает, что произойдёт по нажатию кнопки и контролирует, должна ли быть кнопка активна в данный момент. Ren'Py предоставляет довольно много встроенных действий для рутинных задач типа переходов по сценарию или сохранения игры, но можно сделать и свой. Наконец, последним добавляется рисунок корабля и трансформация, благодаря которой он не просто выводится на экран, а ползёт из точки А в точку Б. Трансформации описываются отдельным языком ATL (Animation & Transformation Language).

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

              Играем в карты

              В предыдущей статье я обещал показать два способа делать интерфейсы в Ren'Py. Первый мы только что увидели: он довольно прост, но отнюдь не универсален. Я так и не понял, например, как на экранном языке описать перетягивание объектов мышью и обрабатывать коллизии. Или как верстать экраны, если количество элементов на них определяется в ходе игры. К счастью, Displayables – это полноценные виджеты: они могут включать в себя другие Displayables, ловить события ввода и при необходимости меняться. Следовательно, можно описать в одной Displayable всю мини-игру практически так же, как мы бы сделали для отдельного приложения, и вставить в проект.

              Мини-игра у нас будет карточная. Вся потенциально полезная собственность и познания главного героя представлены в виде колоды карт номиналом от 1 до 10 в четырёх мастях: Сила, Знания, Интриги и Деньги. Скажем, двойка силы – это бесценное знание о том, где у человека дых, под который надо бить, а восьмёрка интриг – достоверный список агентов Конторы среди контрабандистов. Несложная игра со взятками определяет, справился ли игрок со стоящей перед ним проблемой и какой мастью он выиграл или проиграл. В результате у конфликта может быть максимум восемь исходов: победа и поражение каждой мастью в принципе могут приводить к разным последствиям.

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

              class CardSmallDisplayable(renpy.Displayable): 
                      """ 
                      Regular card displayable 
                      """ 
              
                      suit_bg = {u'Деньги': 'images/MoneySmall{0}Card.jpg', 
                         u'Знания': 'images/KnowledgeSmall{0}Card.jpg', 
                         u'Интриги': 'images/IntrigueSmall{0}Card.jpg', 
                         u'Сила': 'images/ForceSmall{0}Card.jpg'} 
              
                      def __init__(self, card, **kwargs): 
                          super(CardSmallDisplayable, self).__init__(xysize=(100, 140), xfill=False, yfill=False, **kwargs) 
                          self.bg = Image(self.suit_bg[card.suit].format((card.spendable and 'Spendable' or 'Permanent'))) 
                          self.text = Text(u'{0}'.format(card.number), color = '#6A3819', font='Hangyaboly.ttf') 
                          self.xpos = 0 
                          self.ypos = 0 
                          self.xsize = 100 
                          self.ysize = 140 
                          self.x_offset = 0 
                          self.y_offset = 0 
                          self.transform = Transform(child=self) 
              
                      def render(self, width, height, st, at): 
                          """ 
                          Return 100*140 render for a card 
                          """ 
                          bg_render = renpy.render(self.bg, self.xsize-4, self.ysize-4, st, at) 
                          text_render = renpy.render(self.text, width, height, st, at) 
                          render = renpy.Render(width, height, st, at) 
                          render.blit(bg_render, (2, 2)) 
                          render.blit(text_render, (15-int(text_render.width/2), 3)) 
                          render.blit(text_render, (88-int(text_render.width/2), 117)) 
                          return render 
              
                      def visit(self): 
                          return[self.bg, 
                                 self.text] 
              
              

              Это, по сути, самая простая возможная Displayable. Она состоит из двух других: фоновой картинки, выбираемой в зависимости от масти, и текста с номиналом. Оба метода (не считая конструктора) необходимы для работы Displayable: self.render возвращает текстуру, а self.visit –
              список всех Displayables, входящих в данную. И она действительно рисует маленькую карту; вот несколько таких карт на экране колоды:

              image

              Уже неплохо, но карта сама по себе умеет только находиться на экране, и то только если её кто-нибудь туда поставит. Чтобы в мини-игру можно было, собственно, играть, нужно добавить внешнюю Displayable, способную обрабатывать ввод и обсчитывать игровую логику. Карты и прочие элементы интерфейса будут входить в неё так же, как текстовые поля и фон являются частями карты. Отличаться эта Displayable будет наличием метода self.event(), который получает на вход события PyGame. ССЫЛКА НА PYGAME.EVENT Примерно вот так (полный код доступен на гитхабе в классе Table):

              def event(self, ev, x, y, st): 
                      if ev.type == pygame.MOUSEBUTTONDOWN and ev.button == 1: 
                          #  …
                          #  Process click at x, y
                          #  …
                      elif ev.type == pygame.MOUSEMOTION and self.dragged is not None:
                          #  …
                          #  Process dragging card to x, y
                          #  …
                      renpy.restart_interaction()
              
              

              Об очереди событий можно не беспокоиться: движок сам раздаст все события всем элементам, которые активны в данный момент. Методу остаётся только проверить, интересует ли его произошедшее, ответить на событие и завершить взаимодействие. Взаимодействие в Ren'Py приблизительно эквивалентно тику игровой логики в других движках, но не ограничено по времени. В общем случае это одна команда игрока и ответ игры на неё, хотя иногда (например, при промотке текста) взаимодействия могут завершаться сами собой.
              Эту Displayable, как и все остальные, мы завернём в экран:

              screen conflict_table_screen():
                  modal True
                  zorder 9
                  add conflict_table
              
              

              conflict_table в данном случае не имя класса, а глобальная переменная, в которой хранится соответствующая Displayable. Выше упоминалось, что код экрана в принципе может выполняться в любое время, но перед показом он выполнится непременно, иначе игра не будет знать, что ей, собственно, выводить. Поэтому вполне безопасно непосредственно перед мини-игрой выполнить что-то вроде conflict_table.set_decks(player_deck, opponent_deck) и полагаться на то, что перед игроком окажется ровно то, что нужно. Аналогичным образом по завершении мини-игры можно обратиться к результатам, которые хранятся в том же объекте.

              Надо сказать, что использование глобальных переменных – это не ограничение Ren'Py, а наше собственное решение. Поддерживаются экраны и Displayables, которые способны принимать аргументы и возвращать значения, но с ними несколько сложнее. Во-первых, поведение таких экранов слабо задокументировано. По крайней мере, разобраться, в какой именно момент экран начинает своё первое взаимодействие и когда именно возвращает контроль сценарию, довольно сложно. А это очень важный вопрос, так как не зная ответа на него, сложно гарантировать, что весь предшествующий конфликту текст будет показан до начала конфликта, а следующий за конфликтом текст показан не будет. Во-вторых, с использованием глобальных переменных большинство нужных для мини-игры объектов инициализируется только один раз, а потом мы меняем их атрибуты при каждом следующем запуске. В противном случае пришлось бы с каждым конфликтом тратить время на подгрузку всех необходимых файлов; игра ощутимо лагает, если к HDD параллельно обращаются ещё, например, торрент и антивирус. Наконец, к картам обращается не только экран конфликта, но и несколько других экранов, поэтому логично использовать одни и те же Displayables везде, где они нужны.

              Послесловие

              На этом собственно программная часть разработки заканчивается и начинается наполнение игры контентом. Литературой в игре занимаюсь в основном не я, поэтому о структуре повествования и стилистике рассуждать не стану. На эту тему могу посоветовать почитать, например, классическую статью о структуре CYOA. Или неплохое руководство по написанию убедительных независимых NPC от сценариста 80 days.
              Ещё больше ссылок (а также обзоров на свежие англоязычные работы и статей на смежные темы) можно найти в блоге Эмили Шорт.

              Комментарии (0)

                Let's block ads! (Why?)

                [Из песочницы] Что такое AXON

                четверг, 30 июня 2016 г.

                Root — робот который помогает детям учить программирование

                Windocks — SQL Server и .NET контейнеры на Windows Server 2012 R2 с поддержкой docker api

                Здравствуйте, уважаемые читатели хабра. Сегодня мы поговорим о Windocks–продукте компании с одноименным названием, позиционирующем себя как первый движок докер для Windows. Пока клиенты майкрософт ожидают новую версию Windows Server, а именно Windows Server 2016 (на момент написания статьи доступна версия Technical Preview 5) с нативной поддержкой докер-контейнеров, создатели windocks попытались предоставить докер-контейнеры для текущего поколения серверных систем windows.

                Итак, что же такое Windocks? Если коротко — это смесь портированной версии существующего демона докер и open source container project от Uhuru Software. Об особенностях Windocks и вызовах, которые стояли перед разработчиками, можно почитать тут.

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

                Что доступно сейчас


                На момент написания windocks поддерживает следующие ОС:
                • Windows 8/8.1
                • Windows 10
                • Windows Server 2012 / Windows Server 2012 R2
                • Windows Server 2016

                В этих ОС можно создавать контейнеры из образов:

                • SQL Server 2016/2014/2012/2008 R2/2008
                • Net 4.5/3.5/2.0
                • Jenkins (агенты, демо по запросу)
                • Office conversion (для конвертации презентаций powerpoint в видео, демо по запросу)
                • Microsoft Dynamics (линейка ERP и CRM решений, демо по запросу)

                Развертываем windocks на VPS с Windows Server 2012


                Совсем недавно Windocks объявили о релизе community edition, для того чтобы можно было бесплатно ознакомиться с возможностями продукта. Эту версию мы и будем разворачивать. Отправляем запрос через форму на сайте. Ответ приходит мгновенно, и через минуту ссылка уже у нас.

                В качестве окружения для развертывания windocks будем использовать VPS с конфигурацией 2x2.6ГГц, 2Гб RAM, 20Гб HDD / Windows Server 2012 R2. Для того, чтобы использовать SQL Server контейнеры на VPS необходимо установить SQL Server. Устанавливаем, а затем ставим и сам windocks. Никаких дополнительных настроек в процессе установки не требуется.

                Сгенерируем сертификаты и ключи для хоста и для клиента, чтобы настроить двустороннюю аутентификацию и предоставим их windocks:

                server-key.pem -> C:\Users\Administrator\.docker\key.pem
                server-cert.pem -> C:\Users\Administrator\.docker\cert.pem
                ca.pem -> C:\Users\Administrator\.docker\ca.pem
                

                Теперь запустим windocks (команда запуска выглядит немного иначе, чем для демона докер на линукс):

                docker -H http://tcpWINDOCKS_IP:2376 -dD --tlsverify=true
                

                В логах запуска видим:

                time="2016-06-30T14:32:06.901345300+03:00" level=info msg="Daemon has completed initialization"
                

                Не забываем разрешить входящие подключения на tcp порт 2376 в файрволе для нужных ip-адресов.

                Подключаемся с помощью стандартного клиента докер


                Как вы наверное догадались по заголовку, Windocks поддерживает docker RESTful api. Это означает, что для работы с ним можно использовать стандартного консольного клиента докер. Однако, windocks на данный момент поддерживает не все команды, а только их часть. В DockerFile также поддерживается ограниченный набор комманд (FROM, ADD, COPY, ADDDB, MOUNTDB). Полный список комманд для сравнения вы можете найти тут.

                Подключаемся к windocks абсолютно также, как и к обычному докер хосту (для работы с клиентом рекомендуем создать алиасы команд, в windows это можно сделать, например, с помощью DOSKEY):

                docker.exe --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=WINDOCKS_IP:2376 version
                

                В результате получаем что-то вроде:

                
                Client version: 1.7.0
                Client API version: 1.19
                Go version (client): go1.4.2
                Git commit (client): 0baf609
                OS/Arch (client): windows/amd64
                Server version: $VERSION
                Server API version: 1.19
                Go version (server): go1.5.1
                Git commit (server): $GITCOMMIT
                OS/Arch (server): windows/amd64
                
                

                Запускаем .NET приложение в windocks


                У windocks свой локальный реестр образов, который в community addition по умолчанию содержит 2 образа: dotnet и mssql. Его содержимое можно получить, выполнив (считаем, что мы уже сделали алиас с параметрами для аутентификации с помощью сертификатов):
                docker images
                

                Результат:

                Итак, нам доступны 2 базовых образа: dotnet-4.5 и mssql-2014. Вместе с Windocks идёт набор сэмплов, демонстрирующих его возможности.

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

                Соберем контейнер из сэмпла testMountDb и запустим его.

                docker build testMountDb
                

                Получаем следующее:

                
                Sending build context to Docker daemon 2.048 kB
                        Sending build context to Docker daemon
                        Step 0 : FROM mssql-2014
                        Step 1 : MOUNTDB customers C:\dcr\mountDbExample\customerdata.mdf
                        ContainerId = c4d6e4f7bdc9cfc4c8b62b09572b255f43b56716d862b28e291e7af86e08bc3f & ContainerPort = 10001 & ContainerUserName = prison_oo_aqRdapH & ContainerPassword = Pr!5n5A39SvNRo & MSSQLPort = 10001 & MSSQLServerUserName = sa & MSSQLServer
                        SaPassword = Pr!54q0flmqoM1 & DockerFile output:  & Line 2- Mounted DB: customers, C:\ dcr \mountDbExample\customerdata.mdf
                
                

                Отсюда нам понадобятся MSSQLServerUserName и SaPassword, которые мы будем использовать в web.cfg в строке подключения.
                Запустим созданный контейнер:

                docker start c4d
                

                Теперь соберем контейнер из сэмпла testDotnet и запустим (всё аналогично):

                docker build testDotnet
                

                Результат:

                
                SECURITY WARNING: You are building a Docker image from Windows against a Linux Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.
                        Sending build context to Docker daemon 20.99 kB
                        Sending build context to Docker daemon
                        Step 0 : FROM dotnet-4.5
                        Step 1 : COPY web.config app-root\\runtime\\repo
                        Step 2 : COPY default.aspx app-root\\runtime\\repo
                        Step 3 : COPY default.aspx.cs app-root\\runtime\\repo
                        Step 4 : COPY main.css app-root\\runtime\\repo
                        Step 5 : COPY windockslogo.png app-root\\runtime\\repo
                        Step 6 : COPY testrun.bat .
                        Step 7 : RUN cmd.exe /C .\testrun.bat
                        ContainerId = 3b8cefeef04d3ac5010fa8e37b04ce42aa207359e4848581222d521839ea7a04 & ContainerPort = 10003 & ContainerUserName = prison_oo_P4yo5gA & ContainerPassword = Pr!5c6g8mVfQq7 & DOTNETPort = 10003 & DockerFile output: PS
                
                        docker start 3b8
                
                
                

                Открываем в браузере

                http://WINDOCKS_IP:10003 
                
                и, если все сделано правильно, видим следующее:

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

                docker commit a29 windocks_dotnet_demo_v1
                

                И выведем обновлённый список образов:

                docker images
                

                Теперь образ windocks_dotnet_demo_v1 могут использовать и другие пользователи

                Выводы


                На наш взгляд, несмотря на то, что windocks достаточно ограниченный (в чем его ограничения мы указали в самом начале статьи) инструмент в сравнении с демоном докер для линукс, он достаточно хорошо справляется со своими стандартными задачами по предоставлению SQL Server и .NET контейнеров. Также его можно вполне комфортно использовать для разработки и тестирования приложений на ASP.NET 4.5 / MSSQL в небольших командах, однако для использования в продакшене инструмент ещё слишком молод и ещё может стать неактуальным после релиза Windows Server 2016. А вы как считаете, уважаемые читатели?

                Комментарии (0)

                  Let's block ads! (Why?)

                  [Перевод] Intel Edison, Windows Phone и Microsoft Azure: облачный союз пульта и светодиода

                  Возможности современных технологий ярче всего проявляются при их совместном использовании. Например, что можно сделать, если объединить Intel Edison, Microsoft Azure и Windows Phone? На самом деле, много всего. Здесь мы рассмотрим один из вариантов – дистанционное включение и выключение светодиода, присоединённого к одному из цифровых выходов Edison с коммутационной платой Arduino.



                  Мобильный сервис Azure связывает Edison и «пульт управления», в роли которого выступает приложение, работающее на платформе Windows Phone. Edison читает значение атрибута из таблицы мобильного сервиса и выполняет некие действия в соответствии с полученными данными. Приложение для смартфона меняет значение в таблице, что позволяет ему воздействовать на плату.

                  Предварительные требования


                  Мы предполагаем, что читатель обладает базовыми навыками разработки приложений для платформы Windows Phone и способен связать такое приложение с мобильной службой Azure. Если вы не знаете, как создавать серверную часть приложения, взгляните на этот материал.

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

                  • Плата Intel Edison с коммутационной платой Arduino
                  • Светодиод
                  • Visual Studio
                  • Arduino IDE
                  • Мобильный сервис Azure

                  Сначала создадим мобильный сервис Azure. Тому, как это делается, посвящено множество публикаций. Например, можете воспользоваться этим руководством и создать мобильный сервис, который содержит одну таблицу. Эту таблицу назовите «controltable». В ней должно быть два столбца. Один – с именем «status», второй – «name». Значение «status» будет либо 1, либо 0. В «name» будет «Edison/Arduino».


                  Мобильный сервис Azure

                  Обратите внимание на то, что атрибут «device» в SQL-таблице Azure значения не имеет. Его, если хотите, вполне можете удалить.
                  Теперь займёмся разработкой приложения для Windows Phone, которое будет добавлять или модифицировать значение атрибута.

                  Приложение для Windows Phone


                  Здесь мы не вдаёмся в подробности разработки для Windows Phone. Если вам нужны дополнительные сведения об этом, взгляните на материалы, размещённые на этом ресурсе.

                  В нашем приложении будет всего две кнопки: «on» и «off». Вот, как всё это выглядит.


                  Экран приложения для Windows Phone

                  На кнопку-ссылку «Log out» можете внимания не обращать.

                  Итак, как же работает приложение? При нажатии на кнопки «on» и «off» меняется атрибут «status» в таблице Azure, он устанавливается либо в 1, либо в 0.

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

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

                  Разработка для Edison


                  Займёмся разработкой для Edison. Прежде чем приступить, нужно настроить плату. После подключения Edison к компьютеру, выясните номер COM-порта, который соответствует плате. Затем запустите IDE Arduino и, из списка поддерживаемых устройств, выберите Edison. Если нужную плату в списке найти не удаётся, нужно загрузить необходимые файлы с помощью инструмента Boards Manager.

                  Загрузка файлов для Edison в IDE Arduino

                  После того, как вы откроете IDE Arduino, к вашим услугам будет пара функций – это void setup() и void loop(). Edison оснащён встроенным Wi-Fi-адаптером. Мы будем использовать его для подключения устройства к интернету. Поэтому первым делом импортируем в проект библиотеку для поддержки Wi-Fi. Для этого нужно выполнить команду Sketch->include library->Wi-Fi. Теперь добавим в проект нижеприведённый код, который позволит подключить устройство к Wi-Fi-сети.

                  #include <SPI.h>
                  #include <WiFi.h>
                  WiFiClient client;
                  
                  char ssid[] = "networkssid"; //  SSID (имя) сети 
                  char pass[] = "password";    // пароль для подключения к сети (для сети с поддержкой WPA), или ключ (для сети с поддержкой WEP)
                  int keyIndex = 0;            // индекс ключа сети (нужен только для WEP)
                  int status = WL_IDLE_STATUS;
                  
                  void setup()
                  {
                    pinMode(13,OUTPUT);
                    digitalWrite(13,HIGH);
                    Serial.begin(115200);
                    while (!Serial) {
                      ; // ожидаем подключение по последовательному порту. Нужно только для Leonardo
                    }
                    
                    // Проверка наличия платы расширения:
                    if (WiFi.status() == WL_NO_SHIELD) {
                      Serial.println("WiFi shield not present"); 
                      // не будем продолжать:
                      while(true);
                    } 
                  
                    String fv = WiFi.firmwareVersion();
                    if( fv != "1.1.0" )
                      Serial.println("Please upgrade the firmware");
                    
                    // пытаемся подключиться к Wi-Fi-сети:
                    while (status != WL_CONNECTED) { 
                      Serial.print("Attempting to connect to SSID: ");
                      Serial.println(ssid);
                      // Подключение к сети WPA/WPA2. Измените эту строчку, если используете открытую сеть или сеть с поддержкой WEP:    
                      status = WiFi.begin(ssid, pass);  
                    }
                  }
                  

                  Этот фрагмент кода позволяет подключить Edison к интернету по Wi-Fi. Кроме того, мы установили PIN 13 в режим вывода и перевели в отключенное состояние. Мы будем использовать последовательный порт для мониторинга процесса в IDE Arduino.
                  Теперь займёмся методом void loop(). Получение данных из Azure выполняется с помощью метода GET.
                  void loop()
                  {
                    send_request();
                    wait_response();
                    read_response();
                    end_request();
                    delay(100);
                  }
                  

                  Эти функции будут загружать данные из таблицы облачного сервиса. Но, прежде чем рассмотреть код функций, нам нужно добавить в проект несколько глобальных констант, которые будут хранить данные, необходимые для подключения Edison к Azure.
                  const char* server= "YourAzureService.azure-mobile.net";
                  const char* table_name= "TableName";
                  const char* ams_key="YourApplicationKey";
                  char buffer[64];
                  

                  Ключ приложения (ams_key) можно найти в разделе управления ключами в вашей учётной записи Azure. Теперь рассмотрим код методов, используемых в void loop().
                  void send_request()
                  {
                    Serial.println("\nconnecting...");
                  
                    if (client.connect(server, 80))
                   {
                  
                      sprintf(buffer, "GET /tables/%s HTTP/1.1", table_name);
                      client.println(buffer);
                  
                      // Заголовок хоста
                      sprintf(buffer, "Host: %s", server);
                      client.println(buffer);
                  
                      // Ключ приложения Azure Mobile Services
                      sprintf(buffer, "X-ZUMO-APPLICATION: %s", ams_key);
                      client.println(buffer);
                  
                      // Тип содержимого – JSON
                      client.println("Content-Type: application/json");
                  
                      // Размер содержимого
                      client.print("Content-Length: ");
                      client.println(strlen(buffer));
                  
                      // Окончание заголовка
                      client.println();
                  
                      // Тело запроса
                      client.println(buffer);
                      
                    } 
                  else 
                  {
                      Serial.println("connection failed");
                    }
                  }
                  

                  Мы выполнили HTTP-запрос и вызвали GET, где сначала указали имя таблицы, имя сервера и код ключа. Это нужно для успешного получения данных из облака. Затем мы указали, в каком формате хотим получить данные, в нашем случае – это JSON. Теперь займёмся кодом функции wait_response().
                  void wait_response()
                  {
                    while (!client.available()) 
                    {
                      if (!client.connected()) 
                      {
                        return;
                      }
                    }
                  }
                  

                  После того, как данные получены, их надо прочитать. Так как мы имеем дело с форматом JSON, нужно разобрать полученную строку для того, чтобы извлечь из неё то, что нам нужно. Вот как такая строка может выглядеть:
                  {"id":"2492D996-C471-48F0-B3C9-F33E3B37477F","status":"0","name":"arduino"}
                  

                  Для разбора JSON существует весьма эффективная библиотека, ArduinoJson. Она выполняет основную часть работ. Но полученная строка с JSON-данными заключена в прямоугольные скобки «[]». Прежде чем передавать строку библиотеке, эти скобки нужно удалить. Итак, сначала включим библиотеку в наш код и добавим следующие глобальные переменные:
                  #include <ArduinoJson.h>
                  #define RESPONSE_JSON_DATA_LINENNO 10
                  int charIndex=0;
                  StaticJsonBuffer<200> jsonbuffer;
                  

                  Теперь, в методе read_response(), напишем следующее:
                  void read_response()
                  {
                    boolean bodyStarted;
                    int jsonStringLength;
                    int jsonBufferCntr=0;
                    int numline=RESPONSE_JSON_DATA_LINENNO;
                    //Во всём ответе нам интересна лишь десятая строка
                    while (client.available()) 
                    {
                      //Serial.println("Reading:");
                      char c = client.read();  
                      if (c == '\n')
                      {
                        numline -=1;
                      }
                      else 
                      {
                        if (numline == 0 && (c!='[')&& (c!=']') )
                        {
                          buffer[jsonBufferCntr++] = c; 
                          buffer[jsonBufferCntr] = '\0'; 
                        }
                      }
                    }
                    Serial.println("Received:");
                    Serial.println(buffer);
                    Serial.println("");
                    parse();
                  }
                  

                  Вышеприведённый код выполняет чтение ответа. Метод parse ответственен за разбор строки. Вот код этого метода. Здесь мы изменяем состояние вывода PIN 13 на плате в соответствии с полученными данными.
                  void parse()
                  {
                    StaticJsonBuffer<150> jsonbuffer;
                    JsonObject& root = jsonbuffer.parseObject(buffer);
                    if(!root.success())
                    {
                      Serial.println("PARSING FAILED!!!");
                      return;
                    }
                    int f= root["status"];
                    Serial.println("Decoded: ");
                    Serial.println(f);
                    if(f==0)
                      digitalWrite(13,LOW);
                    else
                      digitalWrite(13,HIGH);
                  }
                  

                  В этом коде переменная f хранит значение атрибута status. Мы это значение проверяем и, в зависимости от того, 0 или 1 окажется в переменной, переводим выход на плате в состояние HIGH или LOW. Нам осталось рассмотреть код лишь одного метода, end_request(). Вот он.
                  void end_request()
                  {
                    client.stop();
                  }
                  

                  Вполне вероятно то, что после компиляции этой программы вы столкнётесь с неожиданными ошибками. С ними надо разобраться. Причина ошибок описана здесь. Дело в том, что в файле WString.h отсутствует некоторый необходимый код. Здесь можно оценить различия правильного и неправильного файлов. Рабочий файл нужно поместить по такому пути:
                  C:\Users\Username\AppData\Roaming\Arduino15\packages\Intel\hardware\i686\1.6.2+1.0\cores\arduino\WString.h
                  

                  После этого ошибки исчезнут, можно будет добиться правильной компиляции кода и прошить его на Edison. Теперь подключите к плате светодиод, присоединив более длинную ножку (сюда так же можно добавить резистор на 233 Ом) к PIN 13, а короткую – к GND. Теперь система, состоящая из платы Intel Edison, облачной службы Microsoft Azure и Windows Phone, готова к испытанию.

                  Напомним: мы собираемся управлять светодиодом, подключённым к Edison, с помощью устройства на Windows Phone, пользуясь для этого облачным сервисом Azure, который является связующим звеном проекта. Взгляните на окно с отладочной информацией, которая поступает от Edison. Первая стадия работы – попытка подключения к Wi-Fi-сети.


                  Edison подключается к Wi-Fi сети

                  Следующий шаг – подключение к мобильному сервису Azure. После подключения Edison загружает данные в формате JSON, затем осуществляется их разбор с использованием соответствующей библиотеки, из данных извлекается интересующий нас показатель. Светодиод, подключённый к PIN 13, включается или выключается в соответствии со значением этого показателя.


                  Intel Edison подключился к облаку и получил данные

                  Итоги


                  Из этого материала вы узнали о том, как организовать управление платой Intel Edison с использованием мобильных сервисов Microsoft Azure. В нашем случае мобильный сервис – это связующее звено. Он позволяет взаимодействовать приложению, запущенному на Windows Phone, и плате Intel Edison. Делается это благодаря тому, что приложение, работающее на смартфоне, записывает данные в таблицу, а Edison эти данные считывает и соответствующим образом реагирует на них.

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

                  Комментарии (0)

                    Let's block ads! (Why?)