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

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

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

 

 -Постоянные читатели

 -Статистика

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

Habrahabr








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

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

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

Coco Framework — блокчейн по-крупному

Вторник, 15 Августа 2017 г. 10:58 + в цитатник
Технология блокчейн получила широкую известность в связи с быстро набравшими популярность криптовалютами. Однако её возможности куда шире. Благодаря своей децентрализованности и прозрачности, блокчейн может стать удобным средством для построения профессиональных и корпоративных информационных средств – любых, где требуется самоконтролируемый обмен сообщениями о транзакциях.



Однако для того, чтобы стать профессиональным инструментом, блокчейн должен соответствовать жестким требованиям по производительности и безопасности, иметь хорошую репутацию и открытый код. Осознавая это, два компьютерных гиганта, Intel и Microsoft, решили «с нуля» создать принципиально новую блокчейн-систему, Coco Framework. Ее принципиальная особенность – поддержка технологии Intel Software Guard Extensions (Intel SGX) для аппаратного ограничения доступа к данным блокчейна и ускорения процесса их обработки.

По обещаниям разработчиков, исходный код Coco Framework будет выложен на GitHub в начале 2018 года. Под катом вы найдете инфографику о преимуществах Intel SGX с точки зрения блокчейна.
Читать дальше ->

https://habrahabr.ru/post/335658/


Метки:  

Как мы участвовали в первом Лигалтех хакатоне СНГ и почему решили делать ещё один в Москве

Вторник, 15 Августа 2017 г. 10:53 + в цитатник
Мы давно делаем b2b-решения небольшой командой и вообще счастливы. Однако in-house, это как фриланс. Однажды ты обнаруживаешь себя работающим в трусах в 4 ночи вокруг грязной посуды. Стали искать аутсорс, смотреть в сторону других технологий и проектов. В мае нас с супругой пригласили на лигалтех-хакатон в Киеве и, конечно, мне было очень интересно. Жена лигал, я тех, отличная команда. Мы собрали вещи и покатили.



Пока ехали в поезде, думали, с каким проектом пойдём. Хотели сделать бота для налоговых вычетов, но вроде сбертех это делал, да и профита там мало. Была идея сделать личного помощника по всем юр.вопросам, некий мост между клиентами и биржами юристов. В итоге остановились на системе автоматизации работы m&a-юриста венчурного фонда. Так сказать и задачка интересная, и mvp поможет супруге на работе, избавив от рутины и необходимости в помощнике.



Погуляв по Киеву, расписали MVP и легли спать. Взбодрившись ирландским кофе с утра, пошли на открытие. Победа для меня не была самоцелью, но я чувствовал наше превосходство ;) Мы практики, с большим опытом, с опытом запуска бизнеса, привлечения инвестиций, а тут студентики какие-то. Так я думал до представления команд. Самооценка падала постепенно, но уверенно ))) Те самые «студенты» предлагали интересные продукты, к некоторым из команд я сам бы с удовольствием подключился. Единственное, что вселяло в меня веру в успех — наше трудолюбие. Не каждый выдерживает сутки работы без сна, а себе я периодически устраиваю такие встряски.



К нашему проекту присоединилось 3 человека — 2 юриста и разработчик. Сначала я думал, что оно нам не надо, но я сильно ошибался. Ребята взяли на себя работу по презентации, часть кода и много поддерживали. Вдвоём я вообще не уверен, что мы бы справились.

В качестве mvp мы решили показать путь подписания документов при привлечении инвестиций для стартапа, шаблонизировать их, предлагая юристу или стартапу заполнить анкету и загрузить фотографию своих подписей. Заполнив анкету и загрузив подпись, система сама генерила docx-договоры (NDA, dd, ts и т.п.) с уже проставленными подписями и данными. Это лишь одна из задач будущей системы, но для суточного хакатона вполне подойдёт.



Накидали бэклог для канбана, оставалось порядка 20 часов. Я прикинул, что на фронт нам потребуется порядка 3-4 часов, поставил пару будильников и мы приступили к бэкенду.

Редбул лился, код мутился. Ближе к утру стало понятно с чем мы успеваем, с чем не очень, с какими проблемами столкнулись и что в итоге у нас получается. Мы закончили бэкенд, успев реализовать 99% задумок, было 7 утра. Я поставил будильник на час и улёгся на пуфик. В 8 утра народ начал стягиваться обратно на площадку, а мы сели за фронт. У нас был готов код для шаблонизации вордовских документов, был написан небольшой CV для поиска и «вычленения» подписи с фотки, однако, наш шаблонизатор не умел вставлять картинки туда, куда нам надо. Это немного печалило, но время шло и мы решили не отвлекаться на это. Покажем при демонстрации MVP, что мы это сделали, но в готовый документ не встанет, жаль, но ладно. Спасибо Ангуляру и Бутстрапу — мы достаточно быстро накидали интерфейс как для юриста фонда, так и для стартапа.

Презентация 5 минут. И тут началось самое интересное ) Ребята показывали хорошие проекты, хорошие и разные, близкие к потребителю, явно ближе, чем наш b2b-инструмент. Тут уже я начал нервничать, я понимал, что победа не главное, но победить-то хотелось ) Были, конечно, и промахи. Например, одна из команд приехала уже с готовым проектом, в чём честно признались.

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



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



После хакатона в Киеве мы задумались о развитии лигалтех-движения в России и в Москве. Оказалось, его не так много, почти нет. Нам очень понравился формат, участие и участники. Лигалтех из «какого-то там направления» стал для нас очень интересным вектором.

Захотелось провести свой хакатон, но перед ним нужно заявить о себе, что мы и сделаем 30 августа на первом мероприятии нашего лигалтех-движения в Москве. Мы уже договорились с топ-3 юридическими вузами о прямой поставке студентов, договорились с СМИ, нашли чумовую площадку для проведения ивента и активно набираем ЦА для проведения первой встречи. Мероприятие бесплатное, наша задача — развитие лигалтеха, а не моментальная прибыль. Поэтому если кому покажется интересным провести 30 августа (среда) в компании единомышленников, пообсуждать лигалтех, его будущее и узнать наши планы по хакатону, пишите в личку, отвечу всем.

Обращаясь к Киевским ребятам, которые двигают там лигалтех, хочется ещё раз сказать спасибо! Вы все большие молодцы. И то, что вы делаете для лигалтеха — бесценно.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335626/


Метки:  

Исследование асинхронной схемы в ModelSim

Вторник, 15 Августа 2017 г. 10:51 + в цитатник


Прочитал статью FPGA/Асинхронный дешифратор от ajrec.

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

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

Честно говоря, я всегда с любопытством смотрю на проекты, которые «не такие, как все». Я считаю, что только такие проекты имеют шанс либо глубоко провалиться, либо высоко взлететь. Если делаешь что-то так же, как и сотни других разработчиков, то идешь проторенной дорогой, здесь легко и комфортно. Сделать шаг в сторону и пойти своей тропой могут далеко не все.

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

Честно говоря, изложенное в статье "Асинхронный дешифратор" и "Система синтеза самосинхронных схем Petrify: проблемы и их решение" плохо влазит в мою голову. Я основательно «испорчен методикой синхронного дизайна».

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

Выходные сигналы:
q1 — переключается если r1=1 и r2=1;
q2 — переключается если r1=1 и r2=0;
q3 — переключается если r1=0 и r2=1;
q4 — переключается если r1=0 и r2=0.

Я наверное придираюсь, но с трудом представляю себе программиста, который пишет нумерацию сигналов начинающуюся с единицы и чтобы q4 переключается, когда r1=0 и r2=0. В моей голове все строго по шаблону: { 0, 0 } — соответствует сигналу q0. { 0, 1 } — соответствует сигналу q1. { 1, 0 } — соответствует сигналу q2 и { 1, 1 } — соответствует сигналу q3. Но это, конечно, мелочи, которые не относятся к делу.

Основная же претензия с моей стороны: автор не проверяет свои гипотезы/исследования/проекты в ПЛИС или любым другим способом. Он пишет: «У меня нет возможности проверить схему в реальном воплощении.» Ну как так то?

Собственно это и подтолкнуло меня к мысли попробовать просимулировать логику описанного ajrec дешифратора. Временная симуляция — это достаточно хорошая модель, которая будет показывать поведение схемы в реальной ПЛИС. Правда… тут есть нюанс — у меня нет полной уверенности, что ModelSim сможет достоверно симулировать асинхронные схемы: симулятор ModelSim предназначен прежде всего для «традиционного» синхронного дизайна.

Итак. Исходный код из статьи FPGA/Асинхронный дешифратор у нас не знаю на каком языке и выглядит следующим образом:

e=NOT(o1); e1=AND(o1,j1); e2=AND(o2,h1); e3=NAND(e,i1);
e4=OR(o2,h1); f1=AND(d,r1); f2=AND(d,r2); g=NOT(d);
h1=NOR(g,r1); h2=NOR(g,r2); i1=NOR(f1,j1); i2=NOR(f2,j2);
j1=NOR(g,i1); j2=NOR(g,i2); k1=AND(e1,j2); k2=AND(e1,h2);
k3=AND(e2,j2); k4=AND(e2,h2); l1=AND(t1,i2); l2=NOR(t2,h2);
l3=AND(t3,i2); l4=NOR(t4,h2); m1=NOR(k1,n1); m2=NOR(k2,n2);
m3=NOR(k3,n3); m4=NOR(k4,n4); n1=NOR(l1,m1); n2=NOR(l2,m2);
n3=NOR(l3,m3); n4=NOR(l4,m4); o1=NOR(p1,p2); o2=NOR(p3,p4);
p1=NOR(m1,s1); p2=NOR(m2,s2); p3=NOR(m3,s3); p4=NOR(m4,s4);
s1=NOR(n1,l1); s2=NOR(n2,l2); s3=NOR(n3,l3); s4=NOR(n4,l4);
t1=NOR(e3,s1); t2=OR(e3,s2); t3=NOR(e4,s3); t4=OR(e4,s4);
q1=NOR(s1,k1); q2=NOR(s2,k2); q3=NOR(s3,k3); q4=NOR(s4,k4).


Далее в статье идет эквивалентная схема этой штуки и говорится о том, что вообще-то сигналы m1, m2, m3, m4 нужно еще сбрасывать в исходное состояние перед началом работы. Хорошо, перепишу все это на язык Verilog вот так:

module test(
	input wire d,
	input wire r1,
	input wire r2,
	input wire set0,
	output wire q1,
	output wire q2,
	output wire q3,
	output wire q4,
	output wire [3:0]qq
);

wire e, e1, e2, e3, e4;
wire f1, f2;
wire g;
wire i1, i2;
wire j1, j2;
wire h1, h2;
wire k1, k2, k3, k4;
wire l1, l2, l3, l4;
wire m1, m2, m3, m4;
wire n1, n2, n3, n4;
wire o1, o2;
wire p1, p2, p3, p4;
wire s1, s2, s3, s4;
wire t1, t2, t3, t4;

function NOT;
input s;
begin
	NOT=~s;
end
endfunction

function AND;
input s1,s2;
begin
	AND=s1&s2;
end
endfunction

function NAND;
input s1,s2;
begin
	NAND=~(s1&s2);
end
endfunction

function OR;
input s1,s2;
begin
	OR=s1|s2;
end
endfunction

function NOR;
input s1,s2;
begin
	NOR=~(s1|s2);
end
endfunction

assign e=NOT(o1);     assign e1=AND(o1,j1); assign e2=AND(o2,h1); assign e3=NAND(e,i1);
assign e4=OR(o2,h1);  assign f1=AND(d,r1);  assign f2=AND(d,r2);  assign g=NOT(d);
assign h1=NOR(g,r1);  assign h2=NOR(g,r2);  assign i1=NOR(f1,j1); assign i2=NOR(f2,j2);
assign j1=NOR(g,i1);  assign j2=NOR(g,i2);  assign k1=AND(e1,j2); assign k2=AND(e1,h2);
assign k3=AND(e2,j2); assign k4=AND(e2,h2); assign l1=AND(t1,i2); assign l2=NOR(t2,h2);
assign l3=AND(t3,i2); assign l4=NOR(t4,h2); 

assign m1=NAND( set0, OR(k1,n1)); /* NOR(k1,n1); */
assign m2=NAND( set0, OR(k2,n2)); /* NOR(k2,n2); */
assign m3=NAND( set0, OR(k3,n3)); /* NOR(k3,n3); */
assign m4=NAND( set0, OR(k4,n4)); /* NOR(k4,n4); */

assign n1=NOR(l1,m1); assign n2=NOR(l2,m2);
assign n3=NOR(l3,m3); assign n4=NOR(l4,m4); assign o1=NOR(p1,p2); assign o2=NOR(p3,p4);
assign p1=NOR(m1,s1); assign p2=NOR(m2,s2); assign p3=NOR(m3,s3); assign p4=NOR(m4,s4);
assign s1=NOR(n1,l1); assign s2=NOR(n2,l2); assign s3=NOR(n3,l3); assign s4=NOR(n4,l4);
assign t1=NOR(e3,s1); assign t2=OR(e3,s2);  assign t3=NOR(e4,s3); assign t4=OR(e4,s4);
assign q1=NOR(s1,k1); assign q2=NOR(s2,k2); assign q3=NOR(s3,k3); assign q4=NOR(s4,k4);

assign qq = 1 << {r2,r1};

endmodule

Я постарался в исходный код вносить минимальные изменения, чтобы случайно не нарушить логику, которую я не понимаю. Поэтому я определил Verilog функции AND, OR, NOR, NAND, чтобы они делали то, что должны делать. Присвоение значения сигнала в Verilog делается через assign. Если кому нужно, то вот Краткое описание языка Verilog.

Таким образом, в модуле test имеются входные сигналы декодируемого адреса r1 и r2, а так же сигнал сброса set0 и сигнал d — команда на операцию дешифрации адреса (в терминологии автора). Выходные сигналы q1, q2, q3 и q4.

Кроме этого, я добавил отсебятину, четырехбитный выходной сигнал qq, который вычисляется традиционным дешифратором, вот так: assign qq = 1 << {r2,r1};

Модуль test был вставлен в проект Intel Quartus Prime Lite Edition v16.1 для микросхемы ПЛИС Cyclone IV E.

Проект был скомпилирован. Предварительно в настройках проекта Assignments=> Settings => EDA Tools Settings => Simulation нужно выбрать Tool Name => ModelSim-Altera. Тогда, после компиляции будет создана папка simulation/modelsim в которой будут находиться модули *.sdo и *.vo, которые нужны для временной симуляции.

Теперь нужен testbench. Я написал его по простому:

`timescale 1ns / 1ns

module tb();

reg r1_;
reg r2_;
reg d_;
reg set_;
wire q1_,q2_,q3_,q4_;
wire [3:0]qq_;

test test_inst(
	.d(d_),
	.r1(r1_),
	.r2(r2_),
	.set0(set_),
	.q1(q1_),
	.q2(q2_),
	.q3(q3_),
	.q4(q4_),
	.qq(qq_)
);

initial
begin
	$dumpfile("out.vcd");
	$dumpvars(0,tb);
	
	//reset m1-m4 signals using "set_"
	r1_=0;
	r2_=0;
	d_=0;
	set_=0;
	#100;
	set_=1;
	#100;
	
	//check addr 00
	r1_=0;
	r2_=0;
	#100;
	d_=1;
	#100;
	d_=0;
	#200;
	
	//check addr 01
	r1_=1;
	r2_=0;
	#100;
	d_=1;
	#100;
	d_=0;
	#200;
	
	//check addr 10
	r1_=0;
	r2_=1;
	#100;
	d_=1;
	#100;
	d_=0;
	#200;
	
	//check addr 11
	r1_=1;
	r2_=1;
	#100;
	d_=1;
	#100;
	d_=0;
	#200;
	
	//--------------------
	//check addr 00
	r1_=0;
	r2_=0;
	d_=1;
	#100;
	d_=0;
	#200;
	
	//check addr 01
	r1_=1;
	r2_=0;
	d_=1;
	#100;
	d_=0;
	#200;
	
	//check addr 10
	r1_=0;
	r2_=1;
	d_=1;
	#100;
	d_=0;
	#200;
	
	//check addr 11
	r1_=1;
	r2_=1;
	d_=1;
	#100;
	d_=0;
	#200;
end

endmodule

Здесь сигналы адресов r1 и r2 поочередно устанавливаются во все четыре возможные значения 2'b00, 2'b01, 2'b10 и 2'b11, при этом еще вырабатывается этот сигнал d, который активизирует декодирование.

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

Теперь запускаю ModelSim-Altera (или теперь уже Intel). Создаю проект и воркспейс. Добавляю в него два существующих файла: тестбенч tb.v и скомпилированный квартусом проект async_test.vo:



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

Теперь можно начинать симуляцию через меню ModelSim, Simulate-> Start Simulation…
Тут появляется диалоговое окно где нужно:

1) выбрать топ модуль и сейчас это тестбенч



2) добавить библиотеки альтеры



3) добавить скомпилированный SDF файл, из которого все временные параметры пойдут



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



Нажимаем OK и далее меню Simulate => Run => Run-all

Вот оно и получилось — заработало!



Что я вижу:
Во-первых, проект похоже почти работает и декодирует адреса, как и обещано — вот только для первой группы четырех импульсов d. Как-то перепутаны выходы дешифратора, при {r2,r1} = 2'b01 зажигается q2. Но я про эту странность выше уже говорил. Честно говоря, я был сильно удивлен, что это вообще работает при каких-то условиях.

Вторая группа из 4х импульсов d не работает правильно. Это тот момент когда сигнал d приходит одновременно с изменяющимися r1 и r2.

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


Защелкиваемый в регистре сигнал обязан быть стабильным некоторое время tsu (setup time) до фронта тактовой частоты и некоторое время после фронта тактовой частоты thold (hold time). Если это условие не выполняется, то синхронная схема не работает или работает не надежно (ну то есть фактически не работает).


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

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

Мне кажется, что автору асинхронной схемы не удастся избежать временного анализа. Обязательно нужно позаботиться о стабильности сигналов r1 и r2 до прихода d. Это нужно как-то контролировать и учитывать. Честно говоря, сам импульс d чем-то напоминает синхроимпульс тактовой частоты. У сигнала d есть длительность, значит она, эта длительность чем-то обеспечена, каким-то генератором? Получается, что для работы асинхронной схемы все таки нужны времязадающие цепи, те которые определяют длительность и позицию сигала d? Вообще, происхождение этого сигнала d не очень понятно…

Хотел обратить внимание еще на несколько моментов в моей симуляции. Даже когда схема работает (первые 4 импульса d на симуляции) выходные сигналы q1, q2, q3, q4 сдвинуты относительно сигнала d и каждый сдвинут на разное время. Самое интересное, что ширина импульсов q1, q2, q3 и q4 всегда больше ширины импульса d и все они разные по ширине. Я не знаю, как автор собирается комбинировать такие схемы одну с другой. Если предположить, что выходной сигнал q1 для последующих схем становится сигналом d, то получится, что через цепочку схем сигнал d становится шире и шире — но это уже мои фантазии. Повторюсь, я не очень понимаю как такие схемы будут собираться в бОльшие.

Еще одна мысль не покидает меня.
Мне кажется, что традиционный дешифратор вроде такого, как у меня выше assign qq = 1 << {r2,r1}; займет в чипе гораздо меньше места по числу транзисторов, чем описанная автором асинхронная схема. А число транзисторов в ASIC напрямую влияет на производительность, ведь получается большая площадь чипа занята и более длинные трассы между элементами, большее энергопотребление. Об этом обязательно нужно подумать, как это оценить и как это посчитать.

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

Я как-то делал эксперименты по реализации схемы на Double-Edge-Trigger. Идея заключалась в том, чтобы использовать для обычной синхронной логики оба фронта тактовой частоты. Я думал, что использование DET триггера позволит в чипе для той же скорости вычислений иметь рабочую частоту в два раза меньше и значит как минимум снизится энергопотребление. Я просимулировал свой проект и даже запустил его в ПЛИС — он работал. Подробности об этом проекте здесь.

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

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

Симуляция — бесплатна! Пробуйте господа!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335386/


Метки:  

Чат-бот своими руками: история одного велосипеда

Вторник, 15 Августа 2017 г. 10:44 + в цитатник


Привет, хабр! Сегодня я расскажу о том, как своими руками с нуля собирались мозги для чат-бота, умеющего создавать резюме на основе беседы с человеком. Речь пойдет о том, как развивался написанный для этого дела велосипед, какие трудности встречал на своем пути и как изменялся в целях преодоления этих трудностей. Все описанные события происходили в процессе моего обучения в Школе программистов HeadHunter в 2017 году. Кому интересно — добро пожаловать под кат. Читать дальше ->

https://habrahabr.ru/post/335574/


Метки:  

Избранное: ссылки по reverse engineering

Вторник, 15 Августа 2017 г. 10:42 + в цитатник


Всем привет!


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


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


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


Итак, перейдем к списку материалов!


  1. Тематики
    a. Реверс
    b. Поиск уязвимостей (fuzzing)
    c. Эксплуатация уязвимостей
    d. Анализ вредоносного кода
  2. Инструменты
    a. IDA Pro
    b. Radare2
    c. WinDBG (Ollydbg / Immunity Debugger / x64dbg)
    d. GDB
    e. DBI
    f. SMT
    g. Python для автоматизации
    h. BAF (Binary Analysis Frameworks)
  3. Архитектуры
    a. x86-x86_64
    b. ARM
  4. ОС
    a. Windows
    b. Linux
    c. Mac OS(OSX) / iOS
    d. Android
  5. Форматы файлов
    a. PE
    b. ELF
    c. Mach-O
  6. Программирование
    a. C/C++
    b. Assembler
  7. Практика
    a. War games



1. Тематики


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


1.a Reverse engineering


  • "Искусство дизассемблирования" от Крис Касперски — не новая, но очень хорошая и до сих пор актуальная книга от Криса с хорошей систематизацией знананий и отличным материалом;
  • "Practical Reverse Engineering: x86, x64, ARM, Windows Kernel, Reversing Tools, and Obfuscation" — "новая" книга от нескольких известных специалистов по ИБ, покрывающая некоторые новые моменты и темы, что отсутствуют в книге Криса;
  • "Реверсинг для начинающих" от Дениса Юрьевича — полностью бесплатная книга, переведенная уже на множество языков мира. Здесь, наверное, самое примечательное — это наличие интересных заданий после каждой главы, при этом — для нескольких архитектур сразу;
  • "Practical RE tips" — отличный webinar на английском языке от Gynvael Coldwind, содержащий много полезных советов и скриптов про RE;
  • Ресурс "OPENSECURITYTRAINING.INFO" содержит хорошие обучающие лекции и видеоролики по RE на английском;
  • "Digging Through the Firmware" — неплохая серия статей Practical Reverse Engineering — полезные статьи для тех, кто только собирается погрузиться в мир реверсинга прошивок устройств;
  • "Training: Security of BIOS/UEFI System Firmware from Attacker and Defender Perspectives" — если вы хотите окунуться в мир firmware security, UEFI BIOS, то вам определенно нужно ознакомиться с данными слайдами, которые ранее были в платном тренинге на ведущих конференциях по безопасности;
  • CRYPTO101 — немного введения в криптографию, без которой не обойтись.

1.b Поиск уязвимостей



1.c Примеры эксплуатации найденных уязвимостей



1.d Анализ вредоносных программ



2. Необходимый инструментарий


Ниже представлены популярные инструменты, применяемые при RE.


2.a IDA Pro



2.b Radare2


  • "The radare2 book" — основная книга по использованию фреймворка Radare2 для реверса;
  • "Radare2 Cheatsheet" — "шпаргалка" по основным командам;
  • "Radare Today — the blog of radare2" — блог фреймворка. В нём найдутся не только новости, но и практические примеры.

2.c WinDBG (Ollydbg / Immunity Debugger / x64dbg)


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


  • Advanced Windows Debugging: Developing and Administering Reliable, Robust, and Secure Software — в первую очередь, эта книга пригодится для понимания и "отлова" ошибок типа повреждения кучи;
  • "Inside Windows Debugging: A Practical Guide to Debugging and Tracing Strategies in Windows" — это издание хорошо дополнит предыдущую книгу;
  • "Введение в крэкинг с нуля, используя OllyDbg" — к сожалению, старейший ресурс wasm.ru закрылся, но подобная подборка легко ищется, поскольку была продублирована на множество ресурсов. К тому же, в сети стали появляться "форки", только в них уже используется x64dbg или IDA.

2.d GDB



2.e DBI


Программируемая отладка — это сегодня неотъемлемый подход в арсенале любого реверсера. И DBI — один из инструментов. Подробнее:


  • "Dynamic Binary Instrumentation в ИБ" — в этой статье уже собрана некоторая обобщенная информация про DBI;
  • "Light And Dark Side Of Code Instrumentation" — данная презентация поможет вам ориентироваться в разновидностях различных инструментаций кода и в том, что и когда вам может помочь с анализом программ.

2.f SMT


Что такое SMT-решатель? Если кратко, SMT-решатель — это программа, которая может решать логические формулы.


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


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


  • поиск багов (статический анализ/фаззинг);
  • деобфускация;
  • "домашний" криптоанализ;
  • символьное исполнение (в качестве "движка");
  • также есть определенные успехи в области автоматической генерации эксплойтов (например, генерации ROP).

За это время SMT потеряла ореол таинственности, появились более-менее работающие инструменты для "простых" людей.


Ниже приведены источники, которые помогут погрузиться в тему:



2.g Python для автоматизации


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



2.h BAF (Binary Analysis Frameworks)


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



Несколько интересных фреймворков/инструментов:



3. Архитектуры


Мы рассмотрим только несколько популярных архитектур. В конце статьи в разделе с дополнительными материалами вы найдете информацию по многим другим (MIPS, PowerPC и т.д.).


3.a x86-x86_64


  • "Intel 64 and IA-32 Architectures Software Developer Manuals" — раньше такие руководства отправляли на почту, но из-за большого количества материала в них печать стала дорогим удовольствием. Рекомендуется в качестве настольного справочника.

3.b ARM


  • Azeria Labs (ARM Assembly Basics & ARM Exploit Development) — сайт со статьями по основам ARM-ассемблера и разработке эксплойтов под эту архитектуру;
  • Курс "Introduction to ARM" — двухдневный видеокурс, посвященным ARM-разработке и эксплуатации;
  • VisUAL — визуализация работы ARM-команд.

4. ОС


Знание принципов работы популярных Операционных Систем.


4.a Windows



4.b Linux



4.c Mac OS(OSX) / iOS



4.d Android


  • "Android Hacker's Handbook" — наверное, самая популярная книга, посвященная безопасности ОС Android;
  • "Android Internals::Power User's View" — книга, рассказывающая о внутренних механизмах этой ОС. Из-за недавних утечек материал появился в открытом доступе, о чём пишет сам автор на своём сайте и предоставляет возможность скачать прошлую версию.

5. Форматы исполняемых файлов


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


5.a PE



5.b ELF



5.c Mach-O



Известный исследователь corkami делает очень полезные и интересные "постеры" со схемой различных форматов файлов, в том числе, упомянутых выше. Советуем использовать их как шпаргалку.


6. Программирование


Один наш знакомый как-то сказал, что хороший реверсер это на 80% хороший программист. Умение программировать и понимание того, что и зачем делается, упрощает процесс исследования чужой программы. Поэтому без программирования в реверсе никуда. Ну и конечно автоматизация рутинной задачи, как вы уже наверняка поняли, — очень полезная вещь ;)


6.a C/C++



6.b ASM



7. Практика


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


7.a War Games


  • SmashTheStack Wargaming Network — данная сеть с несколькими wargame поддерживается волонтёрами и доступна онлайн. Мы рекомендуем начать с неё;
  • BinTut — локальный wargame;
  • Reversing Workshop — мастер-класс по решению заданий с ежегодного соревнования "The Flare On Challenge" за 2016 г.;
  • Exploit-Challenges — подборка уязвимых ARM бинарных файлов;
  • ARM Reverse Engineering Exercises — оригинальный репозиторий "пропал", но был найден один из форков на просторах github;
  • CTF Time — тут вы сможете узнать расписание будущих CTF-мероприятий и прочитать решения прошедших.

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


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

https://habrahabr.ru/post/334832/


Метки:  

[Перевод] 10 шагов по решению задач в программировании

Вторник, 15 Августа 2017 г. 10:39 + в цитатник


Перевод статьи Валинды Чен.

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

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


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

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

Допустим, мы создаём простую функцию selectEvenNumbers, которая берёт массив чисел и возвращает массив evenNumbers с одними лишь чётными числами. Если чётных чисел в исходном массиве нет, то массив evenNumbers возвращается пустым.

function selectEvenNumbers() {
  // здесь ваш код
}

Какие вопросы можно себе задать:

  • Как компьютер может сказать, что число является чётным? Разделить на 2 и проверить, чтобы результат получился целым.
  • Что я передаю этой функции? Массив.
  • Что содержит этот массив? Одно или несколько чисел.
  • Какие типы данных у элементов массива? Числа.
  • Какова цель этой функции? Что я возвращаю в конце её выполнения? Цель — получить все чётные числа и вернуть их в массиве. Если нет чётных чисел, то массив возвращается пустым.

2. Пройдите по задаче вручную как минимум с тремя наборами данных


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

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

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

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

[1]
[1, 2]
[1, 2, 3, 4, 5, 6]
[-200.25]
[-800.1, 2000, 3.1, -1000.25, 42, 600]

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

Давайте пройдём по массиву [1]

  1. 1. Смотрим на единственный элемент массива [1].
  2. 2. Определяем, является ли он чётным. Не является.
  3. 3. Замечаем, что других элементов в массиве нет.
  4. 4. Определяем, что здесь нет чётных чисел.
  5. 5. Возвращаем пустой массив.

Теперь пройдём по массиву [1, 2]

  1. Смотрим на первый элемент массива [1, 2]
  2. Это 1.
  3. Определяем, является ли он чётным. Не является.
  4. Смотрим на следующий элемент.
  5. Это 2.
  6. Определяем, является ли он чётным. Является.
  7. Создаём массив evenNumbers и добавляем в него 2.
  8. Замечаем, что других элементов в массиве нет.
  9. Возвращаем массив evenNumbers — [2].

Можно пройти по задаче ещё несколько раз. Обратите внимание, что количество шагов в алгоритме для [1] отличается от алгоритма для [1, 2]. Поэтому рекомендуется проходить по нескольким наборам данных. Например, с единственным элементом; смесь целых и нецелых чисел; многоразрядные числа; наборы с отрицательными числами.

3. Упрощайте и оптимизируйте свой алгоритм


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

  1. 1. Создадим функцию selectEvenNumbers.
  2. 2. Создадим новый пустой массив evenNumbers для хранения чётных чисел.
  3. 3. Проходим по каждому элементу массива [1, 2].
  4. 4. Находим первый элемент.
  5. 5. Делим его на 2 и определяем, чётный ли он. Если да, то прибавляем к evenNumbers.
  6. 6. Находим следующий элемент.
  7. 7. Повторяем шаг №4.
  8. 8. Повторяем шаги №5 и №4, пока не кончатся элементы в массиве.
  9. 9. Возвращаем массив evenNumbers вне зависимости от того, есть ли в нём что-то.

Этот подход напоминает математическую индукцию:

  1. 1. Доказываем истинность для n = 1, n = 2, ...
  2. 2. Предполагаем, что будет истинно для n = k.
  3. 3. Доказываем истинность для n = k + 1.



4. Пишите псевдокод


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

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

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

Вот пример псевдокода, в основном состоящего из слов:

function selectEvenNumbers
создаём массив evenNumbers и делаем его эквивалентным пустому массиву
для каждого элемента в этом массиве
  смотрим, является ли элемент чётным
   если чётный (при делении на 2 результат получается нецелым)
     добавляем его к массиву evenNumbers
return evenNumbers

А вот псевдокод, в котором слов гораздо меньше:

function selectEvenNumbers
evenNumbers = []
for i = 0 to i = length of evenNumbers
  if (element % 2 === 0) 
    добавляем его к массиву evenNumbers
return evenNumbers

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

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

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


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

Если вы писали на бумаге, то перенесите всё в редактор в виде комментариев, а затем замените каждую строку.

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

selectEvenNumbers([1])
selectEvenNumbers([1, 2])
selectEvenNumbers([1, 2, 3, 4, 5, 6])
selectEvenNumbers([-200.25])
selectEvenNumbers([-800.1, 2000, 3.1, -1000.25, 42, 600])

После каждой переменной или строки можно использовать console.log(). Это поможет проверить, ведут ли себя значения и код так, как ожидается, прежде чем двигаться дальше. Таким образом вы выловите любые проблемы, не зайдя слишком далеко. Вот пример того, какие значения можно проверить при начале работы.

function selectEvenNumbers(arrayofNumbers) {
let evenNumbers = []
 console.log(evenNumbers) // Удаляем после проверки выходных данных
 console.log(arrayofNumbers) // Удаляем после проверки выходных данных
}

Ниже приведён код, полученный после обработки каждой строки псевдокода. Символы // обозначают строки из псевдокода. Жирным выделен реальный код на JavaScript.

// function selectEvenNumbers
function selectEvenNumbers(arrayofNumbers) {
// evenNumbers = []
  let evenNumbers = []
// for i = 0 to i = length of evenNumbers
  for (var i = 0; i < arrayofNumbers.length; i++) {
// if (element % 2 === 0) 
    if (arrayofNumbers[i] % 2 === 0) {
// добавляем его к массиву evenNumbers
      evenNumbers.push(arrayofNumbers[i])
    }
  }
// return evenNumbers
  return evenNumbers
}

Уберём псевдокод, чтобы не путаться.

function selectEvenNumbers(arrayofNumbers) {
  let evenNumbers = []
for (var i = 0; i < arrayofNumbers.length; i++) {
    if (arrayofNumbers[i] % 2 === 0) {
      evenNumbers.push(arrayofNumbers[i])
    }
  }
return evenNumbers
}

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



6. Упрощайте и оптимизируйте код


Возможно, вы заметили, что упрощение и оптимизация — это повторяющиеся темы.

«Простота — предпосылка надёжности».

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

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

function selectEvenNumbers(arrayofNumbers) {
  let evenNumbers = arrayofNumbers.filter(n => n % 2 === 0)
  return evenNumbers
}

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

Задавайте себе такие вопросы:

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

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

Джеральд Сассман и Гарольд Абельсон, авторы “Structure and Interpretation of Computer Programs”

7. Отлаживайте


Этот шаг необходимо выполнять в ходе всего процесса. Сквозная отладка поможет раньше выловить любые ошибки синтаксиса или недостатки логики. Воспользуйтесь преимуществами своего IDE (Integrated Development Environment) и отладчика. При обнаружении бага рекомендуется просматривать код построчно, стараясь найти неожиданные вещи. Несколько советов:

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

«Самый эффективный инструмент отладки — тщательное продумывание в сочетании с разумно размещёнными командами вывода на экран».

Брайан Керниган, профессор информатики в Принстонском университете

8. Пишите полезные комментарии


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

Избегайте таких комментариев:

// Это массив. Итерируем его.
// Это переменная.

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

  • Для чего этот код?
  • Что он делает?

9. Получайте отзывы посредством ревизии кода


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

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

Дядя Боб Мартин, программный инженер и соавтор манифеста Agile

10. Практикуйтесь, практикуйтесь, практикуйтесь


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

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

Майкл Джозефсон, основатель Института этики Джозефа и Эдны Джозефсон
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335666/


Метки:  

[Из песочницы] Подробное руководство по созданию и развертыванию чата на Tornado + Telegram

Вторник, 15 Августа 2017 г. 10:05 + в цитатник
Данное решение подойдет для небольших проектов, так как возможность параллельно вести диалог с несколькими пользователями реализована с помощью создания нового чат-бота, то есть чем больше ботов будет, тем больше людей смогут с вами связаться в один момент времени.

Внутреннее устройство чата
image
Рис.1 UML диаграмма последовательностей

Реализация


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

Подготовка виртуального окружения


Если у вас не стоит virtualenv, то необходимо его установить:

pip install virtualenv

Создадим виртуальное окружение:

virtualenv --no-site-packages -p python3.4 chat

Активируем его:

source chat/bin/activate

Установим все необходимые библиотеки для работы нашего чата:

pip install tornado==4.4.2 psycopg2==2.7.3 pyTelegramBotAPI==2.2.3

Для опроса сервера будем использовать библиотеку для работы с telegram.

Необходимо создать следующую файловую структуру:



Создание бота


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

Чтобы зарегистрировать бота, необходимо написать BotFather /newbot и все дальнейшие инструкции вы получите в диалоге с ним. В итоге, после успешной регистрации BotFather вернет вам токен вашего нового бота.

Теперь необходимо получить свой chat_id, чтобы бот знал, кому отправлять сообщения.
Для этого в приложении telegram находим своего бота, начинаем с ним взаимодействие командой /start, пишем ему какое-то сообщение и переходим по ссылке —

https://api.telegram.org/bot<токен_вашего_бота>/getUpdates

Видим примерно следующий ответ —

{"id":555455667,"first_name":"Иван","last_name":"Иванович","username":"kamrus","language_code":"ru-RU"}
id и есть ваш chat_id


Настройка postgres


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

Переключаемся на пользователя postgres:

sudo su - postgres

Входим в CLI postgres:

psql

Необходимо создать новую базу данных в кодировке Unicode;

CREATE DATABASE habr_chat ENCODING 'UNICODE';

Создадим нового пользователя в БД:

CREATE USER habr_user WITH PASSWORD '12345';

И отдадим ему все привилегии на базу:

GRANT ALL PRIVILEGES ON DATABASE habr_chat TO habr_user;

Подключаемся к только что созданной базе:

\c habr_chat

Создадим таблицу для хранения информации ботах, она будет иметь следующую модель:

Физическая модель

Рис.2 Физическая модель таблицы chat

CREATE TABLE chat (
	id SERIAL NOT NULL PRIMARY KEY,
	token character varying(300) NOT NULL UNIQUE,
	ready BOOLEAN NOT NULL DEFAULT True,
	last_message TEXT,
	customer_asked BOOLEAN NOT NULL DEFAULT False,
	remote_ip character varying(100)
)

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

GRANT ALL PRIVILEGES ON TABLE chat TO habr_user; 

Теперь необходимо добавить в нее токены ботов:

INSERT INTO chat (token) VALUES ('your_bot_token');

Выходим из CLI:

\q

и меняем пользователя обратно:

exit

Написание кода


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

bot_settings.py

CHAT_ID = Вставить ваш chat_id 
db = {
	'db_name': 'habr_chat',
	'user': 'habr_user',
	'password': '12345',
	'host': '',
	'port': ''
}

Основные функции будут находиться в файле core.py

from telebot import apihelper
from bot_settings import db
import psycopg2
import datetime

def get_updates(token, conn, cur, offset=None, limit=None, timeout=20):
    ''' Возвращает сообщение из телеграма '''
    json_updates = apihelper.get_updates(token, offset, limit, timeout)
    try:
        answer = json_updates[-1]['message']['text']
    except IndexError:
        answer = ''
    # если не проверять приходило ли сообщение от пользователя, то
    # функция будет просто возвращать последнее сообщение от менеджера,
    # которое в свою очередь могло предназначаться предыдущему клиенту
    if is_customer_asked(conn, cur, token):
        # необходимо проверять предыдущее сообщение, так как запрос к серверу 
        # повторяется через константное время и клиенту будет отправляться одно и тоже сообщение
        if not is_last_message(conn, cur, token, answer):
            # если сообщение прошло обе проверки то обновить это сообщение
            # в базе данных
            update_last_message(conn, cur, token, answer)
            return answer
    else:
        # если пользователь еще ничего не спросил, то необходимо все равно обновить 
        # предыдущее сообщение менеджера, на случай если предыдущии пользовватель отключится,
        # но менеджер все равно отправит сообщение
        update_last_message(conn, cur, token, answer)

def send_message(token, chat_id, text):
    '''Отправить сообщение менеджеру в телеграм'''
    apihelper.send_message(token, chat_id, text)

def connect_postgres(**kwargs):
    try:
        conn = psycopg2.connect(dbname=db['db_name'],
                                user=db['user'],
                                password=db['password'],
                                host=db['host'],
                                port=db['port'])
    except Exception as e:
        print(e, 'Ошибка при подключении к posqgres')
        raise e
    cur = conn.cursor()
    return conn, cur
    
def update_last_message(conn, cur, token, message, **kwargs):
    ''' Обновляет последнее сообщение, присланное менеджером '''
    query = "UPDATE chat SET last_message = %s WHERE token = %s"
    data = [message, token]
    try:
        cur.execute(query, data)
        conn.commit()
    except Exception as e:
        print(e, 'Ошибка при попытке обновить последнее сообщение на %s' %message)
        raise e

def add_remote_ip(conn, cur, token, ip):
    ''' Функция добавляет ip адрес пользователя '''
    query = "UPDATE chat SET remote_ip = %s WHERE token = %s"
    data = [ip, token]
    try:
        cur.execute(query, data)
        conn.commit()
    except Exception as e:
        print(e, 'Ошибка при попытке добавить ip адрес')
        raise e

def delete_remote_ip(conn, cur, token):
    ''' Удалить ip адрес у бота по переданному токену '''
    query = "UPDATE chat SET remote_ip = %s WHERE token = %s"
    data = ['', token]
    try:
        cur.execute(query, data)
        conn.commit()
    except Exception as e:
        print(e, 'Ошибка при попытке удалить ip адрес')
        raise e

def is_last_message(conn, cur, token, message, **kwargs):
    ''' Проверить является ли переданное сообщение последним сообщением менеджера '''
    query = "SELECT last_message FROM chat WHERE token = %s"
    data = [token, ]
    try:
        cur.execute(query, data)
        last_message = cur.fetchone()
        if last_message:
            if last_message[0] == message:
                return True
        return False
    except Exception as e:
        print(e, 'Ошибка при определении последнего сообщения')
        raise e

def update_customer_asked(conn, cur, token, to_value):
    ''' Обновить статус ответа клиента '''
    query = "UPDATE chat SET customer_asked = %s WHERE token = %s"
    # to_value = True/False
    data = [to_value, token]
    try:
        cur.execute(query, data)
        conn.commit()
    except Exception as e:
        print(e, 'Ошибка при попытке обновить "customer_asked" на %s' %to_value)
        raise e

def is_customer_asked(conn, cur, token):
   ''' Если клиент уже написал сообщение, то функция вернет True '''
    query = "SELECT customer_asked FROM chat WHERE token = %s"
    data = [token, ]
    try:
        cur.execute(query, data)
        customer_asked = cur.fetchone()
        return customer_asked[0]
    except Exception as e:
        print(e, "Ошибка при попытке узнать написал ли пользователь сообщение или еще нет")
        raise e

def get_bot(conn, cur):
    '''
    Функция берет из базы свободного бота, у которого ready = True.
    Возвращает (id, token, ready, last_message, customer_asked) для свободного бота
    '''
    query = "SELECT * FROM chat WHERE ready = True"
    try:
        cur.execute(query)
        bot = cur.fetchone()
        if bot:
            return bot
        else:
            return None
    except Exception as e:
         print(e, "Ошибка при попытке найти свободного бота")
        raise e

def make_bot_busy(conn, cur, token):
    ''' Меняет значение ready на False, тем самым делая бота занятым '''
    query = "UPDATE chat SET ready = False WHERE token = %s"
    data = [token,]
    try:
        cur.execute(query, data)
        conn.commit()
    except Exception as e:
        print(e, 'Ошибка при попытке изменить значение "ready" на False')
        raise e

def make_bot_free(conn, cur, token):
     ''' Меняет значение ready на False, тем самым делая бота свободным '''
    update_customer_asked(conn, cur, token, False)
    delete_remote_ip(conn, cur, token)
    query = "UPDATE chat SET ready = True WHERE token = %s"
    data = [token,]
    try:
        cur.execute(query, data)
        conn.commit()
    except Exception as e:
        print(e, 'Ошибка при попытке изменить значение "ready" на True')
        raise e

tornadino.py

import tornado.ioloop
import tornado.web
import tornado.websocket
import core
from bot_settings import CHAT_ID
import datetime

class WSHandler(tornado.websocket.WebSocketHandler):
    def __init__(self, application, request, **kwargs):
        super(WSHandler, self).__init__(application, request, **kwargs)
        # При создании нового подключения с пользователем подключимся к postgres 
        self.conn, self.cur = core.connect_postgres()
        self.get_bot(self.conn, self.cur, request.remote_ip)

    def get_bot(self, conn, cur, ip):
        while True:
            bot = core.get_bot(conn, cur)
            if bot:
                self.bot_token = bot[1]
                self.customer_asked = bot[4]
                # занять бота
                core.make_bot_busy(self.conn, self.cur, self.bot_token)  
                # добавить боту ip адрес
                core.add_remote_ip(self.conn, self.cur, self.bot_token, ip)
                break     

    def check_origin(self, origin):
        ''' Дает возможность подключаться с различных адресов '''
        return True

    def bot_callback(self):
        ''' Функция вызывается PeriodicCallback и проверяет сервер Telegram на 
            наличие новых сообщений от менеджера
        '''
        ans_telegram = core.get_updates(self.bot_token, self.conn, self.cur)
        if ans_telegram:
            # если пришло сообщение от менеджера, то отправить его в браузер клиенту
            self.write_message(ans_telegram)

    def open(self):
        ''' Функция вызываемая при открытии сокета с клиентом '''
        # Запускает опрос сервера Telegram каждые 3сек
        self.telegram_loop = tornado.ioloop.PeriodicCallback(self.bot_callback, 3000)
        self.telegram_loop.start()
    
    def on_message(self, message):
        ''' Функция вызываемая, когда по сокету приходит сообщение '''
        if not self.customer_asked:
            self.customer_asked = True
            # обновить значение в бд, что клиент задал вопрос
            core.update_customer_asked(self.conn, self.cur, self.bot_token, True)
        core.send_message(self.bot_token, CHAT_ID, message)
        
    def on_close(self):
        ''' Функция вызываемая при закрытии соединения '''
        core.send_message(self.bot_token, CHAT_ID, "Пользователь закрыл чат")
        # остановить PeriodicCallback
        self.telegram_loop.stop()
        # освободить бота
        core.make_bot_free(self.conn, self.cur, self.bot_token)

# WebSocket будет доступен по адресу ws://127.0.0.1:8080/ws
application = tornado.web.Application([
  (r'/ws', WSHandler),
])

if __name__ == "__main__":
    application.listen(8080)
    tornado.ioloop.IOLoop.current().start()

Теперь создадим статические файл:
chat.html
Посмотреть код


chat.css
Посмотреть код
.chatbox {
    position: fixed;
    bottom: 0;
    right: 30px;
    height: 400px;
    background-color: #fff;
    font-family: Arial, sans-serif;

    -webkit-transition: all 600ms cubic-bezier(0.19, 1, 0.22, 1);
    transition: all 600ms cubic-bezier(0.19, 1, 0.22, 1);

    display: -webkit-flex;
    display: flex;

    -webkit-flex-direction: column;
    flex-direction: column;
}

.chatbox-down {
    bottom: -350px;
}
.chatbox--closed {
    bottom: -400px;
}
.chatbox .form-control:focus {
    border-color: #1f2836;
}
.chatbox__title,
.chatbox__body {
    border-bottom: none;
}
.chatbox__title {
    min-height: 50px;
    padding-right: 10px;
    background-color: #1f2836;
    border-top-left-radius: 4px;
    border-top-right-radius: 4px;
    cursor: pointer;
    display: -webkit-flex;
    display: flex;
    -webkit-align-items: center;
    align-items: center;
}
.chatbox__title h5 {
    height: 50px;
    margin: 0 0 0 15px;
    line-height: 50px;
    position: relative;
    padding-left: 20px;

    -webkit-flex-grow: 1;
    flex-grow: 1;
}
.chatbox__title h5 a {
    color: #fff;
    max-width: 195px;
    display: inline-block;
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.chatbox__title h5:before {
    content: '';
    display: block;
    position: absolute;
    top: 50%;
    left: 0;
    width: 12px;
    height: 12px;
    background: #4CAF50;
    border-radius: 6px;
    -webkit-transform: translateY(-50%);
    transform: translateY(-50%);
}
.chatbox__title__tray,
.chatbox__title__close {
    width: 24px;
    height: 24px;
    outline: 0;
    border: none;
    background-color: transparent;
    opacity: 0.5;
    cursor: pointer;
    -webkit-transition: opacity 200ms;
    transition: opacity 200ms;
}
.chatbox__title__tray:hover,
.chatbox__title__close:hover {
    opacity: 1;
}
.chatbox__title__tray span {
    width: 12px;
    height: 12px;
    display: inline-block;
    border-bottom: 2px solid #fff
}
.chatbox__title__close svg {
    vertical-align: middle;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 1.2px;
}
.chatbox__body,
.chatbox__credentials {
    padding: 15px;
    border-top: 0;
    background-color: #f5f5f5;
    border-left: 1px solid #ddd;
    border-right: 1px solid #ddd;

    -webkit-flex-grow: 1;
    flex-grow: 1;
}
.chatbox__credentials {
    display: none;
}
.chatbox__credentials .form-control {
    -webkit-box-shadow: none;
    box-shadow: none;
}
.chatbox__body {
    overflow-y: auto;
}
.chatbox__body__message {
    position: relative;
}
.chatbox__body__message p {
    padding: 15px;
    border-radius: 4px;
    font-size: 14px;
    background-color: #fff;
    -webkit-box-shadow: 1px 1px rgba(100, 100, 100, 0.1);
    box-shadow: 1px 1px rgba(100, 100, 100, 0.1);
}
.chatbox__body__message img {
    width: 40px;
    height: 40px;
    border-radius: 4px;
    border: 2px solid #fcfcfc;
    position: absolute;
    top: 15px;
}
.chatbox__body__message--left p {
    margin-left: 15px;
    padding-left: 30px;
    text-align: left;
}
.chatbox__body__message--left img {
    left: -5px;
}
.chatbox__body__message--right p {
    margin-right: 15px;
    padding-right: 30px;
    text-align: right;
}
.chatbox__body__message--right img {
    right: -5px;
}
.chatbox__message {
    padding: 15px;
    min-height: 50px;
    outline: 0;
    resize: none;
    border: none;
    font-size: 12px;
    border: 1px solid #ddd;
    border-bottom: none;
    background-color: #fefefe;
    width: 100%;
}
.chatbox--empty {
    height: 262px;
}
.chatbox--empty.chatbox-down {
    bottom: -212px;
}
.chatbox--empty.chatbox--closed {
    bottom: -262px;
}
.chatbox--empty .chatbox__body,
.chatbox--empty .chatbox__message {
    display: none;
}
.chatbox--empty .chatbox__credentials {
    display: block;
}
.description {
	font-family: Arial, sans-serif;
	font-size: 12px; 
}
#start-ws {
    margin-top: 30px;
}
.no-visible {
    display: none;
}


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

Html код для сообщении от клиента:

Посмотреть код



Html код для сообщении от менеджера:

Посмотреть код



chat.js
Посмотреть код
(function($) {
    $(document).ready(function() {
        var $chatbox = $('.chatbox'),
            $chatboxTitle = $('.chatbox__title'),
            $chatboxTitleClose = $('.chatbox__title__close'),
            $chatboxWs = $('#start-ws');
        // Свернуть чат при нажатии на заголовок и наоборот
        $chatboxTitle.on('click', function() {
            $chatbox.toggleClass('chatbox-down');
        });
        // Закрыть чат
        $chatboxTitleClose.on('click', function(e) {
            e.stopPropagation();
            $chatbox.addClass('chatbox--closed');
            // Если на момент закрытия был открыт сокет, то 
            // следует закрыть его
            if (window.sock) {
                window.sock.close();
            }
        });
        // Подключиться к сокету
        $chatboxWs.on('click', function(e) {
            e.preventDefault();
            // сделать диалог видимым
            $chatbox.removeClass('chatbox--empty');
            // сделать кнопку начала чата невидимой
            $chatboxWs.addClass('no-visible');
            if (!("WebSocket" in window)) {
                alert("Ваш браузер не поддерживает web sockets");
            }
            else {
                alert("Начало соединения");
                setup();
            }
        });
    });
})(jQuery);

// Функция создания соединения по WebSocket
function setup(){
    var host = "ws://62.109.2.175:8084/ws";
    var socket = new WebSocket(host); 
    window.sock = socket;  
    var $txt = $("#message");
    var $btnSend = $("#sendmessage");
    // Отслеживать изменения в textarea 
    $txt.focus();
    $btnSend.on('click',function(){
        var text = $txt.val();
        if(text == ""){return}
        // отправить сообщение по сокету
        socket.send(text);
        // отобразить в дилоге сообщение
        clientRequest(text);
        $txt.val(""); 
        // $('#send')
    });
    // отслеживать нажатие enter 
    $txt.keypress(function(evt){
        // если был нажат enter
        if(evt.which == 13){
            $btnSend.click();
        }
    });

    if(socket){
        // действие на момент открытия сокета
        socket.onopen = function(){
    }
        // действие на момент получения сообщения по сокету
        socket.onmessage = function(msg){
            // отобразить сообщение в диалоге
            managerResponse(msg.data);
        }
        // действия на момент закрытия сокета
        socket.onclose = function(){
            webSocketClose("The connection has been closed.");
            window.sock = false;
        }
    }else{
        console.log("invalid socket");
    }
}
function webSocketClose(txt){
    var p = document.createElement('p');
    p.innerHTML = txt;
    document.getElementById('messages__box').appendChild(p); 
}
//функция для ответов клиента
function clientRequest(txt) {
    $("#messages__box").append("

" + txt + "

"); } // Функция для ответов менеджера function managerResponse(txt) { $("#messages__box").append("

" + txt + "

"); }


Развертывание на centos7


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

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

Настраиваем postgres


Если у вас на сервере не установлен postgres, то установить его можно так:

sudo yum install postgresql-server postgresql-devel postgresql-contrib

Запускаем postgres:

sudo postgresql-setup initdb
sudo systemctl start postgresql

Добавляем автозапуск:

sudo systemctl enable postgresql

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

Будем запускать наше tornado приложение с помощью supervisor в фоне.

Для начала установим supervisor:

sudo yum install supervisor

Теперь откроем конфигурационный файл супервизора, который будет находится в /etc/supervisor.conf

[unix_http_server]
file=/path/to/supervisor.sock ; (the path to the socket file)

[supervisord]
logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10 ; (num of main logfile rotation backups;default 10)
loglevel=error ; (log level;default info; others: debug,warn,trace)
pidfile=/path/to/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false ; (start in foreground if true;default false)
minfds=1024 ; (min. avail startup file descriptors;default 1024)
minprocs=200 ; (min. avail process descriptors;default 200)
user=root
childlogdir=/var/log/supervisord/ ; ('AUTO' child log dir, default $TEMP)

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///path/to/supervisor.sock ; use a unix:// URL for a unix socket

[program:tornado-8004]
environment=PATH="/path/to/chat/bin"
command=/path/to/chat/bin/python3.4 /path/to/tornadino.py --port=8084
stopsignal=KILL
stderr_logfile=/var/log/supervisord/tornado-stderr.log
stdout_logfile=/var/log/supervisord/tornado-stdout.log

[include]
files = supervisord.d/*.ini


Не забудьте поменять пути в конфигурационном файле!

Перед тем, как запускать supervisor, необходимо создать папку /var/log/supervisord/ в ней будут собираться логи торнадо, так что, если supervisor запустил tornado-8004, но чат не работает, то ошибку стоит искать там.

Запускаем супервизор:

sudo supervisorctl start tornado-8004

Проверяем, что все в порядке:

sudo supervisorctl status

Должны получить что-то подобное:

tornado-8004 RUNNING pid 32139, uptime 0:08:10

На локальной машине вносим изменения в chat.js:

var host = "ws://адресс_вашего_сервера:8084/ws";

и открываем в браузере chat.html.

Готово!

Можно без особых телодвижений прикручивать такой чат к своим проектам, так же достаточно удобно использовать для сбора feedback.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335660/


Метки:  

Что может чат-бот

Вторник, 15 Августа 2017 г. 10:04 + в цитатник
Сначала мы выделили основные офисные процессы. Про чат-бота мы даже не говорили. Вот, например, заказ командировок. Сейчас я должна написать специалисту службы деловых поездок в почту: «Я собираюсь в командировку в Сургут 5-го числа на три дня», а он: «Такой-то самолет и такая-то гостиница — всё подходит?», а я: «Да, давай». Дальше он пойдет согласовывать с руководством, забронирует сам билеты, спустя какое-то пришлёт мне подтверждение, что все Ок. Всё то же самое может делать бот.

Или если нужна справка для визы, то бот постучит в шину, шина постучит в кадровую подсистему и заберёт PDF, дальше отправит его на принтер отдела кадров и напишет письмо, что туда нужна печать. Затем уведомит самого сотрудника, что можно подойти через пару часов. Если нужно оформить пропуск на гостя или забронировать переговорку для встречи, то достаточно поручить боту эту задачу, и он выполнит её.



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




Через 3 недели:


В нашем диалоге про командировку используется типовой бизнес-процесс. Сотрудник (его Ф. И. О. есть в данных аутентификации) указывает город командировки и даты. Бот пишет тикет в бухгалтерию и письмо специалисту, отвечающему за покупку билетов и бронирование в поездках. Когда тикет закрыт, бот получает его результат и дальше обрабатывает по скрипту. Специалиста бот обрабатывает письмами или сообщениями в Телеграм, пока тот не выдаст документы с билетами и бронью. Затем он ищет пользователя по хотспоту Wi-Fi в офисе, чтобы определить ближайший доступный принтер. Отправляет документы на печать. В день вылета напоминает о поездке, забирает погоду с одного из внешних сайтов по его открытому API, затем начинает опрашивать онлайн-табло аэропорта на предмет расписания по рейсу (сам рейс есть в билетах). После поездки он попросит отчёт по средствам для бухгалтерии (пришлёт форму и расскажет, как его заполнить), плюс попросит полезные советы для базы знаний. Вдобавок он может заказать такси в аэропорт, заказать машину на месте и подобрать данные о поездках, чтобы внести суммы сразу в авансовый отчёт.

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



Ещё немного магии



Разговорная речь
Боты умеют обучаться на синтаксисе пользователей. Вот простой пример того, как выглядит обращение в Help Desk через бота:



А это скриншот заявки в ITSM:



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

За несколько лет накопилось довольно много пар из словесных запросов в свободной форме и правильно заполненных форм ITSM-системы. Дальше всё просто: немного машинного обучения по этой выборке, а затем бот вместе с тикетом пользователя отправляет уже тип и подтип заявки в систему. Естественно, он может ошибаться, поэтому процесс пока поменялся незначительно — просто сотрудник хелпдеска получает уже заполненные поля (в 80% случаев правильно заполненные), но, конечно, может поменять их значения.

Администратор тоже может использовать бота в своей работе, например, для этого:



Голос


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

Интеграция с любыми процессами для многих пользователей


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


Например, чат-бот, человек или IVR — это интерфейс данных, пользовательская задача — «закажи переговорку на 15:00». Коннектор — авторизационное звено. Шина выполняет реализацию задачи.

Ещё возможности


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

Поиск по адресной книге:



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





Тут тоже всё относительно просто. Боту можно сказать «напомни мне про обед с Ивановым», и он поставит это в задачи, чтобы вовремя напомнить. Остальные данные доступны в шине.



Как всё начиналось


Всё началось с идеи выяснить, что же такое цифровая трансформация, диджитализация и т. д., а то шума на рынке много, а конкретики никакой. Для того чтобы понять, что это, мы решили начать с себя и понять, что же у нас в компании можно «диджитализировать». В ходе «копания темы» мы проработали кучу кейсов для диджитализации, нашли решения, но при этом нам не давала покоя идея «правильного» интерфейса для офисных бизнес-процессов. Про чат-бота мы даже не говорили, в принципе даже не обсуждалось. В какой-то момент поняли, что и подход к взаимодействию c офисными бизнес-процессами тоже надо менять, всё должно быть удобным и обязательно мобильным, и так как 100 мобильных приложений не сделать, то стало понятно, что будущее за естественными интерфейсами, в частности чатами. В частности, есть невероятный китайский пример WeChat — изначально аналога ICQ, который теперь стал и соцсетью, и торговой площадкой, и доской объявлений, и электронной почтой, и вообще всем.

Ещё боты могут рассказывать базовые вещи из базы знаний корпоративного обучения, искать учебные материалы, показать ближайшие внутренние курсы и семинары, играть в список ачивок с новичками («Получил свои канцтовары», «Нашёл кухню», «Зачекинился в спортзале» и так далее), забронировать комнату отдыха или встать в очередь к игровой консоли там же, получить пароль для гостевого Wi-Fi, узнать номер ДМС, поликлиники по нему, телефон скорой, сообщить офисной медслужбе о травме, записаться к офисной медслужбе, забронировать тренажёр, сообщить о потерянной или найденной вещи (бот потом коннектит людей или скидывает лог секретарю), отправить корпоративные новости, дать разную информацию новым сотрудникам, как жить, предупредить про дни рождения ваших контактов. В дальних планах — проверить длину очереди в столовой по видеоаналитике (обычно от 1 до 15 минут, сейчас это реализовано отдельным информером у нас в офисе), узнать меню на сегодня в столовой или заказать пиццу в офис, найти попутчиков, передать документы. От технической службы в планах — заменить корпоративную SIM-карту, сменить тариф, принять багрепорты на любые вещи вроде «тут на пятом этаже лампа не горит», отправить багрепорт в хозяйственную службу или уборщицам.

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

Про авторизацию же история долгая и богатая на приключения, поэтому если интересно — расскажу отдельно. Ну, и если есть вопросы не для комментариев — пишите: digital@croc.ru.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335650/


Зачем бэкап? У нас же RAID

Вторник, 15 Августа 2017 г. 10:02 + в цитатник


В корпоративные блоги принято писать success story — это положительно влияет на образ компании. К сожалению, не всегда в работе инженера всё заканчивается happy end-ом.
Надо сказать, что коллеги уже начинают подшучивать, что я «притягиваю» проблемы. Тем или иным образом я поучаствовал почти во всех проблемных заявках за последнее время. И теперь хочу рассказать одну поучительную историю из своей практики.

История началась с того, что меня попросили проанализировать производительность дискового массива IBM Storwize v7000 gen1, «тормоза» которого парализовали работу целого филиала. Исходная ситуация такая:

  • На массиве находятся датасторы VMware-фермы.
  • Все тома располагаются на RAID5 (диски 7200 и 10000) и зеркалируются между двумя идентичными массивами.
  • Контракта с IBM на поддержку этого оборудования нет.
  • Версия прошивки массива — 7.3.0.4 (актуальная на тот момент 7.6.1.1).
  • Также СХД IBM Storwize используется для виртуализации СХД HP EVA.

Согласно логам производительности массива, «тормоза» возникали не из-за повышенной нагрузки. Я заподозрил, что причиной проблем является вышедший из строя контроллер на виртуализованной СХД HP EVA. Обычно проблемы с производительностью решаются удалённо, но в данном случае решили отправить инженера на место (тогда ещё никто не подозревал, что командировка затянется на две недели).

И тут в ходе анализа производительности начал проявляться «полтергейст»: у томов с массива в интерфейсе vSphere периодически отображается неверный объём (от отрицательного до десятков петабайт), что заказчик расценил как проблему в массиве. При этом пропадал доступ к консолям части виртуальных машин, и возникают другие неприятности. Даже я уже начал нервничать, а заказчик просто в ауте.

И тут начинается просто фейерверк проблем.

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



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

Напоследок выясняется, что в MS SQL Server Express закончилось свободное место, из-за чего возник «полтергейст» с доступностью консолей VM в vSphere и неверно отображались размеры томов. Так что пока администраторы решали проблемы БД, мы пытались разобраться с СХД.

После некоторых действий основной том вдруг ушёл в оффлайн.

Мы вспоминили про баг в прошивках Storwize версий 7.3, 7.4 и 7.5, из-за которого на сжатых томах после определённого количества обращений могут появиться битые блоки (в этой ситуации не может помочь ни отказоустойвость RAID, ни зеркалирование томов на соседний массив, так как ошибка находится уровнем выше).

И вот тут проявился самый интересный нюанс: оказывается, что СРК у заказчика не работает уже 3 месяца. То есть бэкапы есть, но они не актуальные, и восстанавливаться из них — всё равно, что потерять данные.

Нам удалось перевести том в онлайн (через CLI массива), но при первой же попытке хоста что-то записать, он снова упал. Мы отключили все датасторы на серверах и следующие сутки провели в офисе, почти не дыша копируя все виртуальные машины куда получится — на серверы, USB-диски и ПК.

В результате нам удалось спасти все данные, кроме ВМ, на которой запустили консолидацию снапшотов, так как в процессе консолидации LUN ушёл в оффлайн, и вместо данных ВМ осталась «каша». По закону подлости это оказалась ВМ электронного документооборота. Кроме того, для исключения разных рисков пришлось обновить почти всю инфраструктуру — VMware, Brocade, HP Blade, IBM Storwize.

Предпосылки катастрофы


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

  1. СХД была спроектирована некорректно. Один том на ~12 Тб не будет нормально работать ни на одной классической СХД. Всегда разбивайте общую ёмкость на тома порядка 1-2 Тб. Да, будет меньше полезной ёмкости, но зато будет гораздо меньше шансов открыть заявку «у нас всё тормозит».
  2. Прошивки никогда не обновлялись. Это не единственная история, когда баг в старой прошивке приводил к простоям или потере данных. Да, в новых прошивках тоже есть баги, но вас никто не принуждает пользоваться bleeding edge. Используйте стабильные, рекомендованные версии.
  3. Бэкапы. Сколько было просьб и рекомендаций делать и проверять бэкапы — не счесть. Повторяться не хочется, но ВСЕГДА ДЕЛАЙТЕ И СВОЕВРЕМЕННО ПРОВЕРЯЙТЕ БЭКАП. В этой истории можно было сократить время простоя не менее, чем в два раза, если бы СРК поддерживалась в рабочем состоянии.



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

Спасибо за внимание, работы вам без сбоев.

Алексей Трифонов Tomatos, инженер Сервисного центра компании «Инфосистемы Джет».
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335618/


Метки:  

Визуализация данных Московской Биржи с помощью InterSystems DeepSee. Часть I

Вторник, 15 Августа 2017 г. 09:49 + в цитатник

Введение


В стеке технологий InterSystems есть технология для разработки аналитических решений DeepSee. Это встраиваемая аналитическая технология и набор инструментов для создания систем поддержки принятия эффективных решений, в том числе, и с применением прогнозных моделей. DeepSee работает со структурированными и неструктурированными данными. Она предназначена для создания OLAP-решений для баз данных Cach'e и любых реляционных СУБД. InterSystems DeepSee предоставляет разработчикам средства для внедрения в свои приложения аналитической OLAP-функциональности, которая способна работать на оперативных базах данных приложений без создания отдельной инфраструктуры для решения аналитических задач.


В статье рассматривается пример создания в OLAP-куба, работа со средствами аналитики и построение пользовательского интерфейса на примере анализа котировок акций торгуемых на Московской Бирже.


Этапы


  • Получение данных
  • ETL
  • Построение куба
  • Построение сводной таблицы
  • Построение дашборда
  • Визуализация

Получение данных


Для визуализации данных о котировках акций необходимо их сначала загрузить. У Московской Биржи есть публичное задокументированное API, которое предоставляет информацию о торговле акциями в форматах HTML, XML, JSON, CSV.
Вот, к примеру, XML данные за 27 мая 2013 года. Создадим XML-Enabled класс Ticker.Data в платформе InterSystems:


Ticker.Data
Class Ticker.Data Extends (%Persistent, %XML.Adaptor)
{

/// Дата торгов
Property Date As %Date(FORMAT = 3, XMLNAME = "TRADEDATE", XMLPROJECTION = "attribute");

/// Краткое название компании
Property Name As %String(XMLNAME = "SHORTNAME", XMLPROJECTION = "attribute");

/// Тикер
Property Ticker As %String(XMLNAME = "SECID", XMLPROJECTION = "attribute");

/// Количество сделок
Property Trades As %Integer(XMLNAME = "NUMTRADES", XMLPROJECTION = "attribute");

/// Общая сумма сделок
Property Value As %Decimal(XMLNAME = "VALUE", XMLPROJECTION = "attribute");

/// Цена открытия
Property Open As %Decimal(XMLNAME = "OPEN", XMLPROJECTION = "attribute");

/// Цена закрытия
Property Close As %Decimal(XMLNAME = "CLOSE", XMLPROJECTION = "attribute");

/// Цена закрытия официальная
Property CloseLegal As %Decimal(XMLNAME = "LEGALCLOSEPRICE", XMLPROJECTION = "attribute");

/// Минимальная цена акции
Property Low As %Decimal(XMLNAME = "LOW", XMLPROJECTION = "attribute");

/// Максимальная цена акции
Property High As %Decimal(XMLNAME = "HIGH", XMLPROJECTION = "attribute");

/// Средневзвешенная цена акции http://www.moex.com/s1194
/// Может считаться как за день так и не за период.
Property Average As %Decimal(XMLNAME = "WAPRICE", XMLPROJECTION = "attribute");

/// Количество акций участвовавших в сделках
Property Volume As %Integer(XMLNAME = "VOLUME", XMLPROJECTION = "attribute");

}

И напишем загрузчик данных в формате XML. Так как класс у нас XML-Enabled то конвертация из XML в объекты класса Ticker.Data происходит автоматически. Аналогичного поведения можно достичь для данных в форматах JSON (через динамические объекты) и CSV (используя %SQL.Util.Procedures). Так как API отдаёт данные за определённую дату (день) то нам надо итерировать по дням и сохранять поступающие данные. Кроме того данные о котировках акций приходят страницами по 100 записей. Загрузчик может выглядеть так:


Загрузчик данных
/// Загрузить информацию об акциях начиная с From и заканчивая To. Purge - удалить все записи перед началом загрузки
/// Формат From, To - YYYY-MM-DD
/// Write $System.Status.GetErrorText(##class(Ticker.Loader).Populate())
ClassMethod Populate(From As %Date(DISPLAY=3) = "2013-03-25", To As %Date(DISPLAY=3) = {$ZDate($Horolog,3)}, Purge As %Boolean = {$$$YES})
{
    #Dim Status As %Status = $$$OK
    // Переводим даты во внутренний формат для простоты итерации
    Set FromH = $ZDateH(From, 3)
    Set ToH = $ZDateH(To, 3)

    Do:Purge ..Purge()

    For DateH = FromH:1:ToH {
        Write $c(13), "Populating ", $ZDate(DateH, 3)
        Set Status = ..PopulateDay(DateH)
        Quit:$$$ISERR(Status)
    }

    Quit Status
}

/// Загрузить данные за день. Данные загружаются страницами по 100 записей. 
/// Write $System.Status.GetErrorText(##class(Ticker.Loader).PopulateDay($Horolog))
ClassMethod PopulateDay(DateH As %Date) As %Status
{
    #Dim Status As %Status = $$$OK

    Set Reader = ##class(%XML.Reader).%New()
    Set Date = $ZDate(DateH, 3) // Преобразовать дату из внутреннего формата в YYYY-MM-DD
    Set Count = 0 // Число загруженных записей

    While Count '= $G(CountOld) {
        Set CountOld = Count
        Set Status = Reader.OpenURL(..GetURL(Date, Count)) // Получаем следующую страницу данных
        Quit:$$$ISERR(Status)

        // Устанавливаем соответствие нода row == объект класса Ticker.Data 
        Do Reader.Correlate("row", "Ticker.Data")

        // Десериализуем каждую ноду row в объект класса Ticker.Data
        While Reader.Next(.Object, .Status) {
            #Dim Object As Ticker.Data

            // Сохраняем объект
            If Object.Ticker '="" {
                Set Status = Object.%Save()
                Quit:$$$ISERR(Status)
                Set Count = Count + 1
            }
        }
        Quit:(Count-CountOld)<100 // На текущей странице меньше 100 записей => эта страница - последняя
    }
    Quit Status
}

/// Получить URL с информацией о котировках акций за дату Date, пропустить первые Start записей
ClassMethod GetURL(Date, Start As %Integer = 0) [ CodeMode = expression ]
{
$$$FormatText("http://iss.moex.com/iss/history/engines/stock/markets/shares/boards/tqbr/securities.xml?date=%1&start=%2", Date, Start)
}

Теперь загрузим данные командой: Write $System.Status.GetErrorText(##class(Ticker.Loader).Populate())


Весь код доступен в репозитории.


ETL


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


Таблица фактов для куба обычно является результатом работы аналитиков и разработчиков по процессу, который называется ETL (extract, transform, load). Т.е. из данных предметной области делается “выжимка” необходимых для анализа данных, и переносится в удобную для хранилища структуру "звезда"/"снежинка": факты и справочники фактов.
В нашем случае этап ETL пропустим т.к. наш класс Ticker.Data уже находятся во вполне удобном для создания куба состоянии.


Построение куба


DeepSee Architect — это веб-приложение для создания OLAP-куба. Для перехода к DeepSee Architect откроем Портал Управления Системой -> DeepSee -> Выбор области -> Architect. Открывается рабочее окно Архитектора.


Возможно нужно будет выбрать область, которая поддерживает DeepSee. В том случае если вы не видите вашей области в списке областей DeepSee перейдите в Портал Управления Системой -> Меню -> Управление веб-приложениями -> /csp/область, и там в поле Включен поставьте галочку DeepSee и нажмите кнопку сохранить. После этого выбранная область должна появиться в списке областей DeepSee.


Создаем новый куб.


Нажав на кнопку "Создать" попадаем на экран создания нового куба, там необходимо установить следующие параметры:


  • Имя куба — название куба используемое в запросах к нему
  • Отображаемое Имя — локализуемое название куба (перевод осуществляется стандартными механизмами InterSystems)
  • Источник Cube — использовать таблицу фактов или другой куб в качестве источника данных
  • Исходный класс — если на предыдущем шаге был выбран класс, то указываем в качестве таблицы фактов класс Ticker.Data.
  • Имя класса для куба — имя класса, в котором будет храниться определение куба. Создаётся автоматически
  • Описание класса — произвольное описание

Вот как выглядит наш новый куб:


Создание куба


Определяем свойства куба


После нажатия кнопки OK будет создан новый куб:


Новый куб


Слева выводятся свойства базового и связанных с ним по “снежинке” классов, которые можно использовать при построении куба.
Центральная часть экрана — это скелет куба. Его можно наполнить свойствами класса с помощью drag-n-drop из области базового класса, либо добавляя элементы вручную. Основными элементами куба являются измерения, показатели и списки.


Измерения (Dimensions)


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


Для разбиения фактов по тикерам прекрасно подойдет свойство Ticker.
Перетянем Ticker на область измерений — в результате Архитектор добавит в куб измерение Ticker с одной иерархией H1 и одним уровнем Ticker. Укажем отображаемые названия в подписях к измерению и уровню.


Измерение


Измерения помимо группировки позволяют строить иерархии вложенности фактов от общего к частному. Типичным примером является измерение по дате, которое обычно часто требуется представить в виде иерархии Год-Месяц-День.
Для свойств типа дата(например как у свойства Date тип %Date) в DeepSee есть специальный тип измерения time, в котором уже предусмотрены часто используемые функции для создания иерархий по дате. Воспользуемся этим и построим трехуровневую иерархию Год-месяц-день с помощью свойства Date.


Измерение 2


Заметим, что в измерении есть элементы: собственно измерение, иерархия и уровни этой иерархии (Level). Любое измерение куба состоит как минимум из одной иерархии в котором в простейшем случае всего один уровень.


Показатели (Measures)


Показатели или метрики это такие элементы куба, куда относят какие-либо "количественные" данные, которые необходимо посчитать для "качественных" измерений куба (Dimensions).
Например в таблице фактов такими показателями могут быть свойства Volume (количество акций) и Average (Средняя цена). Перетянем свойство Volume на область показателей и создадим показатель "Количество" с функцией SUM, которая будет считать общее количество акций в текущем срезе.
Добавим также в показатели свойство Average и укажем в качестве функции расчета MAX — расчет максимального значения. С целью использования цены для визуализации изменения максимальной цены акции во времени.


Показатели


Списки (Listings)


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


Теперь зададим поля таблицы фактов, которые надо выводить. Например выведем информацию о тикерах и колебаних их цены за день (Name, Ticker, "Open", CloseLegal, Low, Average, High):


Листинг 2


Компиляция куба


Итак мы добавили в куб два показателя, два измерения и один листинг — этого вполне достаточно и уже можно посмотреть, что получилось.
Скомпилируем класс куба (Кнопка "Компилировать"). Если ошибок компиляции нет, значит куб создан правильно и можно наполнить его данными.
Для этого нужно нажать "Построить куб" — в результате DeepSee загрузит данные из таблицы фактов в хранилище данных куба.
Для работы с данными куба нам пригодится другое веб-приложение — DeepSee Analyzer.


Построение сводной таблицы (Pivot)


DeepSee Analyzer — визуальное средство для непосредственного анализа данных кубов и подготовки источников данных для дальнейшей визуализации. Для перехода к DeepSee Analyzer откроем Портал Управления Системой -> DeepSee -> Выбор области -> Analyzer. Открывается рабочее окно Аналайзера.
Analyzer
В рабочем окне Аналайзера слева мы видим элементы созданного куба: показатели и измерения. Комбинируя их мы строим запросы к кубу на языке MDX — аналоге языка SQL для многомерных OLAP кубов.
Рассмотрим интерфейс Аналайзера. Справа — поле сводной таблицы. В поле сводной таблицы Аналайзера всегда показывается результат выполнения MDX-запроса. Посмотреть текущий MDX-запрос можно если нажать кнопку Source. При первом открытии куба в поле сводной таблицы по умолчанию показывается количество записей в таблице фактов — в нашем случае это количество записей в классе Ticker.Data. Этому соответствует MDX: SELECT FROM [TICKER].


Чтобы создать сводную таблицу перетянем в поле колонок измерение “Год”. Показателем выберем "Объём". В результате получим таблицу количества проданных акций по годам.
Pivot 1
Далее перетянем измерение “Тикер” в поле колонок и получим уже сводную таблицу количества акций по инструментам, с разбиением по годам:
Pivot 2


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


Pivot 3


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


Pivot 4


На предыдущем этапе мы создали листинг — инструмент перехода от агрегированных данных к исходным фактам. Выберем любую строку сводной таблицы и нажмём кнопку Drillthrough для перехода к листингу:
Drillthrough 2


Следующий этап — визуализация. Перед сохранением упростим сводную таблицу и сохраним её под именем TickersByYears.
Pivot 5


Построение дашборда (Dashboard)


Портал Пользователя — это веб-приложение для создания и использования дашбордов (панелей индикаторов). Дашборды содержат виждеты: таблицы, графики и карты на основе сводных таблиц, созданных аналитиками в Аналайзере.
Для перехода к Порталу Пользователя DeepSee откроем Портал Управления Системой -> DeepSee -> Выбор области -> Портал Пользователя.
Portal
Создадим новый дашборд нажав на стрелку справа -> добавить -> Добавить индикаторную панель
Portal2


Создадим виджет нажав на стрелку справа -> Виджеты -> "+" -> Линейная диаграмма с маркерами. В качестве источника данных выберем TickersByYears:
Widget


Однако читатель возразит — это же средняя температура по больнице. И будет прав. Добавим фильтрацию по инструменту. Для этого нажмём стрелку справа -> Виджеты -> Виджет 1 -> Элементы управления -> "+". Форма создания нового фильтра выглядит следующим образом:
Filter


А вот так выглядит наш виджет с фильтром. Пользователь может изменить значение фильтра на любое другое.
Widget 2


После этого сохраним дашборд.


Установка MDX2JSON и DeepSeeWeb


Для визуализации созданного дашборда можно использовать следующие OpenSource решения:


  • MDX2JSON — REST API предоставляет информацию о кубах, пивотах, дашбордах и многих других элементах DeepSee, в частности — результатах исполнения MDX запросов, что позволяет встраивать пользовательский интерфейс аналитического решения на DeepSee в любое современное Web или мобильное приложение.
  • DeepSeeWeb — AngularJS приложение, предоставляющее альтернативную реализацию портала пользователя DeepSee. Может быть легко кастомизирован. Использует MDX2JSON в качестве бэкэнда. Вот пример дашборда визуализированного в DeepSeeWeb:

DeepSeeWeb


Установка MDX2JSON


Для установки MDX2JSON надо:


  1. Загрузить Installer.xml и импортировать его в любую область с помощью Studio, Портала Управления Системой или Do $System.OBJ.Load(file).
  2. Выполнить в терминале (пользователем с ролью %ALL): Do ##class(MDX2JSON.Installer).setup()

Для проверки установки надо открыть в браузере страницу http://server:port/MDX2JSON/Test?Debug. Возможно потребуется ввести логин и пароль (в зависимости от настроек безопасности сервера). Должна открыться страница с информацией о сервере. В случае получения ошибки, можно почитать на Readme и Wiki.


Установка DeepSeeWeb


Для установки DeepSeeWeb надо:


  1. Загрузить установщик и импортировать его в любую область с помощью Studio, Портала Управления Системой или Do $System.OBJ.Load(file).
  2. Выполнить в терминале (пользователем с ролью %ALL): Do ##class(DSW.Installer).setup()

Для проверки установки надо открыть в браузере страницу http://server:port/dsw/index.html. Должна открыться станица авторизации. В области SAMPLES представлено множество уже готовых дашбордов и все они автоматически отображаются в DeepSeeWeb.


Визуализация


Откроем http://server:port/dsw/index.html и авторизируемся, также нужно указать область с кубом. Откроется список дашбордов, в нашем случае есть только один созданный дашборд "Акции". Откроем его:


DSW


Отображается наш созданный виджет. Для него поддерживается Drilldown и фильтр созданный в Портале Пользователя DeepSee:


DSW 2


Выводы


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


Ссылки


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

https://habrahabr.ru/post/335586/


Метки:  

ЦОД: этапы большой жизни

Вторник, 15 Августа 2017 г. 09:31 + в цитатник
Cегодня мы решили обратить внимание на тему «жизненного цикла» центров обработки данных, рассказать о том, что это такое и как правильное управление им позволяет компаниям сократить не только текущие, но и капитальные затраты. Учитывая современные тенденции к внедрению все новых решений (включая гиперконвергентные системы) и сервисов, значительные трудности в этом процессе могут быть связаны с изменением и адаптацией физической инфраструктуры дата-центров.

Жизненный цикл ЦОДа включает в себя поэтапный процесс развития инженерной инфраструктуры в соответствии с задачами бизнеса и ИТ-процессами и может быть разделен на несколько этапов:

1. Подготовительный: понимание целей создания, формирование концепции решения, выбор площадки;
2. Проектирование в соответствии с выбранной концепцией и характеристиками площадки;
3. Строительство;
4. Эксплуатация;
5. Оценка соответствия и анализ эффективности.




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




Проектирование и строительство


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

Сегодня требуется четко формировать техническое задание, описывая в нём корректные и оптимальные решения, отвечающие задачам бизнеса в перспективе как минимум 5-8 лет. Помочь в подготовке грамотных специалистов может Центр обучения вендора, обладающего соответствующей компетенцией. Так задачей Центра обучения Schneider Electric является ознакомление партнеров и клиентов с технически сложной продукцией компании, особенностями ее применения, установки и эксплуатации в реальных условиях.
ЦОД, как правило, физически не привязан к потребителям его услуг, однако, важно учитывать, что размещение его в неподходящем здании радикально увеличивает стоимость инфраструктуры площадки. Стоит помнить и о том, что окружение ЦОДа может влиять на его надежность.

Типичные ошибки, совершаемые на подготовительном этапе:

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

Важным является понимание ключевых метрик ЦОДа, перспектив его развития, так как инженерные системы должны «пережить» 2-3 поколения ИТ-оборудования. Службу эксплуатации редко вовлекают в проект на этапе строительства. Между тем специалисты по электроснабжению и системам охлаждения должны привлекаться еще на этапе проектных работ и присутствовать в рабочих группах.


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

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

В ИТ-отделе могут быть и свои, внутренние проблемы. Когда разные ИТ-подразделения отвечают за разные компоненты (серверы, СХД, сетевое оборудование, системы охлаждения и пр.) и при этом не имеют четких регламентов и разделения зон ответственности, на площадке возникает хаос.



В этом случае проще «переехать» на новую площадку, чем проводить глубокую модернизацию старой, и уже потом переделывать имеющийся ЦОД, не нарушая работы функционирующих на новом месте ИТ-сервисов. Еще один вариант – на время «капремонта» арендовать площади в коммерческом ЦОДе. Оптимальное решение выбирается в каждом конкретном случае.
В современных ЦОДах за непрерывную и эффективную работу инфраструктуре, как правило, отвечает отдельная команда специалистов. Для автоматизации мониторинга, управления, ведения отчётности, управления перемещениями и контроля эффективности используют инструменты, которые принято обозначать аббревиатурой DCIM (Data Center Infrastructure Management).



Задачи мониторинга событий, состояния оборудования и среды зачастую решаются в первую очередь – для этого применяются такие средства, как контроллеры NetBotz и система мониторинга Data Center Expert. Но сейчас наиболее актуальными становятся задачи эффективного использования ресурсов — от электроэнергии до пространства в машинных залах. Своевременное внедрение эффективных регламентов и контроль использования электроэнергии позволяет не только снизить весьма значительные эксплуатационные затраты и вернуть инвестиции раньше, но и планировать новые приобретения или строительство с учётом оценки реальной, а не «бумажной» эффективности оборудования. В качестве платформы для организации такого подхода можно использовать Data Center Operation – модульное решение с набором всех необходимых функций.



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


Оценка и оптимизация

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

Различные улучшения, такие как построение систем изоляции воздушных коридоров, реорганизация плит фальшпола, устранение в стойках мест с неправильной циркуляцией воздушных потоков с последующей перенастройкой систем кондиционирования, позволяет сократить энергопотребление ЦОДа в реальных проектах на 10-20%. Окупается такая оптимизация (если брать в расчет только энергопотребление) за 3-3,5 года.

Еще один эффект оптимизации – улучшение работы систем охлаждения, когда эффективнее используется установленное ранее оборудование, к примеру, становится возможной установка в стойку большего числа серверов. «Коэффициент полезного использования ЦОДа» можно довести с 70-75% до 90-95%. Такая оптимизация окупается примерно за полгода.

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

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

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

Во многих проектах работа ведется на стыке этапов эксплуатации и оценки. Сегодня у многих заказчиков появляются новые ИТ-задачи, идет информатизация, цифровизация бизнеса, приходится решать различные эксплуатационные проблемы. Справиться с ними помогает помощь экспертов на этапе оценки и проведение аудита ЦОДа. Созданный Schneider Electric в России Региональный центр разработки приложений (Regional Application Center) занимается комплексными решениями для дата-центров. Его основная задача – создание и поддержка устойчивого и эффективного функционирования ЦОДа на всех этапах его жизненного цикла.

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

https://habrahabr.ru/post/335654/


Метки:  

[Перевод] Kubernetes на голом железе за 10 минут

Вторник, 15 Августа 2017 г. 09:00 + в цитатник


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


По ходу этой статьи мы установим Kubernetes 1.6 на реальную (не виртуальную) машину под управлением Ubuntu 16.04 примерно за 10 минут. В результате у вас появится возможность начать изучать взаимодействие с Kubernetes посредством его CLI kubectl.

Обзор Kubernetes:



Компоненты Kubernetes, автор Julia Evans


Что нам понадобится


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

Зайдите на Packet.net и создайте новый проект. Для целей этой статьи нам хватит хоста Type 0 (4 ядра Atom и 8GB RAM за 0,05$/час).


При настройке хоста не забудьте выбрать в качестве ОС Ubuntu 16.04. В отличие от Docker Swarm Kubernetes лучше работает с проверенными временем релизами Docker. К счастью, репозиторий Ubuntu apt содержит Docker 1.12.6.


  • Установите Docker.

$ apt-get update && apt-get install -qy docker.io

Не обновляйте Docker на этом хосте. Использовать более свежие версии для сборки образов можно в инструментарии CI или на ноутбуке.

Установка


  • Установите apt-репозиторий Kubernetes.

$ apt-get update && apt-get install -y apt-transport-https
$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
OK

$ cat </etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main  
EOF

Теперь обновите список пакетов командой apt-get update.


  • Установите kubelet, kubeadm и kubernetes-cni.

kubelet отвечает за выполнение контейнеров на хостах кластера. kubeadm является удобной утилитой для настройки различных компонентов, составляющих кластер, а kubernetes-cni нужен для работы с сетевыми компонентами.


CNI расшифровывается как Container Networking Interface и представляет из себя спецификацию, определяющую взаимодействие сетевых драйверов с Kubernetes.

$ apt-get update
$ apt-get install -y kubelet kubeadm kubernetes-cni

  • Инициализируйте кластер с помощью kubeadm.

Из документации:


kubeadm предназначен для создания сразу «из коробки» безопасного кластера с помощью таких механизмов, как RBAC.

В Docker Swarm по умолчанию есть драйвер оверлейной сети, но с kubeadm решение остается за нами. Команда все еще работает над обновлением инструкций, поэтому я покажу, как использовать драйвер, наиболее похожий на докеровский, — flannel от CoreOS.


Flannel


Flannel позволяет организовать программно определяемую сеть (Software Defined Network, SDN), используя для этого модули ядра Linux overlay и ipvlan.


В Packet машина подключается к двум сетям: первая — это сеть дата-центра, которая соединяет хосты, входящие в определенный регион и проект, а вторая — это выход в Интернет. Брандмауэр по умолчанию не настроен, поэтому при желании ограничить сетевую активность придется настроить iptables или ufw вручную.


Внутренний IP-адрес можно выяснить с помощью ifconfig:


root@kubeadm:~# ifconfig bond0:0  
bond0:0   Link encap:Ethernet  HWaddr 0c:c4:7a:e5:48:d4  
          inet addr:10.80.75.9  Bcast:255.255.255.255  Mask:255.255.255.254
          UP BROADCAST RUNNING MASTER MULTICAST  MTU:1500  Metric:1

Воспользуемся этим внутренним IP-адресом для трансляции Kubernetes API.


$ kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=10.80.75.9 --skip-preflight-checks --kubernetes-version stable-1.6

  • --pod-network-cidr необходим драйверу flannel и определяет адресное пространство для контейнеров.
  • --apiserver-advertise-address определяет IP-адрес, который Kubernetes будет афишировать в качестве своего API-сервера.
  • --skip-preflight-checks позволяет kubeadm не проверять ядро хоста на наличие требуемых функций. Это нужно из-за отсутствия метаданных ядра на хостах Packet.
  • --kubernetes-version stable-1.6 жестко определяет версию кластера (в данном случае 1.6); при желании использовать, например, Kubernetes 1.7 пропустите этот флаг.

Вот что мы должны получить на выходе:


[init] Using Kubernetes version: v1.6.6
[init] Using Authorization mode: RBAC
[preflight] Skipping pre-flight checks
[certificates] Generated CA certificate and key.
[certificates] Generated API server certificate and key.
[certificates] API Server serving cert is signed for DNS names [kubeadm kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 10.80.75.9]
[certificates] Generated API server kubelet client certificate and key.
[certificates] Generated service account token signing key and public key.
[certificates] Generated front-proxy CA certificate and key.
[certificates] Generated front-proxy client certificate and key.
[certificates] Valid certificates and keys now exist in "/etc/kubernetes/pki"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/kubelet.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/controller-manager.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/scheduler.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/admin.conf"
[apiclient] Created API client, waiting for the control plane to become ready
[apiclient] All control plane components are healthy after 36.795038 seconds
[apiclient] Waiting for at least one node to register
[apiclient] First node has registered after 3.508700 seconds
[token] Using token: 02d204.3998037a42ac8108
[apiconfig] Created RBAC rules
[addons] Created essential addon: kube-proxy
[addons] Created essential addon: kube-dns

Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run (as a regular user):

  sudo cp /etc/kubernetes/admin.conf $HOME/
  sudo chown $(id -u):$(id -g) $HOME/admin.conf
  export KUBECONFIG=$HOME/admin.conf

You should now deploy a pod network to the cluster.  
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:  
  http://kubernetes.io/docs/admin/addons/

You can now join any number of machines by running the following on each node  
as root:

  kubeadm join --token 02d204.3998037a42ac8108 10.80.75.9:6443

  • Создайте непривилегированного пользователя.

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


# useradd packet -G sudo -m -s /bin/bash
# passwd packet

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

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


Войдите под учетной записью нового пользователя: sudo su packet.


$ cd $HOME
$ sudo whoami

$ sudo cp /etc/kubernetes/admin.conf $HOME/
$ sudo chown $(id -u):$(id -g) $HOME/admin.conf
$ export KUBECONFIG=$HOME/admin.conf

$ echo "export KUBECONFIG=$HOME/admin.conf" | tee -a ~/.bashrc

  • Примените конфигурацию сети для подов (flannel).

Теперь с помощью kubectl и двух записей из документации flannel мы применим к кластеру конфигурацию сети:


$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel-rbac.yml
clusterrole "flannel" created  
clusterrolebinding "flannel" created

$ kubectl create -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

serviceaccount "flannel" created  
configmap "kube-flannel-cfg" created  
daemonset "kube-flannel-ds" created 

Сеть для подов сконфигурирована.


  • Разрешите использование однохостового (single-host) кластера.

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


$ kubectl taint nodes --all node-role.kubernetes.io/master-

В качестве альтернативы можно было бы добавить в кластер вторую машину, используя join token из вывода kubeadm.

  • Проверьте работоспособность кластера.

Многие компоненты Kubernetes выполняются в виде контейнеров кластера в скрытом пространстве имен kube-system. Вывести информацию о них можно следующим образом:


$ kubectl get all --namespace=kube-system

NAME                                 READY     STATUS    RESTARTS   AGE  
po/etcd-kubeadm                      1/1       Running   0          12m  
po/kube-apiserver-kubeadm            1/1       Running   0          12m  
po/kube-controller-manager-kubeadm   1/1       Running   0          13m  
po/kube-dns-692378583-kqvdd          3/3       Running   0          13m  
po/kube-flannel-ds-w9xvp             2/2       Running   0          1m  
po/kube-proxy-4vgwp                  1/1       Running   0          13m  
po/kube-scheduler-kubeadm            1/1       Running   0          13m

NAME           CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE  
svc/kube-dns   10.96.0.10           53/UDP,53/TCP   14m

NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE  
deploy/kube-dns   1         1         1            1           14m

NAME                    DESIRED   CURRENT   READY     AGE  
rs/kube-dns-692378583   1         1         1         13m 

Как видно из листинга, все сервисы находятся в состоянии Running, значит, с кластером все в порядке. Если эти компоненты находятся в состоянии загрузки из Интернет, они могут быть еще не запущены.


Запустите контейнер


Теперь в кластере можно запустить контейнер. В Kubernetes контейнеры организованы в поды (Pods), которые используют общий IP-адрес, привязаны к одной и той же ноде (хосту) и могут использовать общие тома.


Проверьте, что сейчас у вас нет запущенных подов (контейнеров):


$ kubectl get pods

Теперь с помощью kubectl run запустите контейнер. Мы развернем Node.js- и Express.js-микросервис, генерирующий идентификаторы GUID по HTTP.


Этот код был изначально написан для руководства по Docker Swarm. Соответствующие исходники можно найти по этой ссылке: Scale a real microservice with Docker 1.12 Swarm Mode

$ kubectl run guids --image=alexellis2/guid-service:latest --port 9000
deployment "guids" created  

Теперь в колонке Name можно увидеть, какое имя было назначено новому поду и когда он был запущен:


$ kubectl get pods
NAME                     READY     STATUS    RESTARTS   AGE  
guids-2617315942-lzwdh   0/1       Pending   0          11s  

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


$ kubectl describe pod guids-2617315942-lzwdh
...
Pulling            pulling image "alexellis2/guid-service:latest"  
...

Раз у нас есть работающей контейнер, можно взять его IP-адрес и получать сгенерированные идентификаторы с помощью curl:


$ kubectl describe pod guids-2617315942-lzwdh | grep IP:
IP:        10.244.0.3

$ curl http://10.244.0.3:9000/guid ; echo
{"guid":"4659819e-cf00-4b45-99d1a9f81bdcf6ae","container":"guids-2617315942-lzwdh"}

$ curl http://10.244.0.3:9000/guid ; echo
{"guid":"1604b4cb-88d2-49e2-bd38-73b589da0469","container":"guids-2617315942-lzwdh"}

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


$ kubectl logs guids-2617315942-lzwdh
listening on port 9000  

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


$ kubectl exec -t -i guids-2617315942-lzwdh sh
/ # head -n3 /etc/os-release
NAME="Alpine Linux"  
ID=alpine  
VERSION_ID=3.5.2  
/ # exit

  • Панель инструментов (Dashboard).

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


$ kubectl create -f https://git.io/kube-dashboard
$ kubectl proxy
Starting to serve on 127.0.0.1:8001  

Теперь создадим туннель на хост Packet и откроем в веб-браузере страницу http://localhost:8001/ui/.


$ ssh -L 8001:127.0.0.1:8001 -N



Более подробную информацию можно получить здесь: Dashboard check it out on Github.


Подведем итоги


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


  • Учитесь на примерах.

Руководство Kubernetes by Example, созданное Michael Hausenblas, показалось мне детальным и доступным.


  • Добавьте больше нод.

Состоящий из одной ноды кластер у нас теперь есть, можно начинать добавлять еще ноды Type 0, используя join token, полученный от kubeadm.


  • Сравните с Docker Swarm.

Docker Swarm — это встроенный в Docker CE и EE инструмент оркестровки. Кластер Docker Swarm может быть поднят одной командой. Более подробную информацию можно почерпнуть из моих уроков по Docker Swarm.


Благодарности:


Спасибо @mhausenblas, @_errm и @kubernetesonarm за обратную связь и советы по настройке кластера Kubernetes.


Ссылки:


  1. Оригинал: Kubernetes on bare-metal in 10 minutes.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/334846/


Метки:  

Система управления складом с использованием CQRS и Event Sourcing. Service Layer

Вторник, 15 Августа 2017 г. 00:03 + в цитатник

Тестируем новый механизм синхронизации настроек JetBrains IDEs

Понедельник, 14 Августа 2017 г. 21:23 + в цитатник
Привет, Хабр!

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

До сих пор эту проблему частично решал встроенный плагин Settings Repository. Для того, чтобы, плагин синхронизировал настройки, необходимо самостоятельно создать Git репозиторий (на GitHub или другом сервисе) и указать его в IDE.

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

Для того, чтобы сделать процесс синхронизации настроек более удобным и безопасным, мы разрабатываем новый механизм, который частично опирается на Settings Repository, однако использует для хранения настроек репозиторий на стороне JetBrains. Доступ к этому репозиторию осуществляется посредством JetBrains Account (JBA).

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

Что такое JetBrains Account

JetBrains Account можно использовать для управления лицензиями, доступа к форумам, блогам JetBrains и репозиторию плагинов. Подробнее о том как работает JetBrains Account можно узнать здесь (на английском).

Доступ к плагину

Новый плагин называется IDE Settings Sync и совместим начиная с версии 2017.2.1. Учитывая раннюю стадию готовности плагина, доступ к нему можно получить пока только по приглашениям.

Если у вас уже есть приглашение, вы можете пригласить ваших коллег и друзей через форму на сайте JetBrains Account.

Если у вас нет приглашения, вы можете попросить его отправив письмо на idea-cloudconfig@jetbrains.com. При этом, письмо должно быть отправлено с почтового ящика, привязанного к вашему JetBrains Account.

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

Ограничения плагина на данный момент

Плагин доступен только для платных продуктов (IntelliJ IDEA Ultimate, PhpStorm, PyCharm, CLion, RubyMine, Rider, и т.п.)
Плагин пока не работает вместе с License Server (мы работаем над этим).

Чтобы плагин заработал, необходимо

1. Получить письмо с приглашением



2. Установить плагин

3. Авторизоваться в IDE (или Toolbox App) с помощью JetBrains Account



4. Включить синхронизацию



5. Отправить приглашение хорошему другу



Обратная связь

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

Я и авторы плагина будем рады ответить на вопросы.

Программируйте с удовольствием!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335646/


Метки:  

[Из песочницы] Стоимость недвижимости на тепловых картах

Понедельник, 14 Августа 2017 г. 19:47 + в цитатник

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


Тепловая карта цен Москвы


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


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


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


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


Инструменты


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


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


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



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


Карта цен resta.by


Глядя на эту карту кто может сказать, где дороже, а где дешевле? Я не могу. В общем такое… три из десяти.


Поиски продолжились, и я нагуглил в итоге на стэке вот что: человек задает именно тот вопрос который интересовал меня, а именно, как сделать тепловую карту а не карту интенсивности. И в ответах есть ссылка на JS-библиотеку которая делает то что надо. Для расчетов используется Inverse Distance Weighting. JS — это конечно не шарп, но уже ближе, так что я был очень рад. Особенно после того как “пощупал” все это на jsfiddle и убедился в годности результата. Через несколько часов у меня уже был работающий код на C# (который впоследствии был сильно доработан). Вот ссылка на Гитхаб, если кому надо.


Данные


За время работы портала у меня накопилось более 20 миллионов объектов по всей России (архивные объекты сохраняются навсегда).


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


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


Я так и не смог определиться с оптимальным шагом для компоновки объектов в исходную точку на карте. Пробовал разные варианты от 100 метров до 5 км. Решил оставить на усмотрение пользователей три наиболее интересных варианта: 250, 500 и 1000 метров.


Точки генерируются следующим образом: область рекурсивно делится на 4 прямоугольные секции до тех пор пока размер секции не будет совпадать или чуть-чуть превышать минимальный, либо до тех пор пока в области не останется объектов меньше чем минимально-допустимое количество (например 3). Для секций в которых меньше трех объектов итоговая точка не создается — они искажают общую картину и создают излишнюю “дырявость”, так как часто такие одинокие объекты отличаются по цене от окружающих.


Heat Map points


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


Для каждого из шагов (250, 500, 1000) генерируется свой набор точек. Для каждой точки запоминается список использованных объектов для отображения по клику на карте.


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


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


Тайлы


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


Теперь чтобы отобразить все на карте надо привязать их к соответствующим координатам. Первое что я нашел в поиске — Ground Overlays.


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


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


Границы регионов


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


Увидев результат я подумал что пользователей мало интересуют официальные границы, и, возможно им было бы полезнее видеть и близлежащие области тоже. Пришлось нарисовать свои границы для карт захватывающие как непосредственно города (Москву и Питер) так и ближайшие области. Количество объектов выросло в несколько раз. Теперь их стало около 140 и 50 тысяч для Москвы и СПб соответственно.


Москва:


Moscow Area


Санкт-Петербург:


SPB Area


Для рисования границ и получения их координат я использовал чей-то готовый код в codepen.io с небольшими изменениями. Вот ссылка для Москвы и Питера. После изменения какой-нибудь из точек на карте в окошко снизу вставляется список географических координат в виде удобном для вставки в БД.


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


Kamenny island


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


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


SPB separated areas


Точность тут не важна. Главное — чтобы не было пересечений между областями.


Выбор цветов


Я сделал настраиваемым процесс генерации тепловой карты так чтобы можно было выбирать цвета, настраивать количество уровней, и т.д.
Например для области в 500 на 500 пикселей с установленными 6 точками со значениями от -100 до 100 можно получить такие варианты.


Тестовые точки со значениями:


TestPoints


Те же данные на карте с уровнями:


MapWithTestPoints


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



С ограничением по цветами и без уровней:



С заданными вручную цветами:



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


Результат при шаге в 500 метров выглядит так:


500m Map


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


Чтобы сгенерировать карту только для Москвы при параллельных 6 потоках (на восьмиядерном сервере 3,2 GHz) требовалось более суток. Это совсем неприемлемо, потому что в перспективе регионов будет больше и запуск должен происходить по расписанию, как минимум раз в неделю.


Узкое место в алгоритме — высчитывание цвета для каждого пикселя в тайле. Нужно отсортировать все точки по расстоянию от данного пикселя. То есть массив из 6000 точек приходилось сортировать 256х256 раз. Бессмысленная трата ресурсов. Очевидно, что все точки не нужны, и можно ограничиться ближайшими. Самое простое решение взять, например топ 100 точек отсортированных по расстоянию от центра тайла. Но тут может быть ситуация когда ближайшие 100 точек находятся в группе, например с одной только стороны. Т.е. нужны не просто 100 ближайших, а так чтобы они еще и были расположены вокруг. Вот что я сделал: из середины тайла во все стороны с шагом в 10 градусов распространяются лучи каждый длиной в треть всей карты. Каждый луч растет до тех пор пока в нем не будет как минимум 5 точек, либо он не достигнет предела по длине. Таким образом, гарантированно в итоговом списке будет примерно 150 точек со всех сторон.


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


Bicycle


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


Выглядят они так:



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


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


Скорость работы выросла в разы. На Москву уходит около 3 часов, из них около часа только на обработку данных, остальное непосредственно на рисование.


Просмотр объектов


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


Display points


Заключение


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


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


Ссылки


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


Ссылки из поста

https://habrahabr.ru/post/324596/
https://developers.google.com/maps/documentation/javascript/examples/layer-heatmap
https://resta.by/karta-cen
https://stackoverflow.com/questions/30073977/create-custom-temperature-map-with-front-end-javascript
https://github.com/optimisme/javascript-temperatureMap
https://en.wikipedia.org/wiki/Inverse_distance_weighting
https://jsfiddle.net/mertk/y9gcuf65/
https://github.com/d-sky/HeatMap
https://developers.google.com/maps/documentation/javascript/maptypes?hl=ru#MapCoordinates
https://developers.google.com/maps/documentation/javascript/examples/map-coordinates
https://developers.google.com/maps/documentation/javascript/examples/groundoverlay-simple
https://developers.google.com/maps/documentation/android-api/tileoverlay
https://codepen.io/d-sky/pen/JJqpYe
https://codepen.io/d-sky/pen/PKJzoO
https://www.ventusky.com/?p=9;71;1&l=temperature
https://квартиры-домики.рф/карта-цен


PS


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

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

https://habrahabr.ru/post/335638/


Метки:  

[Из песочницы] Стоимость недвижимости на тепловых картах

Понедельник, 14 Августа 2017 г. 19:47 + в цитатник

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


Тепловая карта цен Москвы


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


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


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


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


Инструменты


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


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


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



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


Карта цен resta.by


Глядя на эту карту кто может сказать, где дороже, а где дешевле? Я не могу. В общем такое… три из десяти.


Поиски продолжились, и я нагуглил в итоге на стэке вот что: человек задает именно тот вопрос который интересовал меня, а именно, как сделать тепловую карту а не карту интенсивности. И в ответах есть ссылка на JS-библиотеку которая делает то что надо. Для расчетов используется Inverse Distance Weighting. JS — это конечно не шарп, но уже ближе, так что я был очень рад. Особенно после того как “пощупал” все это на jsfiddle и убедился в годности результата. Через несколько часов у меня уже был работающий код на C# (который впоследствии был сильно доработан). Вот ссылка на Гитхаб, если кому надо.


Данные


За время работы портала у меня накопилось более 20 миллионов объектов по всей России (архивные объекты сохраняются навсегда).


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


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


Я так и не смог определиться с оптимальным шагом для компоновки объектов в исходную точку на карте. Пробовал разные варианты от 100 метров до 5 км. Решил оставить на усмотрение пользователей три наиболее интересных варианта: 250, 500 и 1000 метров.


Точки генерируются следующим образом: область рекурсивно делится на 4 прямоугольные секции до тех пор пока размер секции не будет совпадать или чуть-чуть превышать минимальный, либо до тех пор пока в области не останется объектов меньше чем минимально-допустимое количество (например 3). Для секций в которых меньше трех объектов итоговая точка не создается — они искажают общую картину и создают излишнюю “дырявость”, так как часто такие одинокие объекты отличаются по цене от окружающих.


Heat Map points


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


Для каждого из шагов (250, 500, 1000) генерируется свой набор точек. Для каждой точки запоминается список использованных объектов для отображения по клику на карте.


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


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


Тайлы


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


Теперь чтобы отобразить все на карте надо привязать их к соответствующим координатам. Первое что я нашел в поиске — Ground Overlays.


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


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


Границы регионов


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


Увидев результат я подумал что пользователей мало интересуют официальные границы, и, возможно им было бы полезнее видеть и близлежащие области тоже. Пришлось нарисовать свои границы для карт захватывающие как непосредственно города (Москву и Питер) так и ближайшие области. Количество объектов выросло в несколько раз. Теперь их стало около 140 и 50 тысяч для Москвы и СПб соответственно.


Москва:


Moscow Area


Санкт-Петербург:


SPB Area


Для рисования границ и получения их координат я использовал чей-то готовый код в codepen.io с небольшими изменениями. Вот ссылка для Москвы и Питера. После изменения какой-нибудь из точек на карте в окошко снизу вставляется список географических координат в виде удобном для вставки в БД.


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


Kamenny island


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


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


SPB separated areas


Точность тут не важна. Главное — чтобы не было пересечений между областями.


Выбор цветов


Я сделал настраиваемым процесс генерации тепловой карты так чтобы можно было выбирать цвета, настраивать количество уровней, и т.д.
Например для области в 500 на 500 пикселей с установленными 6 точками со значениями от -100 до 100 можно получить такие варианты.


Тестовые точки со значениями:


TestPoints


Те же данные на карте с уровнями:


MapWithTestPoints


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



С ограничением по цветами и без уровней:



С заданными вручную цветами:



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


Результат при шаге в 500 метров выглядит так:


500m Map


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


Чтобы сгенерировать карту только для Москвы при параллельных 6 потоках (на восьмиядерном сервере 3,2 GHz) требовалось более суток. Это совсем неприемлемо, потому что в перспективе регионов будет больше и запуск должен происходить по расписанию, как минимум раз в неделю.


Узкое место в алгоритме — высчитывание цвета для каждого пикселя в тайле. Нужно отсортировать все точки по расстоянию от данного пикселя. То есть массив из 6000 точек приходилось сортировать 256х256 раз. Бессмысленная трата ресурсов. Очевидно, что все точки не нужны, и можно ограничиться ближайшими. Самое простое решение взять, например топ 100 точек отсортированных по расстоянию от центра тайла. Но тут может быть ситуация когда ближайшие 100 точек находятся в группе, например с одной только стороны. Т.е. нужны не просто 100 ближайших, а так чтобы они еще и были расположены вокруг. Вот что я сделал: из середины тайла во все стороны с шагом в 10 градусов распространяются лучи каждый длиной в треть всей карты. Каждый луч растет до тех пор пока в нем не будет как минимум 5 точек, либо он не достигнет предела по длине. Таким образом, гарантированно в итоговом списке будет примерно 150 точек со всех сторон.


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


Bicycle


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


Выглядят они так:



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


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


Скорость работы выросла в разы. На Москву уходит около 3 часов, из них около часа только на обработку данных, остальное непосредственно на рисование.


Просмотр объектов


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


Display points


Заключение


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


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


Ссылки


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


Ссылки из поста

https://habrahabr.ru/post/324596/
https://developers.google.com/maps/documentation/javascript/examples/layer-heatmap
https://resta.by/karta-cen
https://stackoverflow.com/questions/30073977/create-custom-temperature-map-with-front-end-javascript
https://github.com/optimisme/javascript-temperatureMap
https://en.wikipedia.org/wiki/Inverse_distance_weighting
https://jsfiddle.net/mertk/y9gcuf65/
https://github.com/d-sky/HeatMap
https://developers.google.com/maps/documentation/javascript/maptypes?hl=ru#MapCoordinates
https://developers.google.com/maps/documentation/javascript/examples/map-coordinates
https://developers.google.com/maps/documentation/javascript/examples/groundoverlay-simple
https://developers.google.com/maps/documentation/android-api/tileoverlay
https://codepen.io/d-sky/pen/JJqpYe
https://codepen.io/d-sky/pen/PKJzoO
https://www.ventusky.com/?p=9;71;1&l=temperature
https://квартиры-домики.рф/карта-цен


PS


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

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

https://habrahabr.ru/post/335638/


Метки:  

Emercoin снизит комиссии на транзакции в 100 раз

Понедельник, 14 Августа 2017 г. 17:28 + в цитатник


Блокчейн-платформа Эмер с собственной криптовалютой Emercoin снизит комиссии на транзакции и создание записей в блокчейне в 100 раз. Функциональная ценность монеты EMC увеличится стократно.

В настоящее время в сети Emer действует фиксированный размер комиссии на транзакции на уровне 0,01 EMC. По текущему курсу Emercoin это составляет $0,01 или около 60 копеек.
После сокращения комиссия на транзакции упадет до 0,0001 доллара или до 0,6 копеек (0,0001 EMC), а стоимость создания произвольных записей в блокчейне Emer составит 0,0005 EMC вместо прежних 0,05 EMC

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

Чтобы начать работать с Emercoin, выберете кошелек для вашей ОС ( Windows, Linux или Mac), так же доступен TestNet Emercoin`a монеты которого бесплатны и доступны любому.

Как это будет осуществлено технически?

По общим правилам, характерным для криптовалют:

  1. Будет предложен новый клиент Emercoin со сниженной комиссией.
  2. При достижении 95% консенсуса участников сети (950 блоков из 1000 последних), сеть перейдет на новые правила.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335630/


Метки:  

Ой, у меня задержка

Понедельник, 14 Августа 2017 г. 17:28 + в цитатник
К нам часто приходят с такой проблемой, но надо сразу уточнить: обычно это мужчины, а мы занимаемся доставкой видео.

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

Прежде чем переходить к обсуждению задержек (оно же latency, delay), надо ответить на очень важный вопрос: а зачем вообще их сокращать. Сокращать задержку хочется почти всегда, но требуется не всегда.

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



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

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

Формирование задержки



Давайте разберемся, как формируется задержка при передаче видео. Примерная схема доставки видео сигнала (схема видеотракта) следующая:

  1. с сенсора камеры снимается изображение в видеопамять
  2. видео энкодер кладет сырое изображение в буфер кодирования
  3. алгоритм сжатия видео находит оптимальный способ компрессии нескольких видео кадров в буфере
  4. сжатый видеокадр отправляется в серверный буфер доставки видео (если такой есть)
  5. видеокадр передается по UDP или копируется в ядерный буфер TCP для отправки
  6. байты долетают до клиента и складываются в ядерный буфер приемки сетевых данных
  7. доходят до клиента
  8. возможно складываются в буфер сортировки кадров
  9. оттуда складываются при необходимости в буфер компенсации флуктуации скорости сети
  10. из него отправляются в декодер, который накапливает свой буфер для b-frames
  11. декодер декодирует кадр и отправляет его на отрисовку


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

Почему? Да потому что буферизация — это обычный способ снизить стоимость, повысить общую пропускную способность. Есть ещё один момент: буфер помогает сгладить колебания. Колеблется скорость передачи по сети – не беда, закачаем на старте побольше байт/кадров и пока интернет восстанавливается, будем играть то, что лежит в буфере.

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

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

Детали



Снятие с сенсора



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

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

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

Точную оценку по задержке здесь дать не готов.

Буфер кодирования



Энкодер занимается крайне ресурсоёмкой задачей, а так же жутко нагружает шину передачи данных между памятью и процессором. Ему надо перебрать разные комбинации вариантов сжатия видео, найти разницу между соседними кадрами и сделать кучу сложных математических вычислений. Учитывая, что FullHD видео на 25 кадрах в секунду — это порядка гигабита в секунду (100 мегабайт), нагрузка огромная. Но просьба не совершать классическую ошибку и не путать нагрузку на процессор с задержкой. Время, которое уходит на сжатие кадра всё равно меньше 1/fps (иначе уже можно не дергаться, всё равно ничего не получится), а задержку энкодер создает гораздо больше.

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

  • поддержание среднеровного битрейта потока. Если в одном кадре очень хочется сделать качество получше, значит на остальных кадрах надо постараться поджаться
  • выбор оптимальных кадров, на которые можно ссылаться. Иногда так бывает, что стоит сослаться не на предыдущий кадр, а на следующий. Таким образом возникают перестановки кадров и экономия трафика до 15-20%


С этой задержкой можно играть, но прежде всего это будет приведет к росту битрейта. Есть хороший пост на покинувшем нас сайте от автора libx264 о low latency кодировании. Вот это оно.

Итого, здесь можно справиться за 1-2 кадра (по 40 мс каждый), а можно и потратить до 3-5 секунд, но сэкономить битрейт.

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

Буфер на сервере



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

На самом деле серверная буферизация вполне бывает, например при упаковке mpegts очень хочется подождать с отправкой аудиокадров, что бы положить несколько кадров в один PES пакет. Или при упаковке таких протоколов, как HLS или DASH вообще надо ждать по несколько секунд.

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

Некоторые серверы буферизуют кадры даже при использовании покадровых протоколов типа RTMP для того, что бы сократить использование CPU, ведь послать один раз 100 килобайт дешевле, чем 2 раза по 50.

Т.е. здесь всё напрямую зависит от протокола: если у нас на сервере HLS или DASH, то буферизация хотя бы сегмента (1-10 секунд) неизбежна. Если покадровый протокол, то не нужно, смело можно рассылать кадры всем клиентам по одному, но всё равно так делают редко.

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

Здесь есть маленькая деталь: существует инициатива CMAF low latency. Суть идеи в том, что когда приходит опорный кадр (он же keyframe), то сервер анонсирует новый сегмент всем клиентам. Все клиенты срочно начинают его скачивать и тут они получают кадр за кадром через http progressive download.

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

Это пока инициатива и в разработке, но может стать интересным.

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

Сетевой буфер на отправку



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

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

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

Увеличиваем ядерные буферы и быстро теряем контроль за скоростью скачивания — становится тяжело контролировать отправку кадров и на сервере становится непонятно: клиент скачивает видео или уже нет.

Если шлем по UDP, то надо решать, чего делать с потерей пакетов. Есть вариант с повторной пересылкой UDP пакетов (эдакий недо-TCP), но он требует буферизации на клиенте (см ниже). Есть вариант с организацией чего-то типа RAID-5 поверх сети: в каждый udp пакет кладется избыточность, позволяющая восстановить один пакет из, скажем, 5 (см FEC, Fountain Codes и т.п.). Это может требовать роста задержки на сервере для вычисления такой избыточности, а так же поднимает битрейт на 10-30%. Считается, что избыточность не требует экстра буфера на клиенте, или по крайней мере он будет 1-2 кадра, но не 5 секунд (125 кадров)

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

Вернемся в реальный мир.

С FEC есть как хорошие обещания так и реалии от гугла: «XOR FEC не работает». Пока непонятно и непонятно уже очень давно. С другой стороны в спутникой доставке FEC давно используется, но там нет никакого другого контроля за ошибками.

С SVC всё хорошо, кроме того, что он никак не взлетит. Напоминает JPEG2000 или вейвлеты: всем хороши, но что-то вот не хватает для покорения мира. По факту используется в закрытых реализациях видеоконференций, где под контролем сервер и клиент, но сходу этим механизмом воспользоваться не получается.

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

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

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


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

Доставка до клиента



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

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

Эта часть может как уложиться в 10 мс, так и растянуться на 300 мс (на таком RTT вообще сложно добиться приличной скорости).

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

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

Продолжение следует. В следующей публикации рассмотрим что происходит у клиента.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335208/


Метки:  

Ой, у меня задержка

Понедельник, 14 Августа 2017 г. 17:28 + в цитатник
К нам часто приходят с такой проблемой, но надо сразу уточнить: обычно это мужчины, а мы занимаемся доставкой видео.

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

Прежде чем переходить к обсуждению задержек (оно же latency, delay), надо ответить на очень важный вопрос: а зачем вообще их сокращать. Сокращать задержку хочется почти всегда, но требуется не всегда.

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



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

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

Формирование задержки



Давайте разберемся, как формируется задержка при передаче видео. Примерная схема доставки видео сигнала (схема видеотракта) следующая:

  1. с сенсора камеры снимается изображение в видеопамять
  2. видео энкодер кладет сырое изображение в буфер кодирования
  3. алгоритм сжатия видео находит оптимальный способ компрессии нескольких видео кадров в буфере
  4. сжатый видеокадр отправляется в серверный буфер доставки видео (если такой есть)
  5. видеокадр передается по UDP или копируется в ядерный буфер TCP для отправки
  6. байты долетают до клиента и складываются в ядерный буфер приемки сетевых данных
  7. доходят до клиента
  8. возможно складываются в буфер сортировки кадров
  9. оттуда складываются при необходимости в буфер компенсации флуктуации скорости сети
  10. из него отправляются в декодер, который накапливает свой буфер для b-frames
  11. декодер декодирует кадр и отправляет его на отрисовку


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

Почему? Да потому что буферизация — это обычный способ снизить стоимость, повысить общую пропускную способность. Есть ещё один момент: буфер помогает сгладить колебания. Колеблется скорость передачи по сети – не беда, закачаем на старте побольше байт/кадров и пока интернет восстанавливается, будем играть то, что лежит в буфере.

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

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

Детали



Снятие с сенсора



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

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

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

Точную оценку по задержке здесь дать не готов.

Буфер кодирования



Энкодер занимается крайне ресурсоёмкой задачей, а так же жутко нагружает шину передачи данных между памятью и процессором. Ему надо перебрать разные комбинации вариантов сжатия видео, найти разницу между соседними кадрами и сделать кучу сложных математических вычислений. Учитывая, что FullHD видео на 25 кадрах в секунду — это порядка гигабита в секунду (100 мегабайт), нагрузка огромная. Но просьба не совершать классическую ошибку и не путать нагрузку на процессор с задержкой. Время, которое уходит на сжатие кадра всё равно меньше 1/fps (иначе уже можно не дергаться, всё равно ничего не получится), а задержку энкодер создает гораздо больше.

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

  • поддержание среднеровного битрейта потока. Если в одном кадре очень хочется сделать качество получше, значит на остальных кадрах надо постараться поджаться
  • выбор оптимальных кадров, на которые можно ссылаться. Иногда так бывает, что стоит сослаться не на предыдущий кадр, а на следующий. Таким образом возникают перестановки кадров и экономия трафика до 15-20%


С этой задержкой можно играть, но прежде всего это будет приведет к росту битрейта. Есть хороший пост на покинувшем нас сайте от автора libx264 о low latency кодировании. Вот это оно.

Итого, здесь можно справиться за 1-2 кадра (по 40 мс каждый), а можно и потратить до 3-5 секунд, но сэкономить битрейт.

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

Буфер на сервере



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

На самом деле серверная буферизация вполне бывает, например при упаковке mpegts очень хочется подождать с отправкой аудиокадров, что бы положить несколько кадров в один PES пакет. Или при упаковке таких протоколов, как HLS или DASH вообще надо ждать по несколько секунд.

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

Некоторые серверы буферизуют кадры даже при использовании покадровых протоколов типа RTMP для того, что бы сократить использование CPU, ведь послать один раз 100 килобайт дешевле, чем 2 раза по 50.

Т.е. здесь всё напрямую зависит от протокола: если у нас на сервере HLS или DASH, то буферизация хотя бы сегмента (1-10 секунд) неизбежна. Если покадровый протокол, то не нужно, смело можно рассылать кадры всем клиентам по одному, но всё равно так делают редко.

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

Здесь есть маленькая деталь: существует инициатива CMAF low latency. Суть идеи в том, что когда приходит опорный кадр (он же keyframe), то сервер анонсирует новый сегмент всем клиентам. Все клиенты срочно начинают его скачивать и тут они получают кадр за кадром через http progressive download.

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

Это пока инициатива и в разработке, но может стать интересным.

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

Сетевой буфер на отправку



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

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

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

Увеличиваем ядерные буферы и быстро теряем контроль за скоростью скачивания — становится тяжело контролировать отправку кадров и на сервере становится непонятно: клиент скачивает видео или уже нет.

Если шлем по UDP, то надо решать, чего делать с потерей пакетов. Есть вариант с повторной пересылкой UDP пакетов (эдакий недо-TCP), но он требует буферизации на клиенте (см ниже). Есть вариант с организацией чего-то типа RAID-5 поверх сети: в каждый udp пакет кладется избыточность, позволяющая восстановить один пакет из, скажем, 5 (см FEC, Fountain Codes и т.п.). Это может требовать роста задержки на сервере для вычисления такой избыточности, а так же поднимает битрейт на 10-30%. Считается, что избыточность не требует экстра буфера на клиенте, или по крайней мере он будет 1-2 кадра, но не 5 секунд (125 кадров)

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

Вернемся в реальный мир.

С FEC есть как хорошие обещания так и реалии от гугла: «XOR FEC не работает». Пока непонятно и непонятно уже очень давно. С другой стороны в спутникой доставке FEC давно используется, но там нет никакого другого контроля за ошибками.

С SVC всё хорошо, кроме того, что он никак не взлетит. Напоминает JPEG2000 или вейвлеты: всем хороши, но что-то вот не хватает для покорения мира. По факту используется в закрытых реализациях видеоконференций, где под контролем сервер и клиент, но сходу этим механизмом воспользоваться не получается.

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

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

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


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

Доставка до клиента



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

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

Эта часть может как уложиться в 10 мс, так и растянуться на 300 мс (на таком RTT вообще сложно добиться приличной скорости).

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

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

Продолжение следует. В следующей публикации рассмотрим что происходит у клиента.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/335208/


Метки:  

[Перевод] Охота на вредоносные npm-пакеты

Понедельник, 14 Августа 2017 г. 14:14 + в цитатник

Метки:  

Поиск сообщений в rss_rss_hh_full
Страницы: 1824 ... 1477 1476 [1475] 1474 1473 ..
.. 1 Календарь