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

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

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

 

 -Статистика

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

Habrahabr/New








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

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

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

День из жизни технической поддержки

Среда, 31 Мая 2017 г. 22:03 + в цитатник
Хорошо ли работать в техподдержке? Ну это зависит от того, что нужно поддерживать! Сегодня мы расскажем о том, какие задачи приходится решать саппортерам в Virtuozzo, а они поделятся своими секретами – почему пришли работать именно на эти должности.

image



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

image

Ведущие специалисты техподдержки Virtuozzo Мария Антонова и Анна Винокурова

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

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

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

Однако есть и предсказуемые волны нагрузки, они как правило связаны с релизами новых продуктов, выпуском мажорных апдейтов или же с обнаружением очередной уязвимости Linux.
— Когда в интернете публикуется очередной бюллетень безопасности (security advisory), в саппорт начинают приходить взволнованные клиенты, которые хотят узнать какие версии затронуты уязвимостью, и как скоро мы выпустим апдейт, — отмечает Анна Винокурова.

— Мне очень нравится работать в технической поддержке, потому что эта работа сочетает в себе задачи эксперта в сфере программного обеспечения, программиста и детектива. Иногда проблема оказывается настолько замаскирована, что ее решение — совершенно неочевидно. И каждый раз, когда удается разгадать загадку, «почему ничего не работает», приходит чувство глубокого удовлетворения и даже торжества! – говорит Мария Антонова.

Чем же занимается саппорт Virtuozzo?

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

1. Память не добавляется – ошибка CPU
Однажды ночью клиент сообщает в поддержку об ошибке на попытке увеличить RAM для одной из виртуальных машин. Однако ошибка почему-то «ругается» на CPU, а не RAM.
Сначала такая ситуация всех очень удивила, но через некоторое время мы разобрались в том, что возвращаемая ошибка не имела прямого отношения к выполняемому действию – она лишь обнажала сбой, крывшийся намного глубже внутри системы.

Причина оказалась весьма серьезной: на физическом сервере «выбило» сокет CPU, и он перестал определяться, а вместе с ним все виртуальные ядра в пределах сокета ушли в offline. Естественно, при изменении лимитов памяти для виртуальной машины валидация будущей конфигурации не проходила просто в целом, ведь количество ядер стало превышать количество доступных ядер на узле сервера.

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

Решение

На клиентском сервере было внесено исправление в python-составляющую продукта, которое позволило продолжать корректную работу, несмотря на существование узлов NUMA, состоящих целиком из offline-ядер. После этого мы смогли выставить количество CPU в пределах реальных “выживших” ядер для виртуальной машины, а значит – стали возможными дальнейшие изменения конфигурации.

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

«На самом деле клиенту очень повезло, что его сервер оставался доступен достаточно длительное время, несмотря на такой серьезный аппаратный сбой. Это позволило мигрировать виртуальные машины на другие хосты, пока они всё ещё были доступны», — отметила Мария Антонова.

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

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

Для исследования проблемы необходимо было получить виртуальную машину в момент воспроизведения проблемы, но сервисы внутри виртуальных машин должны были работать без перебоев, и наши исследования в режиме «онлайн» вызвали бы проблемы для большого количества конечных пользователей. Поэтому клиент предоставил нам копию виртуальной машины, на которой мы смогли воспроизвести проблему и исследовать её в нашей среде без неудобств для пользователей. Баг нашелся в механизме выделения памяти для сокет-буфера виртуального устройства virtio-net – часть модуля ядра.

Решение

Увы, быстрого решения проблемы, без изменения модулей ядра просто не было, оказалось, что клиент просто не обновился до последней версии Virtuozzo Linux. На момент обращения уже было выпущено обновление, причем в формате ReadyKernel. Это позволило применить обновление и решить проблему за несколько минут, даже не перезагружая сервисы.

3. Пользователь удалил все контейнеры

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

Решение

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

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

Физически файлы виртуальных дисков были перенесены во временную директорию для удаления файлов, и остановка такого контейнера привела бы к конечному удалению данных.
Так как метаданные варьируются от контейнера к контейнеру, их необходимо создавать с нуля индивидуально. Конфигурационные файлы восстанавливались на основании биллинговой информации и тарифных планов конечных пользователей — это клиент делал сам, так как у нашей службы поддержки нет доступа к биллинговой информации клиента. Но мы, в свою очередь, предоставили что-то вроде «скелета» конфигурации, в который нужно было внести IP, имя хоста, лимиты ресурсов. Для восстановления метаданных диска, саппортеры Virtuozzo создали скрипт, который генерировал корректные метаданные на основе информации о смонтированных контейнерах из ядра, а также вычислял виртуальные параметры дисков, такие как количество цилиндров, головок и секторов.

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

И детектив, и лингвист, и программист

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

Никто ещё не голосовал. Воздержавшихся нет.

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

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

https://habrahabr.ru/post/329950/


Метки:  

[recovery mode] Нерекурсивный алгоритм генерации всех разбиений целого числа

Среда, 31 Мая 2017 г. 21:35 + в цитатник
Привет, Хабр! Вдруг снова захотелось написать сюда! Так как было время на выходных, я снова решил поиграться с алгоритмизацией и написал вот эту статейку. Хотел даже отправить в какой-нибудь рецензируемый журнал, но оценить тривиальность/нетривиальность данного материала я не в состоянии, так программирование всего лишь мое хобби и то эпизодическое, поэтому, как обычно, заранее прошу прощения, если все окажется слишком простым и до боли знакомым.
Спасибо администрации Хабра за отзывчивость и молниеносную оперативность при восстановлении аккаунта!

Итак, плоды усилий долгих...

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

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

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

Описание алгоритма
Дано: исходный массив в виде единиц — А (1,1,1,1).
Шаги
1) Двигаясь по массиву слева направо, искать в массиве А минимальный элемент — x,
последний элемент не учитывается.
2) Перенести единицу из конца (последнего элемента) в найденный минимальный элемент x
(равносильно увеличению x на единицу и уменьшению на единицу последнего элемента).
3) Если в массиве А есть ноль — 0, то удалить последний элемент.
4) Разложить сумму всех элементов после измененного элемента — x – на единицы.
Пример
А=(1,1,1,1,1)
2,1,1,1
2,2,1
3,1,1

Код алгоритма на PHP




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

Литература
[1] Donald E. Knuth. The Art of Programming. Vol. 4. 2008.
[2] Dennis Ritchie and Brian Kernighan. The C Programming Language. 1978.
[3] Aleksandr Shen. Algorithms and Programming: Problems and Solutions.
[4] ru.wikipedia.org/wiki/Разбиение_числа
При чтении описания данного алгоритма получается ли у Вас вывести его на листке бумаги?

Никто ещё не голосовал. Воздержался 1 человек.

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

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

https://habrahabr.ru/post/329948/


Метки:  

Странности Generic типов Java

Среда, 31 Мая 2017 г. 21:34 + в цитатник

Я множество раз слышал о том, что дизайн Generic типов в Java является неудачным. По большей части претензии сводятся к отсутствию поддержки примитивных типов (которую планируют добавить) и к стиранию типов, а конкретнее — невозможности получить фактический тип параметра в рантайме. Лично я не считаю стирание типов проблемой, как и дизайн Generic-ов плохим. Но есть моменты, которые меня порядком раздражают, но при этом не так часто упоминаются.


1


Например, мы знаем, что метод Class#getAnnotation параметризован и имеет следующую сигнатуру: public A getAnnotation(Class annotationClass). Значит, можно писать вот такой код:


Deprecated d = Object.class.getAnnotation(Deprecated.class);

Тут я решаю вынести Object.class в отдельную переменную и код перестаёт компилироваться:


Class clazz = Object.class;
// incompatible types:
// java.lang.annotation.Annotation cannot be converted to java.lang.Deprecated
Deprecated d = clazz.getAnnotation(Deprecated.class);

Где я ошибся?


Ошибся я в том, что не параметризовал тип переменной clazz.
Получается, что стирание у типа Class так же стирает типы во всех его методах! Зачем так было делать — понятия не имею. Вносим минимальное исправление в код и всё работает как надо.


Class clazz = Object.class;
Deprecated d = clazz.getAnnotation(Deprecated.class);

2


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


class Ref {
    private T value = null;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

Имея переменную ref я решу написать такой код. Что с ним может произойти плохого?


ref.setValue(ref.getValue());

Разумно было бы считать, что он всегда скомпилируется, но это не так! Вам всего лишь стоит объявить переменную ref с типом Ref h = new HasArrayList<>(new ArrayList<>()); ArrayList list = h.getList();

Параметр T класса HasArrayList имеет верхнюю границу равную ArrayList, а значит при стирании типов код всё ещё должен компилироваться.


HasArrayList h = new HasArrayList<>(new ArrayList<>());
// incompatible types: java.util.List cannot be converted to java.util.ArrayList
ArrayList list = h.getList();

Ну вот, опять не работает. Сейчас то что не так?


Не так то, что в сигнатуре метода getList возвращаемым типом является List, а компилятору просто лень расставлять явные приведения типов. Исправляется всё очень просто — надо переопределить данный метод в подклассе.


class HasArrayList extends HasList {
    public HasArrayList(T list) {
        super(list);
    }

    @Override
    public T getList() {
        return super.getList();
    }
}

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


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

https://habrahabr.ru/post/329550/


Метки:  

Обзор изменений в новом мажорном релизе Node 8

Среда, 31 Мая 2017 г. 20:37 + в цитатник

30 мая 2017 года в 23:00 по московскому времени вышел новый долгожданный мажорный релиз Node.js 8.0.0. Именно эта линейка версий за номером 8 в октябре 2017 перейдет в Long Term Support — цикл длительной поддержки. Предлагаю вашему вниманию небольшой обзор того огромного количества изменений и дополнений, вошедших в этот релиз.


  • npm 5 (lock-файл!)
  • V8 5.8
  • Node.js API (N-API)
  • async_hooks
  • WHATWG URL парсер
  • повышение безопасности буфера
  • упрощенная промисификация
  • статичные коды ошибок
  • и многое другое!


Новая мажорная версия ознаменует собой начало новой ветки с циклом длительной поддержки. Уточню, что произойдет это в октябре 2017, вместе с присвоением кодового названия Carbon, и эта ветка будет поддерживаться до 31 декабря 2019 года. Также Node.js 6 перейдет в режим поддержки в апреле 2018 и окончит свою жизнь в апреле 2019 года.



npm 5.0.0


Новая версия Node поставляется в комплекте с новой версией менеджера пакетов npm. Очевидно, что значительное влияние на разработчиков npm оказал неожиданный конкурент: Yarn от Facebook'а.


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


Вот неполный список изменений:



V8 5.8


Новая версия JavaScript рантайм движка V8 содержит значительные улучшения в производительности и доступном API. Разработчики гарантируют, что эта версия будет содержать ABI (application binary interface) совместимый с грядущими версиями V8 — 5.9 и 6.0.


В 5.8 5.9 впервые включат по умолчанию оптимизирующий компилятор TurboFan и интерпретатор Ignition, которые правда были доступны и в предыдущем релизе. Это обещает нам меньшее потребление памяти, и значительную оптимизацию try/catch объявлений, генераторов и async функций. Эти же изменения попадут в следующий релиз Chrome 59.


Эти изменения оказались настолько значительными, что технический комитет Node.js (Core Technical Committee) принял решение перенести релиз 8.0.0, изначально планировавшийся в апреле, на май.


N-API


N-API — это API для разработки нативных аддонов, независимо от нижележащего JavaScript рантайма. Используемый ABI (application binary interface) будет оставаться стабильным на протяжении нескольких версий Node. Таким образом, модули скомпилированные для одной версии, будут выполняться и на более поздних версиях Node без перекомпиляции.


async_hooks


Этот экспериментальный модуль (ранее известный как async_wrap) предоставляет разработчикам возможность отслеживать исполнение событийного цикла Node.js. API позволяет зарегистрировать коллбэки, которые уведомляют потребителя о жизненном цикле асинхронных ресурсов внутри приложения, взаимодействующих с нижележащим C++ кодом. Такие асинхронные ресурсы отвечают, например, за TCP-сокеты, чтение из файла и т.д.


WHATWG URL парсер


Экспериментальный URL парсер, соответствующий стандарту WHATWG, был добавлен в 7 версии Node.js, а в 8-ой приставку «экспериментальный» официально отменили. Теперь реализация парсера в Node совпадает с таковой в современных браузерах, что позволяет код, связанный с URL, использовать как в серверном так и клиентском окружении.


Повышение безопасности буфера


До 8 версии Node.js буфер созданный с помощью конструктора Buffer(Number) не инициализировал выделяемую память нулями. В результате, инстансы буфера могли содержать чувствительную информацию. Эту уязвимость в новой версии устранили, при создании, буфер будет заполняться нулями. Поскольку это оказывает негативное влияние на производительность, для случаев где допустимо, разработчики предлагают использовать другой API — Buffer.allocUnsafe(), позволяющий создавать неинициализированный буфер.


Упрощенная промисификация


Промисификация — это обертка над асинхронным кодом, использующим коллбэки, возвращающая промис. Функция, выполняющая промисификацию, есть, например, у популярной библиотеки bluebird. Теперь в Node.js появился ее аналог для нативных промисов: util.promisify(). Вот пример ее использования:


const fs = require('fs');
const util = require('util');

const readfile = util.promisify(fs.readFile);

readfile('/some/file')
  .then((data) => { /** ... **/ })
  .catch((err) => { /** ... **/ });

Статичные коды ошибок


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


  • используя новое свойство code у инстансов объекта Error
  • при выводе трассировки стека ошибки, код будет в квадратных скобках, например: [ERR_ASSERTION]


Из мелких, но приятных бонусов: за флагом --harmony теперь можно использовать свойство rest у объектов:


let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }
console.log(x) // 1
console.log(y) // 2
console.log(z) // { a: 3, b: 4 }

А также нативные методы (без --harmony) string.padStart() и string.padEnd(), ведь мы все помним и скорбим...


P.S. Ах да, и самое важное нововведение — при упоминании версий Node.js разработчики отказались от префикса «v»: v0.10, v0.12, v4, v6. Чтобы не было путаницы с движком V8, теперь официально звучит так: Node.js 8.



При подготовке статьи были использованы следующие ресурсы:


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

https://habrahabr.ru/post/329942/


Метки:  

ТОП 100 англоязычных сайтов об IT

Среда, 31 Мая 2017 г. 19:02 + в цитатник


Привет, Хабр!
Чтение на английском может быть полезно как в изучении иностранного языка, так и в пополнении профессиональных знаний. Мы задались вопросом, какие новостные порталы, блоги и прочие Интернет-ресурсы на английском языке имеют четкую IT-направленность и наиболее интересны разработчикам, админам, тестировщикам, веб-дизайнерам и людям других технических специальностей в России?

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

Всего за последние полгода на Хабре было около 1000 переводных статей. Мы посчитали для каждого источника переводов средний рейтинг публикации, среднее число просмотров и количество добавлений в Избранное. Затем были удалены сайты со средним рейтингом публикаций менее 16 и числом просмотров менее 4000. Полученные сайты мы отсортировали по числу переводов на Хабре из этого источника, так как, возможно, это характеризует ресурс как часто обновляемый и «объёмный».

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

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

Qnty — количество переводов из источника
Rating — средний рейтинг перевода
Views — среднее число просмотров
Fav — среднее число добавлений в Избранное
Чтобы читателю было удобнее понять интересен ли для него ресурс, мы «спарсили» Title


medium.com
Qnty 83 | Rating 26 | Views 15700 | Fav 110
Title: Medium – Read, write and share stories that matter

www.gamasutra.com
Qnty 83 | Rating 25 | Views 11000 | Fav 70
Title: Gamasutra — The Art & Business of Making Games

medium.freecodecamp.com
Qnty 39 | Rating 24 | Views 23700 | Fav 180
Title: freeCodeCamp

hackernoon.com
Qnty 23 | Rating 51 | Views 33800 | Fav 220
Title: Hacker Noon — how hackers start their afternoons

www.youtube.com
Qnty 17 | Rating 28 | Views 15400 | Fav 80
Title: Donald Knuth (Scientist) on YouTube

geekflare.com
Qnty 15 | Rating 16 | Views 12000 | Fav 180
Title: Geek Flare — Web Infrastructure, Web Security & Web Tools.

github.com
Qnty 14 | Rating 32 | Views 25300 | Fav 290
Title: The world's leading software development platform

likegeeks.com
Qnty 12 | Rating 27 | Views 16300 | Fav 360
Title: Linux, Tutorials, Pentesting, Python and IOS — Like Geeks

blogs.msdn.microsoft.com
Qnty 10 | Rating 33 | Views 16100 | Fav 90
Title: MSDN Blogs — Get the latest information, insights, announcements, and news from Microsoft experts and developers in the MSDN blogs.

about.gitlab.com
Qnty 10 | Rating 23 | Views 10000 | Fav 70
Title: Code, test, and deploy together with GitLab open source git repo management software | GitLab

fabiensanglard.net
Qnty 7 | Rating 74 | Views 21100 | Fav 130
Title: Fabien Sanglard's website: Deep magic explained. Fabien Sanglard's chronicles of software wizardry. Fabien Sanglard's non-blog.

www.pymnts.com
Qnty 7 | Rating 16 | Views 6000 | Fav 20
Title: Payments News & Mobile Payments Trends, Consumer Payments News, Financial Technology News | PYMNTS.com

uxdesign.cc
Qnty 6 | Rating 44 | Views 19500 | Fav 160
Title: User Experience, Usability, Product Design. Follow the UX Bear.

www.smashingmagazine.com
Qnty 6 | Rating 32 | Views 19600 | Fav 230
Title: Smashing Magazine — For Professional Web Designers and Developers

docs.microsoft.com
Qnty 6 | Rating 23 | Views 7800 | Fav 80
Title: docs.microsoft.com | Microsoft Docs

blog.wolfram.com
Qnty 6 | Rating 21 | Views 10900 | Fav 70
Title: Wolfram Blog: News and Ideas from Wolfram Research

facebook.github.io
Qnty 5 | Rating 22 | Views 10600 | Fav 110
Title: Facebook code

blog.cleancoder.com
Qnty 4 | Rating 34 | Views 17800 | Fav 100
Title: Clean Coder Blog

learnopengl.com
Qnty 4 | Rating 28 | Views 17900 | Fav 160
Title: Learn OpenGL, extensive tutorial resource for learning Modern OpenGL

preshing.com
Qnty 4 | Rating 26 | Views 12500 | Fav 120
Title: Preshing on Programming — Jeff Preshing, Canadian computer programmer.

www.linux.com
Qnty 4 | Rating 26 | Views 32400 | Fav 200
Title: Linux.com | News for the open source professional

gamedevelopment.tutsplus.com
Qnty 4 | Rating 17 | Views 11600 | Fav 60
Title: How To Make and Design Computer Games by Envato Tuts+

bitsofco.de
Qnty 4 | Rating 17 | Views 11200 | Fav 100
Title: bitsofcode — Articles on Frontend Development. All articles are written by Ire Aderinokun, Frontend Developer and User Interface Designer.

eli.thegreenplace.net
Qnty 3 | Rating 54 | Views 7100 | Fav 80
Title: Eli Bendersky's website

dev.to
Qnty 3 | Rating 53 | Views 18900 | Fav 130
Title: dev.to() => The DEV Community

eng.uber.com
Qnty 3 | Rating 48 | Views 28200 | Fav 200
Title: Uber Engineering Blog

www.redblobgames.com
Qnty 3 | Rating 47 | Views 10100 | Fav 210
Title: Red Blob Games 

gamedevelopment.tutsplus.com
Qnty 3 | Rating 43 | Views 12800 | Fav 200
Title: How To Make and Design Computer Games by Envato Tuts+

drawings.jvns.ca
Qnty 3 | Rating 42 | Views 20100 | Fav 270
Title: julia's drawings

m.signalvnoise.com
Qnty 3 | Rating 32 | Views 25600 | Fav 70
Title: Signal v. Noise — Strong opinions and shared thoughts on design, business, and tech. By the makers (and friends) of Basecamp. Since 1999.

martinfowler.com
Qnty 3 | Rating 24 | Views 10800 | Fav 80
Title: Martin Fowler — software delivery and consulting company

blog.revolutionanalytics.com
Qnty 3 | Rating 23 | Views 11200 | Fav 120
Title: Revolutions — Daily news about using open source R for big data analysis, predictive modeling, data science, and visualization since 2008

slack.engineering
Qnty 3 | Rating 22 | Views 8000 | Fav 90
Title: Several People Are Coding — The Slack Engineering Blog

www.percona.com
Qnty 3 | Rating 16 | Views 6100 | Fav 70
Title: Percona – The Database Performance Experts

blog.intercom.com
Qnty 3 | Rating 16 | Views 8800 | Fav 50
Title: Inside Intercom — Design, Customer Success, & Startup Blog

peteris.rocks
Qnty 2 | Rating 100 | Views 43600 | Fav 590
Title: Peteris Nikiforovs | peteris.rocks — Software Developer

www.yegor256.com
Qnty 2 | Rating 96 | Views 51900 | Fav 230
Title: Blog About Computers

jpauli.github.io
Qnty 2 | Rating 92 | Views 31100 | Fav 340
Title: Welcome to Julien Pauli's page

dzone.com
Qnty 2 | Rating 59 | Views 40200 | Fav 710
Title: Programming, Web Development, and DevOps news, tutorials and tools for beginners to experts. Hundreds of free publications, over 1M members, totally free.

80.lv
Qnty 2 | Rating 52 | Views 17700 | Fav 90
Title: 80 level is the best source of valuable information about the gaming industry and its recent trends.

blog.rust-lang.org
Qnty 2 | Rating 45 | Views 6300 | Fav 20
Title: The Rust Programming Language Blog

www.toptal.com
Qnty 2 | Rating 44 | Views 21500 | Fav 190
Title: Toptal — Hire Freelance Talent from the Top 3%

www.asimovinstitute.org
Qnty 2 | Rating 43 | Views 24900 | Fav 540
Title: The Asimov Institute — artificial intelligence research company that uses deep learning to develop tools for the creative industry – architects, graphic designers, fashion couturiers, marketeers and music producers.

blog.discordapp.com
Qnty 2 | Rating 42 | Views 18000 | Fav 120
Title: Discord Blog

gist.github.com
Qnty 2 | Rating 40 | Views 19400 | Fav 130
Title: Create a new Gist · GitHub

blog.acolyer.org
Qnty 2 | Rating 32 | Views 6700 | Fav 70
Title: an interesting/influential/important paper from the world of CS every weekday morning, as selected by Adrian Colyer

blog.codinghorror.com
Qnty 2 | Rating 32 | Views 43100 | Fav 160
Title: Coding Horror

blog.kovalevskyi.com
Qnty 2 | Rating 30 | Views 23000 | Fav 220
Title: TensorFlow in a nutshell

www.math.cornell.edu
Qnty 2 | Rating 26 | Views 7100 | Fav 60
Title: www.math.cornell.edu | Department of Mathematics

vsavkin.com
Qnty 2 | Rating 25 | Views 16200 | Fav 80
Title: Victor Savkin on Angular — In-depth articles about Angular by a core contributor and a co-founder of Nrwl.io.

www.johnzaccone.io
Qnty 2 | Rating 25 | Views 12800 | Fav 110
Title: Docker and other things

news.realm.io
Qnty 2 | Rating 24 | Views 7500 | Fav 110
Title: Realm: Create reactive mobile apps in a fraction of the time

foonathan.net
Qnty 2 | Rating 23 | Views 11000 | Fav 100
Title: foonathan::blog() — Thoughts from a C++ library developer

www.top500.org
Qnty 2 | Rating 21 | Views 10700 | Fav 30
Title: Home | TOP500 Supercomputer Sites

hintjens.com
Qnty 2 | Rating 20 | Views 6100 | Fav 80
Title: Large software systems and on-line communities, which he describes as "«Living Systems»"

www.bfilipek.com
Qnty 2 | Rating 20 | Views 7700 | Fav 70
Title: Bartek's coding blog

hpbn.co
Qnty 2 | Rating 18 | Views 8600 | Fav 200
Title: High Performance Browser Networking (O'Reilly)

kevinkuang.net
Qnty 2 | Rating 18 | Views 5100 | Fav 50
Title: Bioinformatics Playground

microsoft.github.io
Qnty 2 | Rating 17 | Views 4300 | Fav 10
Title: Microsoft on GitHub

androidworks-kea.blogspot.com
Qnty 2 | Rating 17 | Views 6300 | Fav 50
Title: Axiomworks — We create 3D live wallpapers for Android

paulgraham.com
Qnty 2 | Rating 16 | Views 7400 | Fav 40
Title: Paul Graham

seriot.ch
Qnty 1 | Rating 158 | Views 40700 | Fav 360
Title: seriot.ch — open-source software, such as an Objective-C Runtime Browser or the STTwitter library/

www.adriancourreges.com
Qnty 1 | Rating 144 | Views 25600 | Fav 170
Title: Projects — Adrian Courr`eges — French software engineer currently living and working in Tokyo.


mywiki.wooledge.org
Qnty 1 | Rating 139 | Views 44500 | Fav 710
Title: Greg's Wiki (also known as GreyCat's)

www.gwan.com
Qnty 1 | Rating 133 | Views 58000 | Fav 150
Title: G-WAN web server

fuzzyreflection.com
Qnty 1 | Rating 125 | Views 33000 | Fav 400
Title: Personal Space for Self-Reflection

corgibytes.com
Qnty 1 | Rating 124 | Views 69700 | Fav 310
Title: Corgibytes — Software Remodeling — Old Code. New Tricks | Corgibytes

jumpespjump.blogspot.ru
Qnty 1 | Rating 118 | Views 67000 | Fav 230
Title: Jump ESP, jump!


probablydance.com
Qnty 1 | Rating 116 | Views 27500 | Fav 270
Title: I can program and like games

donw.io
Qnty 1 | Rating 110 | Views 21500 | Fav 180
Title: Gazoo.vrv Don Williamson, Consultant Game Technology Programmer/Director of Celtoys

jazcash.com
Qnty 1 | Rating 101 | Views 26300 | Fav 250
Title: Jasper Cashmore — I write about web stuff

www-cs-students.stanford.edu
Qnty 1 | Rating 98 | Views 17200 | Fav 310
Title: Student Information

www.extentofthejam.com
Qnty 1 | Rating 90 | Views 21100 | Fav 210
Title: Extent of the Jam

3dgamedevblog.com
Qnty 1 | Rating 90 | Views 32500 | Fav 140
Title: 3dgamedevblog

labs.ig.com
Qnty 1 | Rating 89 | Views 26100 | Fav 80
Title: IG Labs | Trading APIs

codewords.recurse.com
Qnty 1 | Rating 88 | Views 86600 | Fav 820
Title: Code Words — A publication about programming from the Recurse Center

redditblog.com
Qnty 1 | Rating 83 | Views 16700 | Fav 90
Title: Upvoted — The official Reddit blog

www.brendangregg.com
Qnty 1 | Rating 81 | Views 26500 | Fav 220
Title: Brendan Gregg's Homepage — work on large scale cloud computing performance at Netflix

nothings.org
Qnty 1 | Rating 79 | Views 28300 | Fav 120
Title: the Nothing itself nothings

www.phpclasses.org
Qnty 1 | Rating 74 | Views 22300 | Fav 90
Title: PHP Class Scripts, Tutorials, Jobs, Professionals, Book reviews, User groups, Forums — PHP Classes

writing.kemitchell.com
Qnty 1 | Rating 73 | Views 33000 | Fav 370
Title: /dev/lawyer

www.joyent.com
Qnty 1 | Rating 73 | Views 34000 | Fav 300
Title: Joyent | Triton — THE NEXT GENERATION OF CLOUD

blog.daftcode.pl
Qnty 1 | Rating 73 | Views 26400 | Fav 120
Title: DaftCode Blog

jonibologna.com
Qnty 1 | Rating 72 | Views 28000 | Fav 490
Title: Joni Bologna’s Blog

devs.cloudimmunity.com
Qnty 1 | Rating 72 | Views 34800 | Fav 410
Title: Devs Security

vtorosyan.github.io
Qnty 1 | Rating 72 | Views 58500 | Fav 110
Title: Vardan Torosyan's Blog

mgba.io
Qnty 1 | Rating 72 | Views 10200 | Fav 50
Title: mGBA is a new generation of Game Boy Advance emulator.

herbertograca.com
Qnty 1 | Rating 71 | Views 46400 | Fav 440
Title: @herbertograca

sitr.us
Qnty 1 | Rating 69 | Views 18300 | Fav 90
Title: posts by Jesse Hallett

blog.elpassion.com
Qnty 1 | Rating 69 | Views 32000 | Fav 60
Title: EL Passion Blog

lukasa.co.uk
Qnty 1 | Rating 68 | Views 22100 | Fav 150
Title: LUKASA'S ECHOCHAMBER

blog.lemberg.co.uk
Qnty 1 | Rating 67 | Views 18700 | Fav 130
Title: Lemberg Solutions Blog | Mobile, Drupal, and All Tech …

www.aaronbell.com
Qnty 1 | Rating 67 | Views 12200 | Fav 60
Title: Funny and interesting words about humans and technology

engineering.instagram.com
Qnty 1 | Rating 66 | Views 25700 | Fav 130
Title: Instagram Engineering

charlesleifer.com
Qnty 1 | Rating 66 | Views 25000 | Fav 70
Title: blogging about Python and programming in general

basho.com
Qnty 1 | Rating 65 | Views 44200 | Fav 180
Title: Enterprise NoSQL Database | Scalable Database Solutions | Basho

sidbala.com
Qnty 1 | Rating 64 | Views 22100 | Fav 170
Title: Technology Polymath

blog.sourced.tech
Qnty 1 | Rating 63 | Views 14700 | Fav 180
Title: source{d} blog — Building the first AI that understands code

fvsch.com
Qnty 1 | Rating 62 | Views 26300 | Fav 290
Title: Florens Verschelde’s handcrafted website

artem.krylysov.com
Qnty 1 | Rating 62 | Views 10000 | Fav 170
Title: Welcome — Artem Krylysov — software engineer based in Philadelphia, PA, USA



У вас есть любимый IT-ресурс на английском? Расскажите о нём в комментариях.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/329936/


Деплоим мобильный софт с помощью devops-конвейера Microsoft

Среда, 31 Мая 2017 г. 17:57 + в цитатник

В прошлой статье мы рассмотрели автоматизацию сборки мобильных приложений с помощью Bitrise, разобрались со сборкой Android- (и iOS-) приложения, подключили Xamarin Test Cloud, провели автоматическое UI-тестирование и внедрили HockeyApp для получения обратной связи. Сегодня мы продолжим погружение в мир инструментов Mobile DevOps, которые не просто ускоряют, но еще и заметно упрощают разработку мобильных приложений. На этот раз мы рассмотрим интегрированное решение Visual Studio Mobile Center.



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


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


Обзор Mobile Center


Корпорация Microsoft в недалеком прошлом упустила рынок мобильных экосистем, поэтому теперь всеми силами (и долларами) старается наверстать упущенное, предлагая разработчикам целую кучу различных SDK, сервисов и инструментов. Покупка Xamarin и HockeyApp позволила корпорации предложить рынку интегрированные инструменты для профессиональной разработки мобильных приложений вне зависимости от целевой платформы. Сам конвейер Visual Studio Mobile Center (далее VSMC) основан на уже знакомых нам сервисе аналитики и дистрибуции HockeyApp и облачной ферме устройств Xamarin Test Cloud.


Visual Studio Mobile Center


Если рассматривать рынок инструментов разработки, то все идет к тому, что миром Mobile будут править Android + Java (или что там обещают вместо Java в будущем? Kotlin?), iOS + Swift, Xamarin и React Native. Все четыре стека уже поддерживаются из коробки в новом VSMC. А в будущем обещают добавить еще и Windows.


Mobile Center пока находится в стадии раннего Preview, поэтому возможности еще достаточно ограниченны и сам сервис не рекомендуется к использованию в production-окружении. Однако в VSMC уже доступны все основные элементы конвейера Mobile DevOps: сборка, тестирование, дистрибуция и аналитика (различные события и краши).


Приятное дополнение к VSMC — модули Tables и Identity, которые могут быть полезны, если ты планируешь использовать Azure в своих мобильных приложениях. Tables — это облачный MBaaS (mobile backend as a service), который позволит развернуть базу данных в облаке и в несколько строк кода получить к ней доступ из приложения. В Azure развернутся SQL Database и REST-сервер (на базе Azure App Services), настроенные для совместной работы и готовые к масштабированию и безотказному доступу. В реальных и больших проектах эта штука часто может быть излишней, однако для твоего стартапа или быстрого прототипа подойдет идеально. С помощью Identity можно будет легко авторизовать пользователей через Facebook, Google или Twitter. Авторизованные таким образом юзеры смогут получать доступ к данным из Tables, помеченным как требующие авторизации. И Tables, и Identity предоставляют базовую функциональность, которой может быть достаточно для небольших или простых проектов. Пара строк кода — и все работает.


Но сам по себе Mobile Center — это в первую очередь конвейер DevOps, поэтому перейдем к рассмотрению ключевой функциональности.


Билдим


Итак, у нас есть исходные коды проекта Navigation Drawer из набора стандартных примеров Android на Java. Заливаем их на GitHub (поддержка других сервисов будет добавлена в VSMC позже), создаем бесплатную учетку на mobile.azure.com и добавляем новое приложение.


Visual Studio Mobile Center


Переходим в раздел Build, подключаем репозиторий GitHub и выбираем основной branch. После сборки мы можем скачать полученные APK и подробные логи. Все как у людей и без излишеств.


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


Visual Studio Mobile Center


В будущем также обещают добавить поддержку репозиториев Bitbucket и Visual Studio Team Services. Для автоматических UI-тестов, правда, пока придется заливать сборку руками из консоли, о чем мы и поговорим далее.


Альтернатива VSMC на данном этапе


Если тебя интересует отдельная система для сборки проектов, то настоятельно рекомендуем познакомиться с Bitrise.io, о котором мы рассказывали.


Гоняем тесты


Как мы уже знаем, в VSMC интегрирован сервис Xamarin Test Cloud. Для нашего примера мы будем использовать написанный раньше скрипт на Calabash.


Visual Studio Mobile Center


Для начала нам необходимо установить Node.js и Ruby плюс ряд дополнительных gems. Выдумывать ничего не придется, просто следуй инструкциям в Mobile Center. Перед тем как отправить приложение на тестирование, его нужно собрать командой calabash-android build [путь до apk].apk в консоли. Результатом работы этой команды должна быть папка test_servers, содержащая корректно подписанный APK-файл. После сборки потребуется выполнить команду mobile-center test run. Через несколько минут мы увидим результаты тестирования и получим email-уведомление о завершении тестов.


Кстати, свои фермы для автоматизированного UI-тестирования приложений также представили Amazon и Google.


Отличий от оригинального Xamarin Test Cloud здесь немного: есть пошаговые скриншоты и мониторинг потребления ресурсов. Устройств много, но в VSMC Preview пока есть ограничение на количество одновременных запусков (одно устройство за один раз) и выделенного времени (до одного часа в день).


Альтернативы VSMC на данном этапе


Если ты ищешь отдельную облачную ферму устройств для автоматизированного UI-тестирования, то могу посоветовать посмотреть в сторону AWS Device Farm, Xamarin Test Cloud, Google Firebase Test Lab.


Анализируем и распространяем


В качестве подсистемы для сборки крашей и событий внутри VSMC используется сервис HockeyApp. Для интеграции SDK достаточно добавить новые зависимости к проекту и зарегистрировать обработчик крашей.


Visual Studio Mobile Center


Сами краши можно смотреть в stack trace, а события — в статистике.


Альтернативы VSMC на данном этапе


Для более детального анализа поведения пользователей все-таки лучше использовать «Яндекс.Метрику», Google Analytics или Flurry, так как маркетологи предпочитают для своей работы эти сервисы.


Visual Studio Mobile Center


Выводы


Итак, сегодня мы познакомились с универсальным и интегрированным конвейером Visual Studio Mobile Center. Если сравнивать с тем же Bitrise.io, интегрированные решения, с одной стороны, могут сильно упростить жизнь разработчикам мобильных приложений и ускорить внедрение инструментов DevOps в повседневную практику, но с другой — они не настолько гибки и функциональны, как DIY-конвейеры. В ближайшее время стоит ожидать появления большего числа интегрированных систем от других игроков. Если же программистский зуд или задачи проекта требуют своего конвейера, то выбор инструментария уже сейчас достаточно широк, включая различные open source проекты, поэтому выбирать надо исходя из требований и планов развития продукта.


Успешной тебе автоматизации! Будут вопросы — пиши в комментариях!


Об авторе



Вячеслав Черников — руководитель отдела разработки компании Binwell. В прошлом — один из Nokia Champion и Qt Certified Specialist, в настоящее время — специалист по платформам Xamarin и Azure. В сферу mobile пришел в 2005 году, с 2008 года занимается разработкой мобильных приложений: начинал с Symbian, Maemo, Meego, Windows Mobile, потом перешел на iOS, Android и Windows Phone.


Статьи Вячеслава вы также можете прочитать в блоге на Medium.


Другие статьи автора:



Напоминаем, что это полная версия статьи из журнала Хакер.

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

https://habrahabr.ru/post/329908/


[recovery mode] Hibernate+jsp при поддержке сервлетов

Среда, 31 Мая 2017 г. 16:48 + в цитатник
Всем привет!
В этой статье я расскажу о том, как связать JSP с Hibernate при помощи сервлета. Я надеюсь, что вы умеете работать с Hibernate, если же нет, то настоятельно рекомендую прочитать эту статью. Также я надеюсь, что вы знакомы с сервлетами и хоть раз их успешно запускали.
Для того, чтобы у вас была такая же база данных в MySQL, вставьте туда следующий код:
CREATE SCHEMA `hero`;

CREATE TABLE `hero`.`heroes`
(
`idhero` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(45) NOT NULL,
PRIMARY KEY (`idhero`));
Создаём maven проект и вставляем следующие зависимости и не только:

war

4.3.5.Final



javax.servlet
servlet-api
3.0-alpha-1
provided


commons-fileupload
commons-fileupload
1.2.2


commons-io
commons-io
2.4


org.hibernate
hibernate-core
${hibernate.version}


org.hibernate
hibernate-entitymanager
${hibernate.version}


javaee
javaee-api
5


mysql
mysql-connector-java
5.1.38


javax
javaee-web-api
6.0
provided



org.apache.commons
commons-lang3
3.4


jstl
jstl
1.2



Подключаемся к базе данных. Опять же: если вы не знаете, как это делается, то статья об этом и не только выше.
Затем подключаем web модуль.
Заходим в File,Project Structure вкладка modules. Передварительно удалите всё, что есть во вкладке modules(если оно там есть) нажав на -(красненький).Нажимаете на +(зелёный)
и добавьте web модуль

Дальше во вкладке Artifacts тоже удаляете все артефакты и добавляете новый +,Web Application:Exploded,From modules,OK

Дальше развертываем Hibernate и для того, чтобы было удобно, засуньте эту модель в папку model, которая находится в by, указав в этот путь при развертке Hibernate.
В папке by создайте папку util, а в ней класс HibernateUtil:
package by.util;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;


public class HibernateUtil {
    private static SessionFactory sessionFactory=buildSessionFactory();

    private static SessionFactory buildSessionFactory() {
        try{
            return new Configuration().configure().buildSessionFactory();
        }catch (Exception e){
            throw new ExceptionInInitializerError(e);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }
}


Благодаря HibernateUtil мы можем создать специальный SessionFactory под наш проект.
В папке by создаём папку DAO, в ней класс DAOImple (мы не создаём интерфейс DAO, так как пример небольшой):
package by.DAO;

import by.model.HeroesEntity;
import by.util.HibernateUtil;
import org.hibernate.Query;
import org.hibernate.Session;

import java.util.List;
public class DAOImple {
    public void saveHero(HeroesEntity heroesEntity){
        Session session= HibernateUtil.getSessionFactory().openSession();
        session.beginTransaction();
        session.save(heroesEntity);
        session.getTransaction().commit();
        session.close();
    }
    public List getAll(){
        Session session=HibernateUtil.getSessionFactory().openSession();
        session.beginTransaction();
        List list=session.createQuery("from HeroesEntity").list();
        session.getTransaction().commit();
        session.close();
        return list;
    }
    public void update(HeroesEntity heroesEntity){
        Session session=HibernateUtil.getSessionFactory().openSession();
        session.beginTransaction();
        session.update(heroesEntity);
        session.getTransaction().commit();
        session.close();
    }
public HeroesEntity getHeroById(int id){
        Session session=HibernateUtil.getSessionFactory().openSession();
        session.beginTransaction();
        Query query= session.createQuery("from HeroesEntity where idhero=:id");
        query.setInteger("id",id);
        HeroesEntity heroesEntity= (HeroesEntity) query.uniqueResult();
        session.getTransaction().commit();
        session.close();
        return heroesEntity;
}
public void deleteHeroes(int id){
    Session session=HibernateUtil.getSessionFactory().openSession();
    session.beginTransaction();
    Query query=session.createQuery("from HeroesEntity where idhero=:id");
    query.setInteger("id",id);
    HeroesEntity heroesEntity= (HeroesEntity) query.uniqueResult();
    session.delete(heroesEntity);
    session.getTransaction().commit();
    session.close();
}
}

Класс DAOImple нам нужен для того, чтобы работать с той информацией, что есть в базе данных.
В папке by создаём папку servlets, а в ней создаём сервлет(не класс!) SaveServlet (комментарии в коде):


package by.servlets;

import by.DAO.DAOImple;
import by.model.HeroesEntity;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class SaveServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        DAOImple daoImple=new DAOImple();
        ////////////////////////////////////////
        //INDEX.JSP
        ////////////////////////////////////////
        if(request.getParameter("add")!=null){//при нажатии на кнопку add
            HeroesEntity heroesEntity=new HeroesEntity();//создаём экземпляр класса модели базы данных
            heroesEntity.setIdhero(Integer.parseInt(request.getParameter("id")));//задаём ему id взятый из поля c именем id
            heroesEntity.setName(request.getParameter("name"));//аналогично с прошлой строкой
            daoImple.saveHero(heroesEntity);//сохраняем в базу данных полученный объект
            request.setAttribute("list",daoImple.getAll());//создаём аттрибут который взял в себя всё что есть в базе данных
            RequestDispatcher requestDispatcher=request.getRequestDispatcher("list.jsp");//перебрасываемся на list.jsp
            requestDispatcher.forward(request,response);
        }
        if(request.getParameter("showAll")!=null){//при нажатии на кнопку showALL
            request.setAttribute("list",daoImple.getAll());//создаём аттрибут который взял в себя всё что есть в базе данных
            RequestDispatcher requestDispatcher=request.getRequestDispatcher("list.jsp");//перебрасываемся на list.jsp
            requestDispatcher.forward(request,response);
        }
        ///////////////////////////////////////////////////
        //LIST.JSP
        ///////////////////////////////////////////////////
        String action=request.getParameter("action");//создаём action который будет реагировать на те или иные действия
        if(action.equalsIgnoreCase("update")){//если action отреагировал на update
          request.setAttribute("hero",daoImple.getHeroById(Integer.parseInt(request.getParameter("idhero"))));//создаём атрибут который по id возвращает определённого HeroesEntity
            RequestDispatcher requestDispatcher=request.getRequestDispatcher("update.jsp");////перебрасываемся на update.jsp
            requestDispatcher.forward(request,response);
        }
        if(action.equalsIgnoreCase("delete")){//если action отреагировал на update
            daoImple.deleteHeroes(Integer.parseInt(request.getParameter("idhero")));//удаляем по id
            request.setAttribute("list",daoImple.getAll());//создаём аттрибут который взял в себя всё что есть в базе данных
            RequestDispatcher requestDispatcher=request.getRequestDispatcher("list.jsp");//перебрасываемся на list.jsp
            requestDispatcher.forward(request,response);
        }
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        DAOImple daoImple = new DAOImple();
//////////////////////////////////
// UPDATE.JSP
//////////////////////////////////
        if (request.getParameter("update") != null) {//при нажатии на кнопку update
            HeroesEntity heroesEntity = new HeroesEntity();//создаём экземпляр класса
            heroesEntity.setIdhero(Integer.parseInt(request.getParameter("idhero")));//задаём id из поля idhero
            heroesEntity.setName(request.getParameter("name"));//задаём name из поля name
            daoImple.update(heroesEntity);//апдейтим
            request.setAttribute("list", daoImple.getAll());//создаём аттрибут который взял в себя всё что есть в базе данных
            RequestDispatcher requestDispatcher = request.getRequestDispatcher("list.jsp");//перебрасываемся на list.jsp
            requestDispatcher.forward(request, response);
        }
    }
}


Регестрируем сервлет в web.xml:


    
        SaveServlet
        by.servlets.SaveServlet
    
    
        SaveServlet
        /save
    


В папке web создаём index.jsp стартовую страницу:




    



а так же list.jsp:




    


id name
${list.idhero} ${list.name} ">update delete

и update.jsp:




    


"> ">

Дальше настраиваем TomCat:


Даём ему артефакт(можно просто нажать внизу на кнопку с красной лампочкой и названием Fix):

Я надеюсь что вам удалось запустить этот пример если же нет можете обратится ко мне в vk:ссылка
Этот пример на github:здесь
Удачи!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/329924/


Метки:  

[Из песочницы] Многоступенчатая организация хранения резервных копий для самых маленьких

Среда, 31 Мая 2017 г. 16:26 + в цитатник
Совсем недавно по планете прокатились волны WannaCry и его клонов. А сама проблема шифровальщиков стоит перед системными администраторами уже более 10 лет. Рано или поздно – но все внедренные и реализованные меры по защите от шифровальщиков не помогают и все-таки находится пользователь, который открывает письмо, вложение и получает полный «букет». Также много «приятных и увлекательных» часов получает системный администратор.

И тут то все четко начинают понимать, что нужны резервные копии (много, разных, в разных местах). Т.е. правило 3-2-1, придуманное и описанное Peter-ом Krogh-ом, весьма желательно выполнять. Данная статья – пример, который помогает сделать реальным выполнение данного правила на «коленке» — без покупки дорогостоящего оборудования (в условиях жесткой экономии).

Итак – условия решаемой задачи:


• Есть небольшая среда виртуализации от Vmware (пара-тройка ESXi-серверов, vCenter, самый дешевый пакет лицензий – начальный Kit – в целом это не важно для данной статьи. Аналогично статья подойдет и для Hyper-V);

• Есть с десяток виртуальных машин, содержимое которых не хочется потерять в случае отработки автоматизированным Ransomware скриптом;

• Есть система резервного копирования от Veeam (бесплатная редакция, бекапы делаются с помощью PowerShell и Task Schedule).

Задачи:


• Делать резервные копии серверов 1 раз в день по ночам;

• Размножать копии (копировать на NAS-сервер с FreeBSD + ZFS). К слову, на ZFS тоже делаются снапшоты, которые автоматически удаляются по заданному расписанию (zfSnap + Cron);

• Иметь оффлайновую копию «бекапов» на съемном носителе.

Реализация:


Так как основной сервер, который делает резервные копии из работающих виртуальных машин, управляется операционной системой Windows Server (ввиду того, что Veeam Backup работает пока что только на базе этой ОС), было решено для реализации задач использовать PowerShell.

Решение задачи синхронизации бекапов между основным сервером (Windows) и NAS-сервером (FreeBSD):


Для решения задачи требовался скрипт, который бы запускался через Task Scheduler и синхронизировал каталог A с сетевым ресурсом B, доступным через протокол SMB. Вначале я попробовал использовать robocopy – но тесты показали весьма низкую скорость работы полученного скрипта. И я решил реализовать данный скрипт на другом инструменте.

Пятиминутный поиск и 10 минут тестов показали наличие весьма жизнеспособного и готового решения: powershell-synchronizing-a-folder

Скрипт оказался шикарным:

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

В итоге на основном сервере появилась пачка заданий в Task Schedule вида:

powershell.exe "C:\Scripts\syncfolder.ps1  -SourceFolder:G:\Backups\WEBAPPS -TargetFolder:\\192.168.0.232\backups$\WEBAPPS"

И задача синхронизации резервных копий после выполнения заданий Veeam Backup была решена (2 копия с дельтой по времени).

Решение задачи создания оффлайновых резервных копий:


Сама идея проста:

• Подключаем к серверу с Veeam Backup внешний USB 3.0 жесткий диск на 2 Тб

• Большую часть времени держим его Offline (и этим мы защищаемся от автоматизированных Ransomware);

• Когда скрипт отрабатывает, он переводит диск в Online, делает каталог с текущей датой, копирует в него текущие резервные копии, и по окончании выполнения снова переводит диск в Offline.

Реализация:

Отправной точкой служит команда: Get-Disk – нам нужно понять, какие диски у нас есть в системе и виден ли нам внешний USB-диск:


PS C:\Windows\system32> Get-Disk

Number Friendly Name                            OperationalStatus                    Total Size Partition Style
------ -------------                            -----------------                    ---------- ---------------
1      WDC WD30PURX-64P6ZY0                     Online                                  2.73 TB GPT
0      WDC WD10EZEX-60M2NA0                     Online                                931.51 GB GPT
2      WD Elements 25A3 USB Device              Offline                                 1.82 TB GPT

Теперь нам нужно поместить ссылку на USB-диск в переменную. Для его идентификации предлагается использовать атрибут «Friendly Name». Если Вы предпочитаете использовать другие атрибуты – выведите полный список (get-disk | select *). Либо посмотрите список доступных свойств и методов (get-disk | get-member).

Итого первая часть скрипта:


# Find USB disk by FriendlyName
$mybackupdisk = get-disk | where {$_.FriendlyName -like 'WD Elements 25A3 USB Device'}

Далее – нужно перевести диск из Offline в Online, а также убедиться, что диск в режиме Read-Write (иногда, по невыясненной причине, диск после перехода в Online становился Read-Only. Для выяснения номера диска используем свойство Number ($mybackupdisk.Number).

Получаем кусок:


# Make disk Online
Set-Disk -Number $mybackupdisk.Number -IsOffline $False
Start-Sleep -s 5
 
# Make disk Writeable (some times it ReadOnly after online - shit happens...)
Set-Disk –Number $mybackupdisk.Number -IsReadonly $False
Start-Sleep -s 5

Для идентификации буквы диска сделаем следующий финт – на USB диск повесим метку (имя): VMUSBBACKUPS (либо через Disk Manager, либо с помощью команды Set-Volume).

Далее – с помощью команды Get-Volume определяем букву подключенного USB-диска (после перевода его в Online):


# Find Disk Volume
$usbvolumename = Get-Volume | where {$_.FileSystemLabel -like 'VMUSBBACKUPS'}

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

Создаем каталог с текущей датой в имени:


$date = Get-Date
$newbackupfolder = $date.ToString("yyyy-MM-dd")
 # Full Backup Fath
$createdirfullpath = $usbvolumename.DriveLetter + ":\" + $newbackupfolder
 # Create Backup Directory
New-Item -ItemType directory -Path $createdirfullpath -Force -Confirm:$false
Start-Sleep -s 2

Копируем резервные копии:


# Source Backup Dir (with backups)
$sourcebackup = "F:\Backups\VCENTER\"
 
# Copy to USB from Disk
Copy-Item $sourcebackup -Destination $createdirfullpath -Recurse
Start-Sleep -s 5

Другой вариант – когда нам нужно не создавать каждый раз новые каталоги и копии – а переписывать файлы новыми версиями – тогда используем уже ранее найденный скрипт для синхронизации каталога А с Б:


# Sync from HDD to USB:
C:\Scripts\syncfolder.ps1  -SourceFolder:F:\Backups\ -TargetFolder:$usbvolumename.DriveLetter:\VMs\
Start-Sleep -s 5

В любом случае – когда Вы закончите копировать либо синхронизировать, весьма желательно сбросить кэш операций (из ОЗУ на HDD/USB) командой:


# Write USB Disk Cache before offline
Write-VolumeCache $usbvolumename.DriveLetter
Start-Sleep -s 5

И не забыть снова перевести диск из Online в Offline:


# Place USB to Offline
Set-Disk -Number $mybackupdisk.Number -IsOffline $True

Результаты:


• Получили резервные копии в трех местах (Windows-сервер, FreeBSD-сервер, USB-диск);
• Два вида хранения (в шарах и на диске);
• Один носитель другого типа – отсуждаемый. Можно вообще иметь пару дисков – и просто 1 или 2 раза в месяц менять их местами (один в сейф). Так как USB-диск в режиме Offline 95% времени – его всегда можно безболезненно выдернуть из сервера.

Моя статистика:


• данная схема работает уже 6 месяцев без сбоев;
• объем синхронизируемых данных (сжатые и дедуплицированные бекапы – от 500 до 700 Гб);
• Время синхронизации на USB-диск – 1 час 20 минут в среднем (1 раз в неделю в выходные).

Полные скрипты можно скачать с Google Disk: BackupExamples
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/329920/


Метки:  

Анализ изменений в игре

Среда, 31 Мая 2017 г. 16:21 + в цитатник
Одна из основных характеристик успешной мобильной игры — ее постоянное оперирование: это и переработка существующего контента, и добавление нового. Но есть и обратная сторона медали – нужно постоянно оценивать риски изменений в очередной версии приложения. Необходимо заранее представлять, как изменения в апдейте повлияют на показатели проекта. Иначе можно оказаться в ситуации, когда во время планового обновления внезапно ломается баланс и нужно срочно поднимать всю команду разработки для выпуска хотфикса.

Еще до сборки нового продакшен-билда мы должны понимать, на какие показатели повлияет нововведение. Ведь в новых версиях игры может быть множество изменений баланса. Без предварительного планирования неизбежно возникнет один из таких вопросов: «Что же повысило ARPU в Канаде — локальные мероприятия в честь национального праздника или общее повышение сложности группы каких-то уровней; а может, просто звезды так совпали?». Безусловно, и после выхода апдейта выполняется всесторонний анализ результатов, но понимать характер изменений нужно заранее.


Разработка изменений


Перед внедрением новых фич в игру мы стараемся ответить на ряд вопросов:

  1. Куда будем смотреть? — на какие метрики (как общепринятые KPI, так и внутренние критерии качества) изменение предположительно повлияет, какой эффект мы будем считать успешным для проекта.
  2. На кого будем смотреть? — для кого предназначено изменение (для китов, для новых игроков, для игроков из Китая, для игроков, застрявших на какой-то конкретной группе уровней, и т.д.).
  3. Зачем будем смотреть? — какие есть сопутствующие риски (сложнее уровни -> больше монетизация и больше отвалы (churn); легче уровни -> меньше монетизация и меньше отвалы).
  4. А есть что смотреть? — вся ли необходимая информация трекается в игре.

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

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

Про AB-тесты написано много хорошего и разного, но мы напишем еще чуть-чуть.

Планирование AB-теста


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

  • Выбор величин для оценки.

    Пример: предполагается, что изменения должны «закрутить гайки» и стимулировать игроков платить для прохождения уровня. Что нам надо оценить: конверсия, ARPU, ретеншен, отвалы, сложность уровня, использование бустов. Нам нужно быть уверенными, что увеличение дохода за счет повышения конверсии стоит потенциальной потери пользователей. Тем более что часть отвалившихся пользователей могла бы в будущем начать смотреть рекламу и приносить доход. Нужно также быть уверенным, что уровень сделан хорошо, что он не слишком рандомный, что нет «серебряной пули» в виде одного буста, гарантирующего прохождение. Хватит ли этих контролируемых метрик или стоит добавить еще?

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

    Пример: модель предсказывает гарантированное отсутствие конверсии у порядка 2000 человек в группе с левел-апом на выбранный уровень в выбранный день. Сколько дней запускать предсказания, чтобы по итогам теста получились значимые результаты при увеличении показа рекламных роликов для данной группы?

  • Формализация условий запуска теста.

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

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

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

  • Формализация критериев для принятия решений о завершении теста.

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

  • Формализация критериев для принятия решения о внедрении изменения в игру. Поговорим об этом в следующем разделе статьи!

Принятие правильных решений по результатам теста


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

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

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

  1. Частотный подход — классический подход в оценке результатов. Формулируем нулевую гипотезу об отсутствии достоверной разницы между группами и альтернативную гипотезу, отрицающую нулевую. Итогом исследования станет решение о справедливости какой-либо из двух гипотез для всей генеральной совокупности. Выбираем уровень альфа (1 — альфа = уровень значимости) и проводим тест с использованием статистического критерия (z-критерий, t-критерий). Далее строим доверительные интервалы для контрольной и тестовой групп. Если эти интервалы не пересекаются, то результат достоверный с вероятностью 1 — альфа. По сути, выбранное значение альфа — это вероятность ошибки первого рода, т.е. вероятность, что по результатам эксперимента будет принята альтернативная гипотеза, хотя для генеральной совокупности верна нулевая гипотеза.

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

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

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

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

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

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

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

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

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

  1. DPU (Daily Paying Users) — тестовая группа больше контрольной на 1% с вероятностью 87% при использовании байесовского подхода, тогда как это же расхождение групп при использовании частотного подхода справедливо с вероятностью 50%.
  2. ARPU Daily (ARPDAU) — тестовая больше контрольной на 2,55%, байесовский подход — 94%, частотный — 74%.
  3. ARPPU Daily — тестовая больше на 1,8%, байесовский — 89%, частотный — 61%.

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


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

  1. Конверсия в контрольной группе была выше, чем в тестовой, при байесовском подходе с вероятностью 97% это было ясно по 50 000 игроков, при частном — с 95% вероятностью по 75 000 игроков.

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


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

Мониторинг метрик


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

У нас есть свой дашборд, внутренняя разработка на базе opensource-решения Cubes. Им пользуются не только аналитики, но и гейм-дизайнеры, менеджеры, продюсеры и другие участники нашей дружной команды разработки. С помощью этого дашборда (точнее, набора дашбордов) по каждому проекту отслеживаются как ключевые показатели, так и свои внутренние метрики, такие как, например, сложность матч-3 уровней. Данные для дашбордов готовятся в формате olap-кубов, которые, в свою очередь, содержат агрегированные данные из базы данных raw-событий. Благодаря выбору аддитивных моделей для каждого графика есть возможность выполнения drilldown (разложения на составляющие) по необходимым категориям. При желании можно группировать или разгруппировать пользователей для применения фильтров при визуализации метрик. Например, можно сделать drilldown по версии приложения, версии настройки уровней, региону и платежеспособности, оставив только платящих игроков из Австралии с версией игры 1.8 и версией настройки уровней 4.



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

https://habrahabr.ru/post/329910/


Метки:  

[Перевод - recovery mode ] Безопасность в веб-разработке: чек-лист

Среда, 31 Мая 2017 г. 16:10 + в цитатник
Светлана Шаповалова, редактор «Нетологии», адаптировала статью Michael O'Brien, в которой он составил чек-лист для веб-разработчиков, предпочитающих разрабатывать не только удобные, но и безопасные приложения.

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

Если вы уже заразились идеей «минимально жизнеспособного продукта» (англ. MVP — minimum viable product, прим. перев.) и считаете, что за месяц можно создать одновременно полезный и безопасный продукт — подумайте дважды, прежде чем выпускать его. Просмотрев чек-лист, вы поймете, что оставляете немало уязвимостей.



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

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

Базы данных


  • Храните данные идентификации пользователей и конфиденциальные данные (токены, адреса электронной почты, платежные реквизиты) в зашифрованном виде.
  • Если база данных поддерживает шифрование хранящихся данных (например, AWS Aurora), подключите его для защиты данных на диске. Убедитесь, что все резервные копии также хранятся в зашифрованном виде.
  • Используйте наименьший уровень привилегий для доступа к учетным записям пользователей в базе данных. Не используйте корневую учетную запись базы данных.
  • Храните и распространяйте секретные данные с помощью keystore, предназначенного для этих целей. Не используйте хардкод в приложениях.
  • Предотвращайте SQL-инъекции, используя исключительно подготовленные SQL-запросы. Например: если вы используете NPM, не используйте npm-mysql, используйте npm-mysql2, который поддерживает подготовленные выражения.

Разработка


  • Убедитесь, что все компоненты приложения проверены на наличие уязвимостей для каждой версии, переданной в продакшн. Сюда входят O/S, библиотеки и пакеты. Проверка должна быть автоматизирована в процессе CI-CD (CI — continuous integration — непрерывная интеграция, CD — continuous delivery — постоянная поставка, прим. перев.).
  • С одинаковой бдительностью относитесь как к безопасности среды разработки, так и к безопасности продакшен-сервера. Создавайте программное обеспечение в защищенной изолированной среде разработки.

Идентификация


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

Защита от DDoS-атак


  • Убедитесь, что DDoS-атаки на ваши API не навредят сайту. Как минимум, защитите «узкие» места API, такие как процедуры генерации логина и токена.
  • Обеспечьте разумные ограничения по размеру и структуре предоставляемых пользователем данных и запросов.
  • Смягчайте DDoS-атаки с помощью глобального сервиса с кэширующим прокси, например, CloudFlare. Он включается, когда вы находитесь под DDOS-атакой, а в обычном режиме функционирует как DNS lookup.

Веб-трафик


  • Используйте TLS для всего сайта, а не только для форм входа и ответов. Никогда не используйте TLS только для формы входа.
  • Куки должны быть «безопасными» (secure) и httpOnly, а область видимости должна определятся атрибутами path и domain.
  • Используйте CSP (Content Security Policy), не допуская небезопасных бэкдоров. Муки с настройками стоят того.
  • Используйте заголовки X-Frame-Option, X-XSS-Protection в клиентских ответах.
  • Используйте механизм HSTS для принудительного доступа через TLS-протокол. Перенаправьте все HTTP-запросы на HTTPS на сервере для обратной совместимости.
  • Используйте токены CSRF во всех формах и новый заголовок ответа Cookie SameSite, который фиксирует CSRF единоразово для всех браузеров.

APIs


  • Убедитесь, что в API нет общедоступных ресурсов.
  • Убедитесь, что при использовании ваших API пользователи полностью идентифицированы и авторизованы.

Валидация


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

Облачная конфигурация


  • Убедитесь, что все сервисы имеют минимальное количество открытых портов. В то время как принцип «безопасность через неясность» (security through obscurity) не обеспечивает полной защиты, использование нестандартных портов немного усложнит жизнь злоумышленникам.
  • Размещайте базу бекэнда на частных VPC, которые не видны в публичной сети. Будьте очень осторожны при настройке групп безопасности AWS и пиринговых VPC — можно непреднамеренно сделать службы публичными.
  • Изолируйте логические службы в отдельных VPC и одноранговых VPC для обеспечения межсервисной связи.
  • Убедитесь, что все службы принимают данные только с минимального набора IP-адресов.
  • Ограничьте исходящий трафик IP и портов, чтобы минимизировать APT и «ботификацию».
  • Всегда используйте роли AWS IAM, а не учетные данные root.
  • Используйте минимальные права доступа для всех сотрудников и разработчиков.
  • Регулярно меняйте пароли и ключи доступа в соответствии с расписанием.

Инфраструктура


  • Убедитесь, что апгрейды делаются без простоя, а ПО обновляется автоматически.
  • Создавайте инфраструктуру с помощью инструментов вроде Terraform, а не через облачную консоль. Инфраструктура должна определяться как «код» и воссоздаваться одним нажатием кнопки.
  • Используйте централизованное логирование для всех служб. Не используйте SSH для доступа или получения логов.
  • Не используйте SSH в службах, кроме разве что разовой диагностики. Регулярное использование SSH, как правило, означает, что вы не автоматизировали все как надо.
  • Не оставляйте порт 22 постоянно открытым на любых сервисных группах AWS.
  • Создайте неизменяемые хосты вместо долгоживущих серверов, которые вы патчите и обновляете.
  • Используйте систему обнаружения вторжений для минимизации APT.

Эксплуатация


  • Выключите неиспользуемые службы и серверы. Самый безопасный сервер — это выключенный сервер.

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


  • Проводите аудит и проекта, и готовой реализации.
  • Проведите тестирование на проникновение — взломайте себя, а также попросите кого-то еще взломать вас.

Главное — планируйте


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

Минутка рекламы от редакции


Мы коммерческий блог, а потому не можем без ссылок:) В этот раз принесли две — на программы «Профессия веб-разработчик» и «Профессия frontend-разработчик».

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

Что изучается на курсе:
  • Кросс-браузерная верстка HTML и CSS.
  • Верстка веб-страниц на основе макета.
  • Бекэнд-разработка на PHP.
  • MySQL.
  • Проектирование структуры базы данных.
  • JavaScript.
  • AJAX.
  • Создание интерактивных веб-страниц.

Длительность обучение — 6 месяцев. Преподают специалисты из Яндекса, Медиа-Шторма, Cond'e Nast.

Старт занятий 23 июня. Все подробности здесь ->

Также идет набор на «Профессию frontend-разработчик» — курс, ориентированный на получение навыков frontend-разработки с нуля.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/329836/


Как я делаю бекапы. СУБД FireBird

Среда, 31 Мая 2017 г. 16:08 + в цитатник
image

Беда пришла откуда не ждали…
У клиента завис процесс “Касса”, так что не смог снять процесс через Диспетчер задач.
Рабочее место “Касса” — одновременно сервер всей системы.

Клиент принял решение ресетнуть через кнопку.

В итоге умерла DB. FireBird 2.5

Backup’ы настроены не были, так что последняя версия БД, которая случайно лежала у меня на винте, была минус 8 дней. Подняли по-быстрому с неё. Но суть дальше.

Как делать бекапы для FireBird.

Я написал скрипт, который, когда его запускают, делает резервное копирование базы данных с именем БазаДаннах_30_05_2017_23_07_51.fbk
@echo off
set "currentTime=%Time: =0%"
set now=%date:~-4%_%date:~3,2%_%date:~0,2%_%currentTime:~0,2%_%currentTime:~3,2%_%currentTime:~6,2%
  
set user=SYSDBA
set password=masterkey
set database_name=PARKDB.FDB
set backup_name=Backup\PARKDB
set ext=.fbk
 
set backup_filename=%backup_name%_%now%%ext%
echo %backup_filename%
 
nbackup -U %user% -P %password% -B 0 %database_name% %backup_filename%

%date:~3,2%

Это означает взять из результата date(Это системная функция Windows), 2 символа, начиная с 3 позиции в строке
Обычный %date% = 31.05.2017
set "currentTime=%Time: =0%"

Означает брать время, учитывая 0, когда часы меньше 10, то есть без этой команды, мы при выполнении команд:
set now=%date:~-4%_%date:~3,2%_%date:~0,2%_%currentTime:~0,2%_%currentTime:~3,2%_%currentTime:~6,2%

получили бы, что now=2017_05_31_ 0_44_33,

а имя файла выглядело бы так: Backup\PARKDB_2017_05_31_ 0_44_33.fbk

Пробел не сильно виден, но он неприемлем в названии файла для nbackup

дата и время берутся из текущего времени системы

Это вроде рассказал :).

далее, чтобы скрипт выполнялся постоянно,
я добавил задачу в планировщик задач виндовс,
он каждые 4 часа запускает мой скрипт
и создается новая БД,
далее я научился создавать задачи в планировщике задач через командную строку
@echo off
set script_name=e:\SoftBuild\Parking\DB\DB_Backup.bat
set task_name=LotParkingBackup
SCHTASKS /Create /SC DAILY /TN %task_name% /TR %script_name% /HRESULT /F /RI 240 /DU 24:00 /v1

Детали параметров можете посмотреть в хелпе. SCHTASKS /Create /?

потом добавил в инсталлятор создание скрипта, который делает то, что я описал только что, и запуск из инсталлятора
function NextButtonClick(CurPageID: Integer): Boolean;
var
  ServerHost, ServerPort, DBFileName, FBDirPath: string;
  ResultCode, ErrorCode: Integer;
  UDFFrom, UDFTo, ReaderPort: string;
  RegistryTaskFile, DBDirPath, BackupScriptPath, RegistryFileName: string;
begin
  if CurPageID = SettingsPage.ID then
  begin
    ServerHost := SettingsPage.Values[0];
    ServerPort := SettingsPage.Values[1];
    DBFileName := SettingsPage.Values[2];
 
    if IsComponentSelected(cDB) then
    begin
      DBDirPath := Copy(DBFileName, 1, Pos('PARKDB.FDB', DBFileName) - 1);
      BackupScriptPath := DBDirPath + 'DB_Backup.bat'
      RegistryTaskFile := '@echo off' + #13#10 + 
                          'set script_name=' + BackupScriptPath + #13#10 + 
                          'set task_name=LotParkingBackup' + #13#10 + 
                          'SCHTASKS /Create /SC DAILY /TN %task_name% /TR %script_name% /HRESULT /F /RI 240 /DU 24:00 /v1' + #13#10;
      
      RegistryFileName := DBDirPath + 'DB_RegistryBackup.bat';
      SaveStringToFile(RegistryFileName, RegistryTaskFile, False);
      
      Exec(ExpandConstant(RegistryFileName), '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
    end;    
  end;
end;

теперь у меня есть скрипт, который делает резервную копию,
и скрипт, который регистрирует задачу,
далее я научился удалять задачу из планировщика,
опять же из командной строки
@echo off
set task_name=LotParkingBackup
SCHTASKS /DELETE /TN %task_name% /F

и добавил в инсталлятор. что если мы делаем удаление программы Парковка, то нужно запустить скрипт, который удалит задачу из Планировщика
[UninstallRun]
Filename: "{app}\DB\DB_DeleteTask.bat"; WorkingDir: "{app}\DB\"; Flags: runhidden waituntilterminated; Components: DB

В итоге у нас каждые 4 часа есть бекап, и если мы делаем UnInstall, то всё чисто :)
Оригинал.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/329914/


Метки:  

[Перевод] CSS в JavaScript: будущее компонентных стилей

Среда, 31 Мая 2017 г. 16:05 + в цитатник


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


Чтобы получить больше информации о проблемах CSS, решаемых в JavaScript, вы можете посмотреть презентацию «React CSS в JS» (React CSS in JS), а для того чтобы изучить улучшение производительности с помощью Aphrodite, прочитайте статью Inline CSS at Khan Academy: Aphrodite. Если же вы хотите узнать больше о лучших практиках CSS в JavaScript, ознакомьтесь с руководством Airbnb (Airbnb’s styleguide).


Здесь речь пойдет об использовании встроенных стилей JavaScript для создания компонентов, позволяющих решить основные проблемы дизайна, о которых я рассказывал ранее в статье «Прежде чем осваивать дизайн, необходимо ознакомиться с основами» (Before you can master design, you must first master the fundamentals).


Мотивирующий пример


Начнем с простого примера: создание и стилизация кнопки. Как правило, компонент и связанные с ним стили находятся в одном файле: Button и ButtonStyles. Причина в том, что они относятся к одной и той же вещи — к представлению (view). Однако в примере я разбил код на несколько составляющих, чтобы сделать его более наглядным.


Рассмотрим элемент кнопки:


...

function Button(props) {
  return (
    
  );
}

Ничего необычного — просто React-компонент. Свойство className — вот где Aphrodite вступает в игру. Функция CSS принимает объект styles и преобразует его в CSS. Объект styles создается с помощью функции StyleSheet.create Aphrodite ({...}). Вы можете посмотреть результат StyleSheet.create ({...}) на странице Aphrodite (Aphrodite playground).


Ниже приведена таблица стилей кнопок:


...

const gradient = 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)';

const styles = StyleSheet.create({
  button: {
    background: gradient,
    borderRadius: '3px',
    border: 0,
    color: 'white',
    height: '48px',
    textTransform: 'uppercase',
    padding: '0 25px',
    boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .30)',
  },
});

Простая миграция и низкая кривая обучения — вот преимущества Aphrodite. Свойства border-radius преобразуются в borderRadius, а значения становятся строками. Псевдоселекторы, медиазапросы и определения шрифтов — все работает. Кроме того, автоматически добавляются вендорные префиксы.


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


image
Простая миграция и низкая кривая обучения — преимущества Aphrodite


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


Основной принцип № 1. Типографика


Начнем с фундаментальной основы дизайна — типографики. Для начала необходимо определить ее константы. В отличие от Sass или Less, константы для Aphrodite могут быть в файлах JavaScript или JSON.


Определение констант типографики


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


export const fontSize = {
  // heading
  displayLarge: '32px',
  displayMedium: '26px',
  displaySmall: '20px',
  heading: '18px',
  subheading: '16px',

  // body
  body: '17px',
  caption: '15px',
};

export const fontWeight = {
  bold: 700,
  semibold: 600,
  normal: 400,
  light: 200,
};

export const tagMapping = {
  h1: 'displayLarge',
  h2: 'displayMedium',
  h3: 'displaySmall',
  h4: 'heading',
  h5: 'subheading',
};

export const lineHeight = {
  // heading
  displayLarge: '48px',
  displayMedium: '48px',
  displaySmall: '24px',
  heading: '24px',
  subheading: '24px',

  // body
  body: '24px',
  caption: '24px',
};

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


Более подробно о вертикальном ритме вы можете прочитать в статье «Почему вертикальный ритм — это важная практика типографики?» (Why is Vertical Rhythm an Important Typography Practice?).


image
Используйте калькулятор для определения высоты линии


Существует целая наука о выборе значений для строк и размеров шрифта. Для создания потенциальных размерных вариантов могут использоваться математические соотношения. Несколько недель назад я опубликовал статью «Типографика может создать или разрушить дизайн: выбор типа» (Typography can make or break your design: a process for choosing type), в которой подробно изложена методология. Для определения размеров шрифтов используйте модульную шкалу (Modular Scale), а чтобы установить высоту линий, можно применять калькулятор вертикального ритма (vertical rhythm calculator).


Определение компонента заголовка


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


import React, { PropTypes } from 'react';
import { StyleSheet, css } from 'aphrodite/no-important';
import { tagMapping, fontSize, fontWeight, lineHeight } from '../styles/base/typography';

function Heading(props) {
  const { children, tag: Tag } = props;
  return {children};
}

export default Heading;

export const styles = StyleSheet.create({
  displayLarge: {
    fontSize: fontSize.displayLarge,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.displayLarge,
  },
  displayMedium: {
    fontSize: fontSize.displayMedium,
    fontWeight: fontWeight.normal,
    lineHeight: lineHeight.displayLarge,
  },
  displaySmall: {
    fontSize: fontSize.displaySmall,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.displaySmall,
  },
  heading: {
    fontSize: fontSize.heading,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.heading,
  },
  subheading: {
    fontSize: fontSize.subheading,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.subheading,
  },
});

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


...
export const tagMapping = {
  h1: 'displayLarge',
  h2: 'displayMedium',
  h3: 'displaySmall',
  h4: 'heading',
  h5: 'subheading',
};

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


export const styles = StyleSheet.create({
  displayLarge: {
    fontSize: fontSize.displayLarge,
    fontWeight: fontWeight.bold,
    lineHeight: lineHeight.displayLarge,
  },

  ...
});

А компонент Heading будет применяться следующим образом:


function Parent() {
  return (
    Hello World
  );
}

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


Основной принцип № 2. Интервал


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


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


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


const spacingFactor = 8;
export const spacing = {
  space0: `${spacingFactor / 2}px`,  // 4
  space1: `${spacingFactor}px`,      // 8
  space2: `${spacingFactor * 2}px`,  // 16
  space3: `${spacingFactor * 3}px`,  // 24
  space4: `${spacingFactor * 4}px`,  // 32
  space5: `${spacingFactor * 5}px`,  // 40
  space6: `${spacingFactor * 6}px`,  // 48

  space8: `${spacingFactor * 8}px`,  // 64
  space9: `${spacingFactor * 9}px`,  // 72
  space13: `${spacingFactor * 13}px`, // 104
};

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


Золотое сечение(1:1.618)
8.0 x (1.618 ^ 0) = 8.000
8.0 x (1.618 ^ 1) = 12.94
8.0 x (1.618 ^ 2) = 20.94
8.0 x (1.618 ^ 3) = 33.89
8.0 x (1.618 ^ 4) = 54.82
8.0 x (1.618 ^ 5) = 88.71

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


const spacingFactor = 8;
export const spacing = {
  space0: `${computeGoldenRatio(spacingFactor, 0)}px`,  // 8
  space1: `${computeGoldenRatio(spacingFactor, 1)}px`,  // 13
  space2: `${computeGoldenRatio(spacingFactor, 2)}px`,  // 21
  space3: `${computeGoldenRatio(spacingFactor, 3)}px`,  // 34
  space4: `${computeGoldenRatio(spacingFactor, 4)}px`,  // 55
  space5: `${computeGoldenRatio(spacingFactor, 5)}px`,  // 89
};

function computeGoldenRatio(spacingFactor, exp) {
  return Math.round(spacingFactor * Math.pow(1.618, exp));
}

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


Например, добавим marginBottom к компоненту Button.


import { spacing } from '../styles/base/spacing';

...

const styles = StyleSheet.create({
  button: {
    marginBottom: spacing.space4, // adding margin using spacing constant
    ...
  },
});

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


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


import React, { PropTypes } from 'react';
import { spacing } from '../../base/spacing';

function getSpacingSize(size) {
  return `space${size}`;
}

function Spacing(props) {
  return (
    
{props.children}
); } export default Spacing;

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


Это работает за счет таких компонентов, как кнопки, инпуты и карточки (cards), которые могут нуждаться в margin, заданном переменными. Например, для кнопки формы может потребоваться большее поле, чем для кнопки панели навигации.


Вы, наверное, заметили, что в приведенных примерах используется только marginBottom. Это связано с тем, что определение всех вертикальных полей в одном направлении позволяет избежать их сбрасывания, а также дает возможность отслеживать вертикальный ритм дизайна. Узнать об этом больше вы можете из статьи Гарри Роберта «Описание одностороннего поля» (Single-direction margin declarations).


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


import React, { PropTypes } from 'react';
import { StyleSheet, css } from 'aphrodite/no-important';
import { spacing } from '../../styles/base/spacing';

function Card(props) {
  return (
    
{props.children}
); } export default Card; export const styles = StyleSheet.create({ card: { padding: spacing.space4}, // using spacing constants as padding background: 'rgba(255, 255, 255, 1.0)', boxShadow: '0 3px 17px 2px rgba(0, 0, 0, .05)', borderRadius: '3px', }, });

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


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


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


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

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

https://habrahabr.ru/post/329710/


Метки:  

PHDays VII: хроники «Противостояния»

Среда, 31 Мая 2017 г. 15:41 + в цитатник

Метки:  

Must see: видеозаписи митапа MoscowJS 37

Среда, 31 Мая 2017 г. 15:38 + в цитатник
image

В четверг, 25 мая, в офисе Avito прошла очередная встреча сообщества фронтенд-разработчиков MoscowJS. Обсуждали отладку анимации, создание WebGL визуализации, сборку webpack’ом и код-ревью. Сегодня публикуем видеозаписи докладов — профессионалам будет интересно. Приятного просмотра!

Встречу открыл Александр Черников (Сбербанк Технологии), который рассказал про то, как их проект, начинавшийся с jQuery и React (es5), дорос до TypeScript'а и Node.js: out of memory.





Иван Стрелков из Avito поделился своим опытом проведения код-ревью. Как разобраться в большом pull request’е и не сойти с ума, какие инструменты и подходы можно для этого использовать, — смотрите в его докладе.





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





В закрывающем докладе Александр Амосов рассказал, как реализовать интерактивную 3D-карту своими руками, начиная от создания модели в трехмерном редакторе и заканчивая оптимизациями из мира компьютерных игр.





По хорошей традиции, митап завершился общением в барчике (и в этот раз почти без ноутбуков).

image

Спасибо всем, кто пришёл на митап и смотрел живую трансляцию! Следить за анонсами следующих встреч MoscowJS можно на сайте сообщества. Фото с мероприятия уже лежат в нашей группе в FB, а плейлист со всеми докладами можно найти на нашем канале.

До новых встреч!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/329898/


Метки:  

[Из песочницы] I want to break free. Чему и как учиться, чтобы сбежать от 1С

Среда, 31 Мая 2017 г. 15:35 + в цитатник
“I want to break free. I want ro breeeeeaaaak freeeeeee…”
Фреди Меркури.

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


Предыстория или как я попал в секту “Свидетели 1С”.


image


В 9 классе в далеком 99 году, чтобы отвлечь сына от игр на (3-м? 4-м?) пентиуме, родители привели меня в “кружок программирования для старшеклассников”. Там же, спустя пару месяцев родился мой первый код — цикл, который гонял по экрану мигающий квадрат. Потом благодаря стараниям преподавателя был кратковременный скачок в html, где все просто и понятно и Java, где понимал чуть больше, чем ничего. На этом история прервалась лет так на 5, потому что с конца 10 класса началась одна большая подростково-студенческая синяя яма, которая продолжалась примерно до 3-го курса факультета экономики Челябинского университета. Ведь именно тогда отец, предпринял очередную попытку “направить на пусть истинный” раздолбая. Будучи бухгалтером и пользователем начинающей тогда набирать обороты 1С: Бухгалтерия, он, судя по всему почувствовав “рынок”, затащил меня на 4-х недельные курс по “по конфигурированию в 1С 7.7”. И меня зацепило. Через месяц еще одни курсы и вот я уже подмастерье в маленьком франчайзи из 2 двух человек (я второй). В итоге, после окончания 5-го курса в 2006 году я смог устроится адинесником в местный ретейл с просто космической для меня на тот момент зарплатой в 25т.р.


Почему я хочу сбежать.


image


Сейчас 2017 год, мне 33, жена и сын 5 лет, я все тот же адинесник, но уже уровня “150т.р. белыми на руки в Москве”. Я в карьерно-профессиональном тупике. Мои текущие умения, знания и способности не дадут мне того, что я хочу. У меня уже близок потолок в части зарплаты и чтобы прыгнуть дальше — нужные качественные изменений, которые потребуют от меня качеств, которых возможно у меня нет (лидерские, управленческие, качестве бизнесмена). Да, у меня есть фундамент для становления руководителем проектов, но не уверен, что мне это понравится и что я смогу там стать лучшим. Мне нравится программировать.


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


Через 5 лет:


  • я должен стать профессионалом в “международном“ языке программирования;
  • я должен стать профессионалом, который может претендовать на 100 т.дол. в год в США, Канаде, Австралии, Англии;
  • я должен стать профессионалом, который может претендовать на работу в компаниях уровня Гугл, Фейсбук, Эпл;

Озвученные цели могут дать представление, что в данном контексте “сбежать от 1С = сбежать из России”. Но я патриот. Даже если придется уехать из страны, чтобы работать в Купертино, то я все равно будут приезжать каждое лето на дачу к родителям.


Как я планирую сбежать.


image


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


  • Попробовать различные области и языки программирования, чтобы найти то, что по душе и что соответствует запросам рынка (2-3 месяца);
  • Потратить год на самообразование в свободное от работы время, чтобы достичь уровня джуниора, который позволит мне устроиться в Москве на 80-90т (цифра взята как минимально необходимая для выживания, но не факт что смогу получить);
  • Потратить год для становления мидл с уровнем зарплаты 120т;
  • Потратить два года для возможной корректировки направления внутри выбранного языка и становления сеньором с уровнем зарплаты 150-170;
  • Начиная с 3-го года начать поиск работы в иностранной компании;
  • Начиная с 5-го поиск работы “отстатыщ”;

Подготовка к Jailbreak’у.


Первые метры подкопа на волю я прокопал в сентябре 2015 года, когда с подачи своего бывшего коллеги обратил внимание на быстрорастущий в последнее время рынок бигдаты и машинного обучения. С его же подачи я открыл для себя Coursera и “специализацию” по бигдате от одного из американских университетов, которая по моему собственному понимаю, должна была меня сделать тем самым джуниором. За 4 месяца было освоено 4 курса из 9 (в среднем один курс рассчитан на 1 месяц и стоит 50 дол). В то время я был просто окрылен — относительно неплохо построен процесс обучения, хорошие материалы, свободный график, интересная подача. Все шло как по маслу, тем более что в качестве языка использовался R, который на самом деле, после небольшого ознакомления с синтаксисом, даже после 13 лет кодинга в 1С воспринялся относительно легко. Камнем же преткновения стал 5й курс по статистике, когда стали “грузить” серьезным материалом на не родном мне языке. С учетом того, что статистика в моем родном университете попала на 3-й “алкогольный” курс и среднее арифметические, скользящие, взвешенные я стал отличать только в контексте расчета себестоимости будучи уже программистом 1С, это стало для меня серьезным препятствием. Я решил взять паузу и заняться изучением основ статистики на английском языке и… на том и остановился. Однако к этому времени я уже был в состоянии написать скрипт, который мог “сграббить” один небольшой форум и вывести по нему небольшую стату (пример одной из моих игр в песочнице).


Не помню что меня толкнуло на следующие пару метров, но полгода назад (в декабре 2016 года) я наткнулся на приложение iTunes U и лекции Стэнфордского университета по языку Swift. Был еще раз приятно удивлен качеством материала, манерой подачи, и что все это было в (внимание!) бесплатно. За несколько дней я познакомился с моделью MVC, стал погружаться в настоящее ООП с инхеритансами и прочими прелестями и через неделю уже был готов мой первый калькулятор. Однако и тогда недостаток свободного времени и, возможно, мотивации сделал свое дело и я поставил все на hold.


Третья волна накрыла меня месяц назад (апрель 2017), которая как раз и вылилась в формализацию целей, в детализацию планов, в очередную специализацию с курсеры и в этот очерк. За 2,5 недели я практически осилил 2 курса из 5 по Ruby on Rails, занимаясь после работы и все выходные с утра до вечера. Не могу сказать, что за это время привык к синтаксису, да и методичку приходится заглядывать регулярно. В настоящий момент понял, что мне трудно усвоить кое-какие моменты и нужно те же самые блоки просмотреть в других источниках и в другом изложении. Так как непосредственно в самом курсе анонсирование ознакомление с JavaScript, то скорее всего следующий курс, который я возьму после того как допишу свой собственный Твиттер с блекджэком и лайками, будет по JavaScript. Потом Python, потом Java.


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


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


  2. Источников и материалов чуть меньше чем дофига и все они разного качества и разной доступности. На той же курсере (если не ошибаюсь) почти все курсы можно пройти в качестве слушателя бесплатно. На том же Udemy бесплатно ничего не получишь (насколько я понял). Сегодня пришлось выложить 10 долларов за курс по Ruby on Rails, который надеюсь позволит мне уложить в голове материал, на котором я застрял в первоначальном курсе. Также попробуйте поискать нужные материалы на iTunes U (как минимум курс по Swift того стоит).


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


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


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

Удачи!


P.S. пока все что языки про языки, среди которых предстоит выбрать — R простой как две копейки, Python для детей, Ruby для наркоманов, JavaScript

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

https://habrahabr.ru/post/329906/


Метки:  

Роутеры, векторы атак и другие приключения Шурика

Среда, 31 Мая 2017 г. 15:15 + в цитатник

Не так давно всего за один месяц свет увидели около 10 уязвимостей, связанных с получением root shell или админских учетных записей на домашних сетевых устройствах. Wi-Fi и 3G-роутеры с админскими учетками вокруг нас. Заплатив 50 $ за платный аккаунт на shodan.io, любой желающий получает доступ к десяткам миллионов IoT-устройств. В их числе роутеры, веб-камеры и прочие гаджеты. С другой стороны, разработчики и поставщики сетевых устройств не придают значения таким понятиям, как «тестирование» и «безопасность». Многие серьезные уязвимости остаются без патчей, а если патчи все-таки выходят, то пользователи не торопятся их применять. Что в результате? Легионы устройств ждут своего часа, чтобы быть взломанными и стать ботами для DDOS-атак.

Предыстория


В августе 2016 года мир познакомился с ботнетом под названием Mirai. Исследователи из группы MalwareMustDie начали изучать зловредную сетевую активность IoT-девайсов в начале августа, а уже 20 сентября ботнет примерно из 150 000 устройств (в основном DVR и IP-камер) атаковал серверы Minecraft, расположенные у французского провайдера OVH.

Заражение IoT-устройств осуществлялось посредством подбора учетной записи Telnet на портах 23 или 2323 по списку из 62 стандартных паролей. IP-адреса генерировались абсолютно случайно по всему диапазону, и после включения в сеть каждое зараженное устройство начинало сканирование этих рандомных адресов. Код, управляющий ботнетом, не хранился в долговременной памяти и, следовательно, не мог пережить перезагрузку устройства. Однако, учитывая скорость, с которой боты сканировали интернет, после перезагрузки устройство вскоре вновь включалось в состав ботнета.

Далее последовали крупнейшие атаки на журналиста Брайана Кребса, на DynDNS, Либерию, немецкого оператора Deutsche Telekom и один из колледжей в США. Исходный код ботнета Mirai был опубликован в начале октября и атака на немецкого оператора двумя месяцами позже уже велась с использованием модифицированной версии Mirai, эксплуатировавшей уязвимость сервера RomPager на порте 7547 (протокол CWMP).

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

Что-нибудь кроме веб-камер?


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

В то время как ботнет вбирал в себя преимущественно видеокамеры и другие IoT-девайсы, у которых служба Telnet дает доступ к Linux-командам, — служба Telnet на роутерах предоставляла доступ лишь к CLI настройки. Такой CLI позволяет читать и менять конфигурацию устройства, настройки DNS, ip route и системную информацию, что уже само по себе позволит провести некоторые атаки, но не позволяет установить ПО для удаленного управления.

Вот такую греющую душу защиту от ботов можно обнаружить на порте 23:
image

Однако отсутствие bash-терминала не означает отсутствия других векторов атак.

Что собой представляет среднестатистический домашний роутер? Это:

  • доступная извне веб-панель с агрессивным дизайном;
  • read-only файловая система squashfs и ~10 МБ флеш-памяти;
  • конечно же, busybox (компактный UNIX-интерфейс командной строки с набором утилит);
  • веб-сервер micro http, SSH-сервер DropBear;
  • открытые порты 80, 443, 23, 22, 21, 137.



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

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


Получив доступ к веб-панели, атакующий также получает уникальную возможность испортить жизнь всем пользователям по ту сторону, проводить DNS Spoofing и исследовать внутреннюю сеть. При большой удаче он способен еще и вызывать с веб-панели такие команды, как ping или traceroute, найти уязвимость в коде веб-сервера для получения шелла или воспользоваться одной из уже найденных.

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

  • NETGEAR DGN2200v1/v2/v3/v4 — 'ping.cgi' RCE (link)


    Недостаточность проверок в ping.cgi приводит к тому, что введенный IP-адрес от пользователя попадает прямо в bash. Выполнить произвольные команды в терминале можно просто дописав их в поле IP-адреса в POST-запросе. Вот так: 12.12.12.12; nc -l 4444 -e /bin/bash. Вместо nc можно добавить более функциональную нагрузку, например msfvenom. 3000 девайсов ждут своего часа, чтобы раскрыть потенциал. Эксплуатация потребует авторизации на веб-интерфейсе.

  • Multiple vulnerabilities in GoAhead WIFICAM cameras (link)


    Многочисленные уязвимости были обнаружены более чем в 1250 моделях IP-камер, в зоне риска находятся около 130 000 камер. Ошибка в реализации кастомного механизма авторизации, которая позволяет получить админский пароль, и уязвимость типа OS Command Injection на странице set_ftp.cgi, которая позволяет выполнять любые терминальные команды, вместе дают полный контроль над устройством без каких-либо ограничений. Впечатляет! Использование этой уязвимости добавил в своей арсенал ботнет TheMoon, известный с 2014 года. В ходе исследования были обнаружены зараженные камеры, в файл settings.ini которых был добавлен скрипт, загружающий зловредный код с сервера злоумышленников при загрузке устройства.


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


    который 17 из 54 антивирусов идентифицируют как Linux/Proxy.

  • Linksys Smart Wi-Fi Vulnerabilities (link)


    По результатам исследования 25 моделей активно продаваемых по всему миру роутеров из линейки Linksys Smart Wi-Fi оказались подвержены 10 уязвимостям различной степени опасности. В числе уязвимостей и те, что позволяют выполнять произвольные команды с правами root. Несмотря на то, что суммарно с помощью Shodan можно обнаружить лишь около 1000 единиц, исследователи говорят более чем о 7000 просканированных устройств.

  • Siklu EtherHaul Unauthenticated Remote Command Execution Vulnerability (<7.4.0) (link)


    Эти серьезные железки от компании Siklu предоставляют абонентам связь через миллиметровый диапазон радиоволн на частотах 70/80 ГГц. Исследователь обнаружил, что таинственный порт 555 на них служит для коммуникации с другими Siklu EH. Прибавив к этому отсутствие ограничений на доступ к порту и хранение паролей в открытом виде, он получил эксплойт для смены админского пароля. Этой ошибке в архитектурном дизайне был присвоен свой номер в системе CVE: CVE-2017-7318.

  • Bypassing Authentication on iball Baton Routers (link)


    Хоть админка устройства iBall Baton 150M и надежно закрыта HTTP-авторизацией, но страница password.cgi видна всем желающим. Очень похоже, что разработчики забыли об этом факте и хранили пароли от всех трех аккаунтов на устройстве в открытом виде в скрипте HTML-страницы; 1500 админских паролей в открытом доступе!


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

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

Альтернативные векторы угроз


Кроме веб-интерфейса, на роутере в среднем открыто 4–5 портов. В их числе Telnet (23), SSH (22) и FTP (21).

Как показывает практика, сервис Telnet служит для доступа к CLI настройки роутера, а FTP — для удаленного обновления прошивки. Например, на 18 000 модемов D-Link DSL 2750U при подборе учетных записей кто угодно обладает возможностью накатить прошивку со встроенным бэкдором. Таким образом он получит ботов, которые уж точно переживут перезагрузку и вряд ли будут взломаны кем-либо еще. Алгоритм действий следующий:

  1. Скачать прошивку для этого устройства с сайта производителя D-Link.
  2. Распаковать.
  3. Дополнить ее backdoor-аккаунтом или скриптом, запускающим bind shell. На этом шаге атакующий может проявить фантазию и воплотить любой из своих способов получения шелла.
  4. Собрать франкенштейна обратно, используя доступные инструменты. Например, firmware framework.
  5. Обновить прошивку устройства через FTP.



Кроме FTP-обновления у D-Link, обновлять можно и через Telnet (доступно 140 тысяч CLI) или через веб-панель. DNS Hijack на этом фоне выглядит совсем несерьезно.

Недавние атаки на устройства Eir D1000 были связаны с уязвимостью OS Command Injection в реализации CWMP — TR-064. Результатом стало заражение около 900 000 устройств модифицированной версией Mirai. Другая уязвимость в сервере RomPager <4.34 под собственным названием Misfortune Cookie (CVE-2014-9222) имеет наивысшее значение CVSS 10.

А тем временем чуть меньше половины всех CWMP используют уязвимый RomPager 4.07. Это почти 2 500 000 доступных устройств. Check Point на RSA 2017 представили собственное исследование проблем безопасности TR-064.

RomPager 4.07 — это не единственный древний сервис, который используют поставщики прошивок. Genivia gSOAP 2.7 была выпущена в 2004 году, а DropBear SSH 0.46 увидел свет во второй половине 2005 года.


Для DropBear известны как минимум несколько уязвимостей (DoS и Authenticated RCE).

Исследователи Bertin Jose и Fernandez Ezequiel 4 апреля опубликовали отчет, согласно которому 18 вендоров, 78 моделей и более полумиллиона устройств содержат ошибку в SNMP-агенте, которая позволяет любому желающему получить полный read/write-доступ над всеми значениями. Ошибка в том, что агент попросту не проверяет community string: любые комбинации проходят авторизацию. Уязвимость получила модное название StringBleed и подвержены ей в основном домашние кабельные модемы, хотя исследователи говорят об обнаружении и других систем с подобной уязвимостью. Последствия такие же, как при отсутствии авторизации вовсе.

И напоследок, 9 из 1000 роутеров предоставляют бесплатный DNS-сервер со включенной по умолчанию рекурсией. Техника эксплуатации известна давно и будет существовать до тех пор, пока по умолчанию DNS на роутерах не перестанет отвечать на запросы из интернета.

Заключение


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

Способы борьбы с ботнетами просты (перечисляем по мере убывания эффективности):

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

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

Доклад был представлен на третьей встрече нижегородского сообщества Defcon NN и все любители живого повествования могут посмотреть его видеозапись:



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

https://habrahabr.ru/post/329902/


[Из песочницы] Первоначальная настройка Puppet — не все так просто, как кажется

Среда, 31 Мая 2017 г. 14:53 + в цитатник

image


Всем привет! В данной статье хочу поделиться с вами впечатлениями от настройки Puppet для конфигурации Windows-серверов.


В целом, задача заключалась в следующем: требовалось организовать процесс автоматической доставки и установки патчей для обновления удаленных серверов. Выбирали из известных менеджеров конфигураций, главное требование к которым — поддержка Windows на высоком уровне. Сразу скажу, что от Puppet решили отказаться из-за высокого порога вхождения, pull подхода и необходимости знания Ruby. Но уж очень хочется поделиться тем, что получилось.



Введение


Puppet обладает коммуникационной моделью (архитектурой) «мастер-агент», причем операционной системой мастера не может быть Windows. Для управления Windows-агентами, на целевых машинах должно быть установлено ПО, которое представляет собой .msi установщик.


Puppet оперирует хостнеймами и взаимодействие между мастером и агентами осуществляется по протоколу HTTPS, из чего следует, что у агентов и мастера есть такие зависимости, как веб-сервер Apache, а также не удастся избежать взаимодействия с сертификатами и их настройки.


Тестовая инфраструктура


  • Виртуальная машина с ОС Ubuntu 16.04 LTS для установки Puppet-мастера.
  • Целевые машины с ОС Windows, которыми, собственно, и нужно управлять.

Установка


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


Мастер


Установка мастера описана здесь. Единственное, что во время установки не завелось – после перегенерации SSL сертификатов Apache перестал запускаться.


image![image](https://imagebin.ca/v/3NbWzYZ942C3)


После обращения к журналу событий становится понятно, в чем проблема – сертификата с таким именем, как в конфигурационном файле /etc/apache2/sites-enabled/puppetmaster.conf, не существует. Заходим, исправляем имя (в моем случае просто puppet), готово. Кстати, посмотреть на сертификат мастера можно здесь — /var/lib/puppet/ssl/certs.


Агенты


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


image


Конкретнее о многих из этих компонентов можно узнать из документации. Далее указываем адрес мастера и готово.


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


image


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


puppet cert list --all
cert sign 

Пример Puppet-манифеста


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

# создаем класс с описанием того, что файл должен существовать
# и иметь определенное содержимое
class action::windows {
    file { 'c:\\Temp\\foo.txt':
        ensure   => present,
        content  => 'This is some text in my file'
    }
}

# класс для обработки всех машин, кроме Windows
class action::default {
    notify{ "Operating system $::operatingsystem not supported": }
}

# анализируем факт osfamily
# и в зависимости от этого выполняем нужные действия
case $::osfamily {
    'windows': { include action::windows }
    default: { include action::default }
}

Чтобы применить манифесты на мастере: puppet apply .
На агентах: puppet agent --test.


Заключение


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


Полезные источники


  1. ssl background
  2. puppet agent/master communications
  3. puppet architecture overview
  4. puppet official modules
  5. installation
  6. tutorial presentation
  7. mcollective

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

https://habrahabr.ru/post/329900/


Метки:  

Acronis Backup 12.5 (теперь и) Advanced: долгожданный выпуск

Среда, 31 Мая 2017 г. 14:15 + в цитатник

Предыстория


Решение Acronis Backup версии 11.5/11.7 было выпущено более 5 лет назад и, хотя данный продукт до сих пор успешно защищает данные по всему миру, он уже успел морально устареть и назрели объективно необходимые изменения, требующие принципиально новых подходов. Несмотря на то, что платформа Acronis Backup 12, удовлетворяющая новым веяниям, была выпущена уже около года назад, её редакция «Advanced» всё это время оставалась на версии 11.7, что привносило проблемы совместимости и сложности одновременной поддержки очень разных архитектурных решений. Наконец-то настал момент, когда вся линейка продуктов Acronis Backup будет доступна на единой платформе. Данная статья посвящена выпуску Acronis Backup 12.5 и, собственно, что же нового было добавлено в этой версии.

Напомним, что решение Acronis Backup 12.5 поставляется в двух вариантах: стандартном (Standard) и расширенном (Advanced), доказало свою эффективность как полномасштабная система резервного копирования для гибридных сред. Простой и удобный веб-интерфейс обеспечивает надежное резервное копирование и восстановление физических, виртуальных и облачных серверов из любой точки мира.



Зачем новая версия? И так же всё хорошо! (нет)


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

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

Пример библиотек
В качестве примера таких сторонних компонентов могу привести переход VMware VDDK, начиная с 6.0 версии, в формат исключительно х64, что сильно затруднило поддержку новых версий VMware vSphere (6.5 в частности) для нашей предыдущей версии Acronis Backup 11.7 Advanced.

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

Что же нового?


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

Разницу между Acronis Backup 12.5 Standard и Advanced по предоставляемой функциональности можно посмотреть здесь.

Независимые планы по работе с данными


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



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

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

Частью этих изменений стало расширение возможностей валидации резервных копий. До версии 12.5 валидация выполнялась только следующим способом:

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

Расширение заключается в новой опции валидации через запуск виртуальной машины из архива. Это будет работать при условии наличия гипервизора VMware vSphere или Microsoft Hyper-V в том числе и для резервных копий физических машин, а не только виртуальных. Другими словами мы можем запускать виртуальную машину на выбранном гипервизоре независимо от того, с какой платформы был снят образ — всё благодаря технологии Acronis Universal Restore, применяемой на лету.

В первой версии проверка валидности запущенной виртуальной машины осуществляется проверкой «heartbeat» отклика от VMware Tools или Hyper-V Integration Services после её старта, т.е. как минимум убеждаемся в том, что гостевая ОС загрузится при восстановлении.
В ближайших обновлениях доступные проверки будут расширены до выполнения скриптов внутри запущенной ВМ, помимо «heartbeat».

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


Иерархическая модель управления


Добавлена возможность создания «Отделов» (так называемых «Unit»-ов) в рамках организации с делегацией управления выделенным администраторам.

Работает это следующим образом: главный администратор устанавливает Acronis Backup 12.5 и в интерфейсе веб консоли создает «Отделы» под свою организацию, например под офисы в Москве и Санкт-Петербурге.



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

Добавление администратора(ов) Отдела


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

Привязка ресурсов происходит на уровне агентов резервного копирования, т.е. если зарегистрировать допустим Agent for VMware (или Agent for Hyper-V) в определенной группе, то все виртуальные машины, которые «видны» данному агенту, будут автоматически включены в этот «Отдел» (разделить виртуальные машины на отдельные подгруппы пока не представляется возможным, и это мы планируем реализовать в ближайшем будущем).

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

Переключение между Отделами


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

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

Пример лога аудита


Панели мониторинга, предупреждения и отчеты


Слушая отзывы по панели мониторинга (Dashboard) в версии 11.7, мы видели, что она обладает недостаточной информативностью и иногда чрезмерно «спамит» предупреждениями (alerts aka «алертов»), поэтому в версии 12.5 данная панель была существенно переработана.

В частности, для системы «алертов» было сделано следующее:

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

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

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



На этом же «движке» Acronis Monitoring работает и построение отчетов, которые также являются интерактивными, настраиваемыми для каждого «Отдела» с возможностью регулярной посылки сводок по e-mail или сохранения в виде .pdf.

Отчет Сводка


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


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

Как это работает: при создании загрузочного диска с помощью Acronis Media Builder доступна опция по указанию скриптов для автоматического выполнения операций резервного копирования и восстановления сразу после загрузки.



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

Настройки соединения с Management Server в Media Builder


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

Меню скриптов


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

Если же загрузиться в GUI интерфейс, то машина автоматически зарегистрируется на Acronis Management Server-е и станет доступна для управления:

Управление машиной загруженной с диска

Вид со стороны подключенной машины:


Acronis Active Protection


Эта функциональность впервые увидела свет в Acronis True Image 2017 в начале этого года, и для чего она нужна лучше всего описано в нашей предыдущей статье, поэтому останавливаться на ней подробно мы не будем.

Вкратце Acronis Active Protection стал теперь доступен и в корпоративной линейке продуктов, а не только для домашних пользователей.

Применяем план Acronis Active Protection к машине с установленным внутри Agent for Windows:

Настройки Acronis Active Protection



В результате на машине запускается соответствующий Acronis Active Protection Service сервис и начинает отслеживать несанкционированные модификации файлов с помощью специального драйвера над файловой системой.

Acronis Notary и Acronis ASign


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

Данные инструменты уже были доступны в Acronis True Image 2017 New Generation, и теперь нашли своё место и в корпоративных решениях.

Acronis Notary, как это работает: при создании резервной копии отдельных файлов/папок создается дерево хэшей — по одной записи в дереве для каждого защищаемого файла и это дерево записывается в архив. Затем вычисляется хэш этого дерева и через веб-сервис notary.acronis.com добавляется в блокчейн базу данных Ethereum. При проверке файла, запрашивается нужный хэш из Ethereum и сравнивается с вычисленным относительно хэша дерева в архиве.

Включение нотаризации в плане


Помимо проверки файла из интерфейса веб-консоли, доступно скачивание сертификата, который генерируется на основании данных в архиве и данных от сервиса notary.acronis.com (который в свою очередь формирует запросы к Ethereum), что позволяет выполнить проверку копии файла не только в архиве, но и в любом другом месте.

Проверка файла в интерфейсе веб консоли
Находим нужный файл в архиве:

Результаты проверки:


Сразу отмечу особенности:

  • «Нотаризация» работает только для файловых архивов. Т.е. архив, включающий в себя диски или разделы, не подлежит «нотаризации».

  • При выполнении задания резервного копирования со включенной «нотаризацией» и при проверке необходим доступ в Интернет до notary.acronis.com (который по сути формирует запросы к базе Ethereum)

Acronis ASign, в свою очередь, позволяет отправлять электронные документы на подпись сразу нескольким лицам по e-mail, формируя для этого специальную страницу на публичном сервисе Asign.acronis.com, где подлинность факта подписи этими пользователями фиксируется и удостоверяется с помощью нотаризации, т.е. всё той же технологии Acronis Notary. Выполняется данная операция только с архивами, лежащими в облаке Acronis, и может инициироваться с помощью консоли веб-рестора, где у каждого документа из резервной копии доступна опция “Отправить на подпись”.

Заключение


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

P.S. Тестирование производительности резервного копирования — это отдельная большая тема, и мы её постараемся раскрыть в следующих статьях.

Ссылки


  1. Страница загрузки продукта
  2. Сравнение Standard и Advanced
  3. Ethereum — платформа для создания децентрализованных онлайн-сервисов на базе блокчейна
  4. FAQ по лицензионной политике и обновлению с предыдущих версий: английская и русская версии
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/327908/


Метки:  

Рецепты под Android: Scroll-To-Dismiss Activity

Среда, 31 Мая 2017 г. 14:07 + в цитатник

Привет! Сегодня мы расскажем, как за минимальное количество времени добавить в свою Activity поведение Scroll-To-Dismiss.
Scroll-To-Dismiss – это популярный в современном мире жест, позволяющий закрыть текущий экран и вернуться в предыдущую Activity.



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


Что имеем?


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


Несмотря на то, что функционально список статей и чтение статьи были уже декомпозированы в отдельные соответствующие фрагменты, это не спасало. Так как сами фрагменты требовали от хостящей их Activity иметь определенный интерфейс и реализацию (как это обычно и бывает с фрагментами). Помимо этого, UI этих экранов довольно сильно различался: разный набор кнопок меню, разное поведение тулбара (Behavior).


Суммарно это все делало объединение двух экранов в один ради одного дизайнерского твика иррациональным.



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


  • BottomSheetFragmentDialog. Это новый FragmentDialog, доступный в дизайнерской библиотеке от Google. Его поведение весьма похоже на нужное нам, но его все равно нужно хостить в рамках одной с контентом Activity, чего мы не хотим. Более того, BottomSheetFragmentDialog требует в layout-е наличия CoordinatorLayout, что вам может не понравиться. А еще этот диалог можно "смахнуть" только вниз.
  • Библиотеки. Android comunity богато на библиотеки на все случаи жизни. Для scroll-to-dismiss тоже нашлась парочка: SwipeBack и android-slidingactivity.

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


Движение – это жизнь


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


Примеры кода будут на Kotlin, потому, что он компактнее :)


abstract class SlidingActivity : AppCompatActivity() {

    private lateinit var root: View

    override fun onPostCreate(savedInstanceState: Bundle?) {
        super.onPostCreate(savedInstanceState)
        root = getRootView() // попросим наследника дать нам корневой элемент иерархии
    }

    abstract fun getRootView(): View
}    

Далее научимся слушать и реагировать на жесты пользователя. Можно было бы обернуть корневой элемент в свой контейнер и отслеживать действия в нём, но мы пойдем другим путем. В Activity можно переопределить метод dispatchTouchEvent(...), который является первым обработчиком касаний экрана. Заготовку обработчика вы можете видеть ниже:


abstract class SlidingActivity : AppCompatActivity() {

    private lateinit var root: View

    override fun onPostCreate(savedInstanceState: Bundle?) {
        super.onPostCreate(savedInstanceState)
        root = getRootView()
    }

    abstract fun getRootView(): View

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {

        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                // запомним начальные координаты
            }
            MotionEvent.ACTION_MOVE -> {
                // определим, куда двигается палец и нужно ли сдвигать контент
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                // закроем Activity, если контент "сдвинут" 
                // на значительное расстояние или вернем все как было
            }
        }

        // передать event всем остальным обработчикам
        return super.dispatchTouchEvent(ev)
    }
}

Понять, что пользователь ведет пальцем сверху вниз (чтобы "смахнуть" экран), не сложно: координата y всех следующих за начальной позицией событий увеличивается, а x может колебаться в каком-то незначительном интервале. С этим проблем, как правило, не возникает. Проблемы начинаются, когда на экране присутсвуют другие прокручиваемые элементы: ViewPager, RecyclerView, Toolbar с некоторым Behavior, их наличие нужно всегда иметь в виду:


abstract class SlidingActivity : AppCompatActivity() {

    private lateinit var root: View
    private var startX = 0f
    private var startY = 0f
    private var isSliding = false
    private val GESTURE_THRESHOLD = 10
    private lateinit var screenSize : Point

    override fun onPostCreate(savedInstanceState: Bundle?) {
        super.onPostCreate(savedInstanceState)
        root = getRootView()
        screenSize = Point().apply { windowManager.defaultDisplay.getSize(this) }
    }

    abstract fun getRootView(): View

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
        var handled = false

        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                // запоминаем точку старта
                startX = ev.x
                startY = ev.y
            }

            MotionEvent.ACTION_MOVE -> {

                // нужно определить, является ли текущий жест "смахиванием вниз"
                if ((isSlidingDown(startX, startY, ev) && canSlideDown()) || isSliding) {
                    if (!isSliding) {
                        // момент, когда мы определили, что польователь "смахивает" экран
                        // начиная с этого жеста все последующие ACTION_MOVE мы будем
                        // воспринимать как "смахивание"
                        isSliding = true
                        onSlidingStarted()

                        // сообщим всем остальным обработчикам, что жест закончился
                        // и им не нужно больше ничего обрабатывать
                        ev.action = MotionEvent.ACTION_CANCEL
                        super.dispatchTouchEvent(ev)
                    }

                    // переместим контейнер на соответсвующую Y координату
                    // но не выше, чем точка старта
                    root.y = (ev.y - startY).coerceAtLeast(0f)

                    handled = true
                }
            }

            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                if (isSliding) {
                    // если пользователь пытался "смахнуть" экран...
                    isSliding = false
                    onSlidingFinished()
                    handled = true
                    if (shouldClose(ev.y - startY)) {
                        // закрыть экран
                    } else {
                        // вернуть все как было
                        root.y = 0f
                    }
                }
                startX = 0f
                startY = 0f

            }
        }
        return if (handled) true else super.dispatchTouchEvent(ev)
    }

    private fun isSlidingDown(startX: Float, startY: Float, ev: MotionEvent): Boolean {
        val deltaX = (startX - ev.x).abs()
        if (deltaX > GESTURE_THRESHOLD) return false
        val deltaY = ev.y - startY
        return deltaY > GESTURE_THRESHOLD
    }

    abstract fun onSlidingFinished()

    abstract fun onSlidingStarted()

    abstract fun canSlideDown(): Boolean

    private fun shouldClose(delta: Float): Boolean {
        return delta > screenSize.y / 3
    }
}

Обратите внимание, что мы добавили новый абстрактный метод canSlideDown() : Boolean. Им мы спрашиваем у наследника, является ли текущий момент подходящим, чтобы начать наш Scroll-ToDismiss жест. Например, если пользователь читает статью и находится где-то на середине текста, то жестом пальца сверху вниз он наверняка хочет прокрутить статью повыше, вместо того, чтобы закрыть весь экран.


Вторым важным моментом является тот факт, что наш обработчик перестает отдавать события дальше по цепочке (не вызвается super.dispatchTouchEvent(ev)) начиная с того момента, как определил нужный нам жест. Это нужно для того, чтобы все вложенные прокручиваемые виджеты перестали реагировать на движения пальца и двигать контент самостоятельно. Перед тем как обрубить цепочку обработки, мы посылаем MotionEvent.ACTION_CANCEL, чтобы вложенные элементы не рассматривали внезапно прервавшийся поток сообщений как "Long Click".





Доводим до готовности


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


private fun closeDownAndDismiss() {
    val start = root.y
    val finish = screenSize.y.toFloat()
    val positionAnimator = ObjectAnimator.ofFloat(root, "y", start, finish)
    positionAnimator.duration = 100
    positionAnimator.addListener(object : Animator.AnimatorListener {
        override fun onAnimationRepeat(animation: Animator) {}

        override fun onAnimationEnd(animation: Animator) {
            finish()
        }

        override fun onAnimationCancel(animation: Animator) {}

        override fun onAnimationStart(animation: Animator) {}

    })
    positionAnimator.start()
}

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



Чтобы Scroll-To-Dismiss выглядел еще круче, можно добавить эффект затемнения заднего экрана по мере прокрутки:


override fun onCreate(savedInstanceState: Bundle?) {

    <...>

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        window.statusBarColor = Color.TRANSPARENT
    }

    windowScrim = ColorDrawable(Color.argb(0xE0, 0, 0, 0))
    windowScrim.alpha = 0
    window.setBackgroundDrawable(windowScrim)
}

private fun updateScrim() {
    val progress = root.y / screenSize.y
    val alpha = (progress * 255f).toInt()
    windowScrim.alpha = 255 - alpha
}

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


Итог


Таким довольно простым способом мы получили не только требуемое поведение, но и возможность гибко влиять на поведение.
Например, при желании, можно научить нашу Activity смахиваться вверх или в бок. Жесты, перехватываемые на уровне Activity не ломают поведение внутренних компонентов,
таких как ViewPager, RecyclerView и даже AppbarLayout + Custom Behavior.
Пользуйтесь на здоровье!

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

https://habrahabr.ru/post/329896/


Метки:  

О чем говорят женщины? (Text mining of beauty blogs)

Среда, 31 Мая 2017 г. 14:00 + в цитатник
В руках нашей команды из CleverDATA оказался уникальный материал – около 100 тыс. страниц англоязычных блогов, посвященных бьюти-сфере. Этот корпус к нам попал благодаря желанию одной косметической корпорации узнать законы, по которым «работает» блогосфера. Компания хотела эффективнее взаимодействовать с бьюти-блогерами – получать больший рекламный эффект, отдавая свои продукты в добрые руки лояльных авторов.
 

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

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

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

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

Отсекаем лишнее



Источник
 
Массив бьюти-блогов состоял из порядка 100 тыс. страниц, а точнее 98 496. Мы сначала обрадовались: 100 тыс. страниц — это хороший корпус для предстоящего исследования. Но выяснилось, что он очень сильно зашумлен, и после очистки осталось только 59.6%, пригодных для анализа.
 
40.4% данных составили пустые страницы и страницы с ошибками, страницы не на английском языке (23,461), фото- и видеоматериалы без текста (2,315), статьи с ресурса techcrunch.com, не имеющего отношения к бьюти-индустрии (очевидно, это ресурс, на котором тестировался краулер, собирающий материал, и его вклад в общем корпусе оказался заметным – 3,402 страниц).
 
Конечно, получить в распоряжение почти 60 тыс. страниц, годных для анализа, тоже неплохо. Выяснилось, что этому объему текста соответствуют около 2 тыс. уникальных блогов, то есть за вычетом клонированных и схожих материалов этот объем текста создали две тысячи уникальных авторов.
 

А автор кто?



Источник
 
Блоги на тему красоты и здоровья – это преимущественно женская тема. Если точный гендерный состав всей англоязычной блогосферы под вопросом, то в блогах о косметике и здоровом образе жизни всё однозначно: здесь большинство авторов и читателей – женщины. О чем говорят эти женщины в блогах? Это главный вопрос, который нам предстояло решить, чтобы понять, как наиболее эффективно продвигать товары бьюти-индустрии в блогосфере. Для ответа на этот ключевой вопрос, сначала попробуем исследовать, как говорят женщины в блогах.
 
Авторский стиль блогеров, конечно, различается. Но по размеру постов их авторов можно объединить в две группы: блогеры-миниатюристы с постами до 100 слов (20% всех авторов), и блогеры, предпочитающие полновесные тексты из 200-500 слов (~80%).

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

 
Анализ показал, что очень мало авторов пишут активно. Не более 20 авторов с момента появления блога успели написать свыше 300 постов, в большинстве блогов – до 100 постов, что укладывается в рамки обычной статистической закономерности.

 

Разговорчики в блоге


Мы посмотрели на дискуссии в блогах и выяснили, что посты очень малого числа блогеров набирают более 40 комментариев. Статьи большинства авторов обсуждаются не столь активно, и на одну публикацию в среднем приходится 10-20 комментариев.


 

Эмоциональная подача

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

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


 
Мы видим ассиметричное квазинормальное распределение с центром в районе 0.72 и тяжелым правым хвостом. Это означает, что абсолютное большинство блогов имеют позитивную эмоциональную тональность. Смещение средней эмоциональной окраски в положительную область является удивительным фактом, о котором можно говорить с высокой статистической значимостью и который можно легко проверить самостоятельно, прочитав несколько взятых наугад женских блогов.
 
Если посмотреть, как распределены блогеры по их активности (анализ количества страниц), можно обнаружить, что самые плодовитые блогеры по эмоциональному окрасу  работают в очень узком диапазоне: 0.74 ± 0.03.


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

Обсуждаемость


Зависит ли обсуждаемость блога от его активности? К удивлению, нет. В наиболее активных блогах мы видим меньше комментариев.

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


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


Популярность блога


Чтобы оценить эффективность блогов, с точки зрения распространения информации  (читают ли их, делают ли перепосты), мы попробовали воспользоваться готовыми инструментами для оценки Интернет-трафика.
 
Среди метрик, отражающих количество читателей и цитирования в сети, выделю следующие.
 
Alexa Rank  – популярный счетчик числа посетителей и количества просмотров сайта, устанавливается далеко не на всех Интернет-ресурсах, поэтому не всегда можно воспользоваться его данными.
 
Yandex Thematic Citation Index (TIC, тематический индекс цитирования Яндекса) определяет «авторитетность» Интернет-ресурса с учетом качественной характеристики ссылок на него с других сайтов, однако не очень распространён в англоязычном сегменте Сети
 
Google Page Rank  – подсчитывает количество и качество ссылок на блог, чтобы оценить значимость ресурса для аудитории; является основным показателем для раскрутки сайтов. Главное преимущество Google Page Rank в том, что он присутствует у всех сайтов, но у многих веб-страниц он явно неадекватный (и в этом его большой недостаток). Кроме того, из-за условий лицензионного соглашения есть ограничения на использование Google Page Rank, что затрудняет использование его даже для таких исследований, как наше.
 
Все вышесказанное определило наш выбор: мы попробовали YandexTIC и AlexaRank. Отмечу, что все эти метрики связаны с объемом аудитории (сколько раз автор цитируется, как много его читают), обладают своими недостатками и поэтому не могут претендовать на статус исчерпывающего.  Поэтому стоит поискать дополнительные инструменты  для оценки популярности автора.
 
Чтобы как-либо еще измерить популярность авторов, мы прибегли к Klout score. Эта метрика используется в социальных сетях, чтобы оценить влияние человека: его социальные связи и цитируемость его постов. Эта, независимая от предыдущих, метрика оценивает и аудиторию читателей, и количество репостов  именно в соцсетях. Примечательно, что с Klout score был связан громкий случай в 2011 году: на должность вице-президента одной из компаний претендовало два кандидата, один из которых 15 лет консультировал гигантов типа America Online, Ford и Kraft, второй же не мог похвастаться столь выдающимся опытом, но имел весомый козырь – Klout score, равный 67. В упомянутом случае позицию получил кандидат с более высоким Klout score. В нашем случае оказалось, что для наших авторов средний показатель Klout score составляет 40.1. Возможно, для вице-президента с пятнадцатилетним стажем такой Klout score будет низок, но для наших блогеров это нормальное значение: исследуемые бьюти-блогеры не являются центрами гигантских социальных скоплений, «наши» блогеры по своим характеристикам ближе к обычным людям со средней активностью в сети.



Если проследить взаимосвязь Klout score и эмоционального окраса, можно обнаружить интересную тенденцию.


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

Работа с брендом


Мы работали с торговой маркой, настоящее имя которой мы, конечно, не раскроем и в статье для удобства назовём ее «Баба Яга». Все продукты этой торговой марки имеют расширенные, многословные названия, например, «Крем для лица Баба Яга».
 
Мы применили технику Fuzzy String Matching на весь корпус текстов и попытались найти упоминания бренда и его продуктов во всех текстах.
 
Fuzzy String Matching основана на анализе расстояния Левенштейна, которое указывает на буквенные различия в словах. Строго говоря, расстояние Левенштейна определяет минимальное количество изменений одного символа (его удаления, замены, добавления), необходимых для превращения одного слова в другое. Расстояние, полученное с помощью модуля Python fuzzywuzzy, нормировано в диапазоне от 0 до 100. Таким образом, абсолютно различные слова будут иметь меру похожести, равную 0, а тождественные слова будут иметь меру похожести, равную 100. Например, в бородатом анекдоте о разнице между хлебом и пивом мера похожести будет равна нулю: чтобы из хлеба получить пиво, нужно заменить все четыре буквы.
 
Необходимо отметить, что нам повезло с названиями продуктов бренда, т.к. они не были односложными (как известное мыло «Удав»), а состояли из нескольких слов, по которым можно было понять тип и отчасти назначение продукта, например, «Масло для лица Баба Яга». Fuzzy String Matching позволяет с соответствующими настройками отлавливать частичное упоминание, например, «Face Oil», и мы пытались на этом играть.
 
Посты, в которых искомый продукт упоминался на 90% по метрике Fuzzy String Matching, отмечались в качестве «хороших». У бренда было около 100 продуктов, таким образом каждая статья проходила проверку для каждого продукта более 100 раз.
 
Рейтинг релевантности для автора брался как сумма всех «хороших» статей. Нормировка на количество статей не вводилась намеренно, чтобы авторы с бОльшим количеством статей вырвались вперед.
 
Впоследствии мы использовали натуральный логарифм от полученного рейтинга. Например, авторы с 30, 10 и пятью «хорошими» статьями получали соответствующий рейтинг релевантности 3.4, 2.3 и 1.6.
 
Подход несложный, однако за счет большого количества статей и большого количества продуктов начинали работать закон больших чисел и ЦПТ (центральная предельная теорема), и мы получали разумные оценки.
 
Чтобы ускорить процесс и повысить точность, мы перешли на использование расстояния, полученного с помощью модели Word2Vec, однако даже при первоначальном подходе мы получили результат, который можно использовать в дальнейшей работе.
 

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


На основе перечисленных техник мы построили рейтинг авторов. Он базируется на:
 
  • количестве постов в блоге,
  • числе комментариев,
  • метриках AlexaRank + YandexTIC,
  • степени релевантности блога товарам бренда,
  • эмоциональном окрасе,
  • Klout score.


 
Следует обратить внимание, что мы не отдаём предпочтение блогерам с большим количеством страниц, потому что встречаются как активные блоги с объемом более 500 постов, так и малоактивные авторы с числом постов менее 100. Также нет предпочтений по количеству комментариев. Отмечу, что по уровню эмоционального окраса они все положительные и работают в диапазоне от 0.70 до 0.78.
 
Мы внимательно изучили лидера списка. Оказалось, что у него опубликована статья, посвященная нашему бренду. Это была хвалебная ода всему бренду, без анализа и описаний конкретных продуктов.
 
Итак, рейтинг блогеров построен, теперь нужно связать авторов и продукты, которые им можно отдать для обзора. Для этого нужно:
 
  • выбрать продукты для обзора,
  • выделить основные темы авторов,
  • сопоставить выделенные темы с названиями продуктов,
  • найти оптимальную связь между продуктом и блогом.

Выбор продуктов для продвижения


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

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

Авторы и их темы


В блогах каждого автора можно найти темы, интересные для продвижения бренда. А о чем вообще пишут авторы? Чтобы узнать список тем каждого блога, составим матрицу,  каждая строка которой будет соответствовать посту, в столбцах укажем ключевые слова, а цвет ячеек будет означать индекс TF-IDF (про применение метода TF-IDF можно почитать, например здесь и здесь) – частотную характеристику ключевого слова. Соответственно, чем более интенсивно окрашена ячейка, тем больше упоминаний слова мы нашли и тем важнее это слово в контексте. Примеры слов для формирования таблицы: «макияж», «лицо», «тело», «скраб», «лосьон», «масло», «очищение», «кондиционер» и т.д.



Далее применяется метод NMF, который позволяет разложить нашу матрицу на две поменьше: «авторы»/«промежуточное измерение» и «промежуточное измерение»/«слова». Единственное наложенное нами условие факторизации – величины должны быть не негативны, то есть они все должны быть больше либо равны 0.
 
«Промежуточное измерение» в данном случае можно интерпретировать как темы. Таким образом, мы разложили авторов на темы их текстов.
 
Используемый метод NMF для получения тематик текстов обычно может конкурировать с методами LDA и LSA (pLSA), однако нельзя не упомянуть еще один сильнейший инструмент в этой области: BigARTM, на который мы переходим в данный момент. Необходимо отметить, что базовая идея разложения матрицы присутствует и в методе BigARTM, однако его преимущество заключается в гибкой возможности использоваяния регуляризаторов


 

Каждому по возможностям


Теперь нужно сопоставить название продуктов бренда с выделенными темами. Это возможно сделать и с помощью уже использованного подхода Fuzzy String Matching, но лучше и точнее использовать дистанцию, измеренную с помощью модели Word2Vec.
 
Здесь следует учесть один момент: если название продукта полностью прописано в заголовке поста, скорее всего, это был обзорный пост, то есть автор уже писал о продукте и ему не стоит предлагать сделать это еще раз.
 
Получаем красочную матрицу, где цветом обозначена степень корреляции автора с продуктом. Матрица «авторы-продукты» отсортирована по нашему рейтингу авторов.
 
Рейтинг авторов отчасти базировался на упоминаемости продуктов бренда в текстах. После сортировки по рейтингу авторов можно наблюдать цветовое распределение, которое сосредоточено на авторах с высоким рейтингом и затухающее по мере уменьшения рейтинга.



Это цветовое распределение было получено с помощью отдельного математического подхода (TF-IDF, NMF, etc), и оно хорошо согласуется с нашим первоначальным результатом, полученным с помощью простых подсчетов упоминаний. Таким образом, мы с одной стороны подтверждаем адекватность своего рейтинга, а с другой стороны показываем разумность результатов, полученных с помощью ряда более сложных математических приемов. Согласованность результатов, полученных различными методами, говорит в нашу пользу.
 
Для продвижения продукта нам не нужно задействовать много человек. Возьмём первые сорок авторов. Для них матрица будет выглядеть следующим образом:



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

Подводя итоги


Таким образом, по результатам исследования первоначального корпуса статей бьюти-блогов с использованием метрики социальной активности Klout score, а также с учетом эмоционального окраса статей, нами был обнаружен ряд особенностей бьюти-блогеров. Эти особенности необходимо учитывать при организации рекламных компаний через бьюти-блогеров. Кроме того, мы нашли основные тематики статей блогов. По найденным тематикам мы соотнесли продукты с авторами таким образом, чтобы продукт был наиболее близок аудитории блога.
 
Первоначальный анализ мы сделали с помощью доступных и несложных методов работы с текстами (Fuzzy String Matching, TF-IDF, NMF), однако уже на этом уровне получили основные результаты, которые затем только уточнялись.
 
Оказалось, что данная косметическая компания работает с 30% исследованных авторов. Разумеется, бьюти-бренд был рад получить данные об оставшихся 70%, чтобы расширить своё влияние в блогосфере. В дальнейшем нам дали доступ к детальному описанию продуктов, данным о ингредиентах и другим характеристикам, что позволило перевести работу на новый уровень, мы стали активно использовать Word2Vec и BigARTM. Описанный анализ стал эволюционировать в инструмент, который прекрасно дополняет рекомендательную систему, подготовленную командой CleverDATA для бренда.
 
Демонстрация наших результатов привлекла еще четыре бьюти-бренда, с которыми мы сейчас сотрудничаем. Естественно, в блогосфере каждый бренд заинтересован исследовать и развивать свою собственную тему, соответствующую его продуктовой нише.
 
Рекомендации конкретных блогеров для продвижения продуктов со временем меняются, т.к. блогосфера растет и эволюционирует. Поэтому регулярный обзор авторов и мониторинг блогов важен брендам не только для продвижения продуктов, но и для понимания своего места в индустрии.
 
Сейчас мы работаем над тем, чтобы проводить краулинг блогов регулярно и разрабатываем возможность для оперативного отображения реакции аудитории на новые статьи с обзорами продуктов. Таким образом, бренд сможет быстро получать обратную связь на свои продукты, не заказывая дорогостоящие маркетинговые исследования. Также планируем подключить больше информации из социальных сетей: соцсети очень интересны брендам для привлечения новой аудитории.
 
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/329892/


Метки:  

Поиск сообщений в rss_rss_hh_new
Страницы: 1437 ... 986 985 [984] 983 982 ..
.. 1 Календарь