...

суббота, 3 мая 2014 г.

Дайджест интересных материалов из мира веб-разработки и IT за последнюю неделю №107 (27 апреля — 3 мая 2014)

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




Метки лучше разделять запятой. Например: общение, социальные сети, myspace.com, подростки, мердок


или закрыть

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Уникальные предметы из истории космонавтики будут проданы на аукционе



С помощью этого контроллера осуществлялась посадка лунного модуля Аполло-15. Старт — 10 тысяч долларов, лот «в хорошем состоянии».

История космонавтики насчитывает уже много десятилетий, а разные проекты/экспедиции порождают много исторических «космо-артефактов». Недавно на Хабре писали об одном таком: камере, побывавшей на Луне, и вернувшейся на Землю.


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


Среди лотов: управляющая консоль на два терминала из программы Apollo, золотая фольга командного модуля Apollo 13 Odyssey, управляющий ручной контроллер, использовавшийся для прилунения в Apollo 15, полетный комбинезон космонавта Александра Волкова, спасательный костюм/скафандр «Сокол» и многое другое.


Есть и такие редкие лоты, как статья одного из братьев Райт с его личной подписью, кусочек обшивки «Гинденбурга».




Тот самый «Гинденбург», часть его обшивки.




Оригинал статьи одного из братьев Райт с личной подписью.




Чек-лист Нейла Армстронга и База Олдрина.




Управляющая консоль на два терминала из программы Apollo.




Фотография Джеймса Ловелла, руководителя миссии «Аполлон-13» с надписью «Хьюстон, у нас проблемы».




Костюм Александра Волкова, СоюзTM-13.




Памятная ваза, подаренная командой своему руководителю, генерал-полковнику Андрею Григорьевичу Карасю.




Спасательный костюм-скафандр "Сокол КМ".


Via theverge


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Профайлер для Entity Framework


Приветствую!


Продолжу серию статей про программирование. На это раз я хочу поговорить о профилировании и логировании по отношению с Entity Framework-у ( далее EF) c 4-й по 6-ю версию, а также о некоторых примененных мной решениях, таких как декомпилирование .NET кода на лету, форма для обработки исключению и прочее.


Подробности под катом.



Введение



Когда я начинал изучение EF, передо мной стал вопрос в профилировании запросов которые генерирует EF. После некоторого поиска я с неудовольствием обнаружил, что штатных средств EF не предоставляет, соответственно, приходилось пользоваться, на мой взгляд, крайне неудобной в использовании программой SQL Server Profiler или купить достаточно дорогой сторонний инструмент Entity Framework Profiler 2.0, за $389.00, а я жадный.

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



//Регистрируем при старте программы свой IDbCommandInterceptor
DbInterception.Add(new LogFormatter());



public class LogFormatter : IDbCommandInterceptor
{
private readonly Stopwatch _stopwatch = new Stopwatch();
public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
// Перезапускам таймер
_stopwatch.Restart();
}
public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
// Останавливаем таймер
_stopwatch.Stop();
// Логируем команду
Log(command, interceptionContext);
}
public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
_stopwatch.Restart();
}
public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
_stopwatch.Stop();
Log(command, interceptionContext);
}
public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
_stopwatch.Restart();
}
public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
_stopwatch.Stop();
Log(command, interceptionContext);
}
private void Log<TResult>(DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
{
Logger.Log(command, _stopwatch.ElapsedMilliseconds, interceptionContext.Exception);
}
}



public static class Logger
{
public static void Log(DbCommand command, long elapsedMilliseconds, Exception exception)
{
// Выводим в Trace команду
Trace.WriteLine(string.Format("Command:{0}, Elapsed Milliseconds:{1}", command.CommandText, elapsedMilliseconds));
}
}




Для 4-й версии EF похожего механизма логирования я не нашел.
Мое решение



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

После недолгого гугления, была найдена отличная библиотека для профилировки EF в ASP MVC приложениях, под названием MiniProfiler . На его основе и было решено сделать свое приложения.
Что получилось



Система получилась максимально простой для использования и интеграции. Забрать бинарники с примером проекта можно на созданном мной сайте на wordpress-e http://ef-logger.com/

Главное окно приложения:


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



1. Забрать архив с бинарниками и примером проекта



2. Подключить библиотеку EFlogger.EntityFramework4.dll для EF 4-5 версии и EFlogger.EntityFramework6.dll для EF 6-й версии

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

// Для EF 6-й версии
EFloggerFor6.Initialize();
// или для EF с 4-й по 6-ю
EFloggerFor4.Initialize();




4. Запустить приложение EFlogger.exe
API



Пример для библиотеки EFlogger.EntityFramework6.dl, в случае использования EFlogger.EntityFramework4.dl статический класс будет назваться EFloggerFor6

//Инициализация
EFloggerFor6.Initialize();

// Задает IP профалера в случаях его отличие от "127.0.0.1"
EFloggerFor6.SetProfilerClientIP(“192.168.10.50″);

// Посылает текстовое сообщение в профайлер
EFloggerFor6.WriteMessage(“Text message”);

// Стартует посылку логов профайлер (уже запущен по умолчанию после инициализации)
EFloggerFor6.StartSendToClient();

// Останавливает посылку логов профайлер
EFloggerFor6.StopSendToClient();

// Стартует логирование в файл EFlogger.log приложения
EFloggerFor6.StartSaveToLogFile();

// Останавливает логирование в файл EFlogger.log приложения
EFloggerFor6.StopSaveToLogFile();

//Очищает лог в профайлере и удаляет файл лога, если включен
EFloggerFor6.ClearLog();

// Отключает декомпиляцию кода
EFloggerFor6.DisableDecompiling();

// Добавляет сборку для игнорирования при построении Stack Trace-a
EFloggerFor6.ExcludeAssembly(“MyAssemly”);


Как это все работает



Механизм внедрения в EF 4-й версии сильно отличается от EF 6-й версии.

Штатные механизмов подмены DbProviderFactory в EF 4 отсутствуют, поэтому приходится идти на подмену штатной фабрики на свою через рефлекцию.

Сам процесс выглядит так:

// проверяем что все фабрики загруженны
DbProviderFactories.GetFactoryClasses();

// получаем тип DbProviderFactories
Type type = typeof(DbProviderFactories);

DataTable table = null;
//получаем значение DataSet статического поля _configTable или _providerTable
object setOrTable = (type.GetField("_configTable", BindingFlags.NonPublic | BindingFlags.Static) ??
type.GetField("_providerTable", BindingFlags.NonPublic | BindingFlags.Static)).GetValue(null);

var set = setOrTable as DataSet;
if (set != null)
table = set.Tables["DbProviderFactories"];

table = (DataTable)setOrTable;

// для каждой записи в DataTable подставляем свою фабрику
foreach (DataRow row in table.Rows.Cast<DataRow>().ToList())
{
DbProviderFactory factory;
try
{
factory = DbProviderFactories.GetFactory(row);
}
catch (Exception)
{
continue;
}

var profType = EFProviderUtilities.ResolveFactoryType(factory.GetType());
if (profType != null)
{
DataRow profiled = table.NewRow();
profiled["Name"] = row["Name"];
profiled["Description"] = row["Description"];
profiled["InvariantName"] = row["InvariantName"];
profiled["AssemblyQualifiedName"] = profType.AssemblyQualifiedName;
table.Rows.Remove(row);
table.Rows.Add(profiled);
}
}




Для интеграции в EF 6-й версии можно воспользоваться штатным механизмом подстановки сервисов:

DbConfiguration.Loaded += (_, a) => a.ReplaceService<DbProviderServices>((s, k) => new EFProfiledDbProviderServices());


Декомпиляция



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

public static string Get(out StackFrame outStackFrame)
{
outStackFrame = null;
var frames = new StackTrace(0, true).GetFrames();
if (frames == null)
{
return "";
}

var methods = new List<string>();

// проходим по всем фреймам
foreach (StackFrame t in frames)
{
// получаем метод
var method = t.GetMethod();

// получаем сборку и проверяем нужно ли ее пропустить
var assembly = method.Module.Assembly.GetName().Name;
if (ShouldExcludeType(method) || AssembliesToExclude.Contains(assembly) ||
MethodsToExclude.Contains(method.Name))
continue;

// находим первый по стеку фрейм и считаем что именно он сгенерировал команду, если нет нужно добавить имя сборки в список на пропуск
if (outStackFrame == null)
{
outStackFrame = t;
}
methods.Add(method.DeclaringType.FullName + ":" + method.Name);
}
return string.Join("\r\n", methods);
}



private static string GetMethodBody(StackFrame stackFrame)
{
MethodBase methodBase = stackFrame.GetMethod();
return Decompiler.GetSourceCode(methodBase.Module.FullyQualifiedName, methodBase.DeclaringType.Name, methodBase.Name);
}




Decompiler


using System;
using System.IO;
using System.Linq;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.Decompiler.Ast.Transforms;
using Mono.Cecil;

namespace Common
{
public static class Decompiler
{
public static string GetSourceCode(string pathToAssembly, string className, string methodName)
{
try
{
var assemblyDefinition = AssemblyDefinition.ReadAssembly(pathToAssembly);

TypeDefinition assembleDefenition = assemblyDefinition.MainModule.Types.First(type => type.Name == className);
MethodDefinition methodDefinition = assembleDefenition.Methods.First(method => method.Name == methodName);
var output = new StringWriter();
var plainTextOutput = new PlainTextOutput(output);
DecompileMethod(methodDefinition, plainTextOutput);
return output.ToString();
}
catch (Exception exception)
{

return string.Format( "Exception in decompling. \r\n Message:{0}, \r\n Inner Exception:{1}, \r\n StackTrace:{2}",exception.Message, exception.InnerException, exception.StackTrace);
}
}

private static void DecompileMethod(MethodDefinition method, ITextOutput output)
{
AstBuilder codeDomBuilder = CreateAstBuilder(currentType: method.DeclaringType, isSingleMember: true);
if (method.IsConstructor && !method.IsStatic && !method.DeclaringType.IsValueType)
{
AddFieldsAndCtors(codeDomBuilder, method.DeclaringType, method.IsStatic);
RunTransformsAndGenerateCode(codeDomBuilder, output);
}
else
{
codeDomBuilder.AddMethod(method);
RunTransformsAndGenerateCode(codeDomBuilder, output);
}
}

private static AstBuilder CreateAstBuilder(ModuleDefinition currentModule = null, TypeDefinition currentType = null, bool isSingleMember = false)
{
if (currentModule == null)
currentModule = currentType.Module;
var settings = new DecompilerSettings();
if (isSingleMember)
{
settings = settings.Clone();
settings.UsingDeclarations = false;
}
return new AstBuilder(
new DecompilerContext(currentModule)
{
CurrentType = currentType,
Settings = settings
});
}

private static void AddFieldsAndCtors(AstBuilder codeDomBuilder, TypeDefinition declaringType, bool isStatic)
{
foreach (var field in declaringType.Fields)
{
if (field.IsStatic == isStatic)
codeDomBuilder.AddField(field);
}
foreach (var ctor in declaringType.Methods)
{
if (ctor.IsConstructor && ctor.IsStatic == isStatic)
codeDomBuilder.AddMethod(ctor);
}
}

private static void RunTransformsAndGenerateCode(AstBuilder astBuilder, ITextOutput output, IAstTransform additionalTransform = null)
{
astBuilder.GenerateCode(output);
}
}
}





Пример рабочего проекта декомпиляции на яндекс файлах: http://ift.tt/1n99UwN
Окно обработки исключительной ситуации



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

Мой вариант формы выглядит так:


Форма предлагает пользователю добавить информацию об ошибке, шаги для воспроизведения и свой email для ответа.

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



[STAThread]
[STAThread]
static void Main()
{
SetupLogger();
// если дебагер не подключен
if (!Debugger.IsAttached)
{
// отлавливаем все необработанные ошибки
Application.ThreadException += (sender, e) => HandleError(e.Exception);
AppDomain.CurrentDomain.UnhandledException += (sender, e) => HandleError((Exception)e.ExceptionObject);
}

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
private static void HandleError(Exception exception)
{
try
{
// запускаем контроллер формы обернутый try/catch на случай если в контроллере тоже произойдет ошибка
new ErrorHandlerController(exception).Run();
}
catch (Exception e)
{
MessageBox.Show("Error processing exception. Please send log file EFlogger.log to developer: " + Settings.Default.ProgrammerEmail + " \r\n Exception:" + e);
// записываем ошибку в лог файл
Logger.Error(e);
if (MessageBox.Show("Attach debugger? \n Only for developer!!!", "Debugging...", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
Debugger.Launch();
throw;
}
}
finally
{
// обязательно принудительно завершаем приложение чтобы Windows не отображала стандартное окно ошибки приложения
Environment.Exit(1);
}
}

//new ErrorHandlerController(exception).Run();
public void Run()
{

// формируем текст ошибки
string exceptionInfoText = string.Format(
"An unexpected error occurred: {0}" + Environment.NewLine +
"Time: {1} " + Environment.NewLine +
"{2}" + Environment.NewLine +
"InnerException: \n {3}" + Environment.NewLine +
"InnerException StackTrace: \n {4}" + Environment.NewLine,
_exception.Message,
DateTime.Now,
_exception,
_exception.InnerException,
_exception.InnerException != null
? _exception.InnerException.StackTrace
: string.Empty
);

// записываем ошибку с лог файл
Program.Logger.Error(exceptionInfoText);
_view.ExceptionInfoText = exceptionInfoText;

// показываем форму, вызвав метод ShowDialog, в противном случае покажется стандартное окно ошибки приложения
_view.ShowDialog();
}




Пример рабочего проекта с формой обработки ошибок можете забрать на яндекс файлах: http://ift.tt/1n99UwP
Послесловие



На случай если сайт упадет, вы можете сказать архив профайлера опять же, на яндекс файлах: http://ift.tt/1kGckzv

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

Всем спасибо за внимание. Надеюсь этот инструмент будет полезен.


PS: По всем вопросам, баг репортам и прочее можете писать на почту: developer@ef-logger.com или st.glushak@gmail.com. Также, автор ищет подработку, ибо ипотека. Спасибо за понимание.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Sol 610: «живая» панорама Марса и Curiosity


сегодня в 20:24



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


Андрей Бодров, фотограф из Эстонии, собрал детальную панораму из 138 фотографий, сделанных марсоходом Curiosity в Sol 610. Фотографии эти получались посредством одной из камер ровера, MAHLI.


В результате получилась «живая» панорама шириной в 30 тысяч пикселей.


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


Кстати, вот интересное видео от NASA, где рассказывается о том, как именно получают такие фотографии:





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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Профессии в мире информационных технологий. Лекция в Яндексе

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

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






Выбор профессии




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

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


Проблемы образования в IT




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

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


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


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


Как выбрать профессию?




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

Как осваивать профессию




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

Что собой представляет отрасль ИТ




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


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


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


  • Знания:


    • Сети;

    • Протоколы;

    • Оборудование.



  • Задачи:


    • Установка;

    • Настройка;

    • Мониторинг;

    • Обслуживание сетевого оборудования.



  • Роли:


    • Сетевой администратор;

    • Сетевой аналитик;

    • Сетевой инженер.






Информационная безопасность


  • Знания:


    • Протоколы аутентификации;

    • Антивирусы;

    • Криптография;

    • Эксплуатация и изоляция уязвимостей;

    • Нормативная база.



  • Задачи:


    • Мониторинг;

    • Диагностика;

    • Тестирование возможности проникновения;

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



  • Роли:


    • Администратор ИБ;

    • CISO, penterter,...






Внедрение корпоративного ПО и консультирование


  • Знания:


    • Корпоративные языки (Java/.NET/SQL);

    • Прикладные: бухгалтерия, финансы;

    • Оборудование.



  • Задачи:


    • Установка;

    • Настройка ERP/CRM/BI/бухгалтерия...

    • Обслуживание.



  • Роли:


    • Консультант;

    • Аналитик;

    • Разработчик.






Дизайн интерфейсов и опыта взаимодействия


  • Знания:


    • Дизайн;

    • Эргономика;

    • Психология.



  • Задачи:


    • Проектирование интерфейсов;

    • Графический дизайн;



  • Роли:


    • Дизайнер;

    • ...






Обслуживание и поддержка


  • Знания:


    • Сети;

    • Протоколы;

    • Оборудование;

    • Все остальное.



  • Задачи:


    • Мониторинг;

    • Получение обратной связи;

    • Диагностика;

    • Устранение неполадок или эскалация проблемы.



  • Роли:


    • Дежурный администратор;

    • Сотрудник службы поддержки;

    • Инженер службы поддержки.






Разработка и администрирование БД


  • Знания:


    • SQL;

    • Реляционные СУБД;

    • Операционные системы;

    • Проектирование баз данных.



  • Роли:


    • Администратор БД;

    • Разработчик БД;

    • Аналитик/проектировщик запросов;

    • Консультант.






Разработка ПО, веб-служб и приложений


  • Роли:


    • Разработчик (программист)


      • Интерфейсов (frontend),

      • Приложений (Windows, OS X, *nix),

      • Мобильных приложений;



    • Тестировщик


      • Функциональный

      • Нагрузочный.








Системный анализ


  • Задачи:


    • Анализ требований;

    • Постановка задач




    Роли:


    • Технический писатель;

    • Бизнес-аналитик;

    • Системный аналитик;

    • Аналитик программного обеспечения.






Управление проектами


  • Задачи


    • Определение и достижение целей;

    • Поиск баланса «время, цена, качество».




    Роли:


    • Руководитель проекта по...

    • Мастер методологии (Scrum, Agile,...).






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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Ускоряем PHP (с ReactPHP)

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



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


OPcache



Большинство современных иснталяций используют APC/OPcache и это считается стандартом и максимум для PHP. У этого подхода наименьшее количество недостатков, т.к. это нативное (родное) решение предлагаемое нам командой PHP. Всё бы хорошо, но скорости маловато.
HHVM



HHVM действительно хорош, для популярных Linux дистров уже есть репозитории и остаётся только поставить и настроить, что в целом дело не хитрое. Но это разработка от команды facebook и на данный момент, HHVM сильно ограничивает в выборе расширений, а если у вас вдруг свои патчи для PHP расширений то и вовсе ставит «крест» на безболезненном переходе с PHP на HHVM. Про PHP 5.5 так же можно забыть. Стоит отметить отличную работу facebook команды по увеличению совместимости HHVM с основными инструментами и фреймворками, но это цифра всё таки в районе 97%.

Из приходящих мне в голову вариантов остаются еще сырой HippyVM и фреймворк PhalconPHP. О Phalcon написано много обзоров и думаю повторять их нет смысла. HippyVM в стадии разработки, кстати это альтернатива HHVM от самой же facebook команды, написан на python, что на мой взгляд делает этот проект еще более туманным.


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


Классическая инсталляция PHP включает в себя установку одного из Nginx, Apache или Lighttpd веб сервера, которые обрабатывают входящие HTTP запросы и перенаправляют динамические к PHP. Существует несколько вариантов подключения PHP к веб серверу:



  • mod_php (apache)

  • f(ast)cgi

  • php-fpm




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

Но PHP не стоит на месте. У нас появилась своя экосистема с тысячами инструментов которые легко можно установить благодаря Composer. PHP позаимствовал много патернов у таких языков как Java и других, привет команде Symfony & Co. Появились инструменты позволяющие работать PHP асинхронно. На эту тему уже есть статья на хабре, по этому не буду повторятся в описании этого подхода. Скажу только, что асинхронный подход позволяет нам создавать не только чат на websocket, но и запускать полноценный HTTP сервер, а это значит что нам не придётся инициализировать PHP на каждый запрос. Таким образом, не сложно догадаться, что такой подход сведёт на нет затрачиваемое время на старт различных фреймворков и в конечном счёте улучшиться время отклика.


Данное решение, как понятно из заголовка, построено на ReactPHP. Сам React это скорее инструмент для создания, а не готовое решение. Хотя в нём уже есть инструменты для обработки входящих Http соединений, а так же есть различные инструменты, например для работы с websockets или async redis, но нет реализованных привычных для современных фреймворков MVC патерна, роутинга и т.д. Для этих целей мы подключим ReactPHP к уже существующему Symfony2 приложению.


ReactPHP основывается на eventloop и для реализации этой архитектуры предлагает на выбор установить одну из ext-libevent, ext-libev, ext-event. В случае отказа, React работает через stream_select и возможности асинхронности сводятся к минимуму, т.к. по сути всё будет выполняться по очереди без возможности на прерывания процесса. Конечно, можно это опустить, т.к. и так по сути асинхронность, это череда задач в пределах одного процесса. Но если функция будет использовать не блокирующие вызовы, то eventloop базирующийся на stream_select будет вынужден ждать выполнения этой функции, т.к. не возможности прервать функцию на время выполнения не блокирующего вызова, например к async-redis. Конечно это можно обойти разбиением функционала, но суть проблемы ясна.


Я сторонник нативных решений, и инсталляция pecl расширений туда не очень входит. К тому же установка pecl потребуется на всём парке серверов да и на хостингах будут проблемы. А ведь у PHP есть возможность реализации корутин средствами PHP 5.5. Благодаря замечательной статье от nikic (перевод на хабре), я решил впилить свою реализацию eventloop на основе описанного nikic планировщика задач. Да звучит не просто, и с непривычки действительно требует основательного изменения представления построения приложений на PHP. Но на мой взгляд за такими решениями будущее PHP.


Кстати Syмfony был выбран не случайно. Реализация стека обработки входящих запросов Symfony, нам позволяет с лёгкостью работать не убивая PHP после каждого запроса. А пока я допиливал этот пост, предложения с подобной реализацией уже поступают на канале Symfony. И сами разработчики не скрывают, что подобное решение у них теплится в умах с начала запуска 2 версии.


Но давайте перейдём от слов к делу. Для начала нам потребуется ваш любимый Linux дистрибутив с установленными и настроенными nginx, php-cli 5.5.x, composer и вашим приложением на Symfony. Если у вас нет под рукой Symfony приложения, то можно взять голую инсталляцию с Symfony сайта, на которой и будет приведён пример. Если вам и composer не знаком, то вкратце можно ознакомится в моей статье к Satis.


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



mkdir fastapp && cd fastapp




Устанавливаем composer:

curl -sS http://ift.tt/SI3ujS | php




Ставим Symfony2.4.4:

php composer.phar create-project symfony/framework-standard-edition symfdir/ 2.4.4 && mv symfdir/* ./ && rm -fr symfdir




Получаем


ls -l

drwxrwxr-x 6 user user 4.0K Apr 30 11:25 app/
drwxrwxr-x 2 user user 4.0K Apr 30 11:25 bin/
drwxrwxr-x 3 user user 4.0K Mar 14 09:37 src/
drwxrwxr-x 13 user user 4.0K Apr 30 11:25 vendor/
drwxrwxr-x 3 user user 4.0K Apr 30 11:25 web/
-rw-rw-r-- 1 user user 2.0K Mar 14 09:37 composer.json
-rw-rw-r-- 1 user user 56K Apr 30 11:25 composer.lock
-rwxr-xr-x 1 user user 990K Apr 30 11:23 composer.phar*
-rw-rw-r-- 1 user user 1.1K Mar 14 09:37 LICENSE
-rw-rw-r-- 1 user user 5.7K Mar 14 09:37 README.md
-rw-rw-r-- 1 user user 1.3K Mar 14 09:37 UPGRADE-2.2.md
-rw-rw-r-- 1 user user 2.0K Mar 14 09:37 UPGRADE-2.3.md
-rw-rw-r-- 1 user user 356 Mar 14 09:37 UPGRADE-2.4.md
-rw-rw-r-- 1 user user 8.3K Mar 14 09:37 UPGRADE.md







Добавляем такие строчки в ваш composer.json:

{
"repositories": [
{ "type": "vcs", "url": "http://ift.tt/1s0OmCb" },
{ "type": "vcs", "url": "http://ift.tt/1s0OmSp" }
],
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"imunhatep/php-pm": "@dev"
}
}




Чтоб выглядело примерно так


{
"name": "symfony/framework-standard-edition",
"license": "MIT",
"type": "project",
"description": "The \"Symfony Standard Edition\" distribution",
"autoload": {
"psr-0": { "": "src/" }
},
"repositories": [
{ "type": "vcs", "url": "http://ift.tt/1s0OmCb" },
{ "type": "vcs", "url": "http://ift.tt/1s0OmSp" }
],
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": ">=5.3.3",
"symfony/symfony": "~2.4",
"doctrine/orm": "~2.2,>=2.2.3",
"doctrine/doctrine-bundle": "~1.2",
"twig/extensions": "~1.0",
"symfony/assetic-bundle": "~2.3",
"symfony/swiftmailer-bundle": "~2.3",
"symfony/monolog-bundle": "~2.4",
"sensio/distribution-bundle": "~2.3",
"sensio/framework-extra-bundle": "~3.0",
"sensio/generator-bundle": "~2.3",
"incenteev/composer-parameter-handler": "~2.0",
"imunhatep/php-pm": "@dev"
},
"scripts": {
"post-install-cmd": [
"Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile"
],
"post-update-cmd": [
"Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile"
]
},
"config": {
"bin-dir": "bin"
},
"extra": {
"symfony-app-dir": "app",
"symfony-web-dir": "web",
"incenteev-parameters": {
"file": "app/config/parameters.yml"
},
"branch-alias": {
"dev-master": "2.4-dev"
}
}
}







Запускаем обновление пакетов:

php composer.phar update




Получаем


Loading composer repositories with package information
Updating dependencies (including require-dev)
- Installing stack/builder (v1.0.1)
Loading from cache

- Installing react/promise (v2.0.0)
Loading from cache

- Installing guzzle/parser (v3.9.0)
Loading from cache

- Installing evenement/evenement (v2.0.0)
Loading from cache

- Installing react/react (v0.4.1)
Loading from cache

- Installing imunhatep/rephp (dev-master 13adf26)
Cloning 13adf2697681a5954978ac56fe2c8fdf6a21dc4a

- Installing imunhatep/php-pm (dev-master 02f44ec)
Cloning 02f44ecb41ca5b4c81d4bb6087da7a0ed4964656


react/react suggests installing ext-libevent (Allows for use of a more performant event-loop implementation.)
react/react suggests installing ext-libev (Allows for use of a more performant event-loop implementation.)
react/react suggests installing ext-event (Allows for use of a more performant event-loop implementation.)
Writing lock file
Generating autoload files
Updating the "app/config/parameters.yml" file
Clearing the cache for the dev environment with debug true
Installing assets using the hard copy option
Installing assets for Symfony\Bundle\FrameworkBundle into web/bundles/framework
Installing assets for Acme\DemoBundle into web/bundles/acmedemo
Installing assets for Sensio\Bundle\DistributionBundle into web/bundles/sensiodistribution







Подготавливаем Symfony cache:

php app/console cache:warmup --env=dev




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

php bin/ppm start --workers 1 --port 8080




Проверяем, что всё работает открыв в любимом браузере localhost:8080. Должна открыться страничка приветствия от Symfony, правда картинки не покажутся и css неподгрузится. И так мы получили PHP веб сервер, обрабатывающий и входящие запросы и не умирающий. Но у нас только 1 процесс, нет обработки статики и нет балансера. Как многие догадались, для этого нам и понадобится nginx.



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



upstream backend {
server 127.0.0.1:5501;
server 127.0.0.1:5502;
server 127.0.0.1:5503;
server 127.0.0.1:5504;
server 127.0.0.1:5505;
server 127.0.0.1:5506;
server 127.0.0.1:5507;
server 127.0.0.1:5508;
}

server {
root /path/to/symfony/web/;
server_name fastapp.com;

location / {
# try to serve file directly, fallback to rewrite
try_files $uri @rewriteapp;
}

location @rewriteapp {
if (!-f $request_filename) {
proxy_pass http://backend;
break;
}
}
}




При этом server_name (fastapp.com) нужно прописать в /etc/hosts:

127.0.0.1 fastapp.com




Теперь чтоб нам не мучиться с ручным запуском n-количества процессов нашего PHP приложения (представленная nginx конф. настроена на n=8), заходим в папку нашего проекта и выполняем:

cp vendor/imunhatep/rephp/ppm.json ./




Подправляем ./ppm.json файлик:

{
"bootstrap": "\\PHPPM\\Bootstraps\\Symfony",
"bridge": "HttpKernel",
"appenv": "dev",
"workers": 8,
"port": 5501
}




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

app/console cache:warmup --env=dev




Заново запускаем наш PHP Process Manager:

php bin/ppm start




Получаем в ответ:

8 slaves (5501, 5502, 5503, 5504, 5505, 5506, 5507, 5508) up and ready.




Сначала проверяем в браузере линк localhost:5501, если всё открылось то пробуем открыть fastapp.com. Должно всё открываться, с картинками и css.

Теперь можно жечь при помощи тулзы siege или ab, на выбор:



siege -qb -t 30S -c 128 http://fastapp.com/




Приведу несколько результатов тестирования своего (не helloworld) Symfony приложения, на девелоперской машине с AMD 8core, 8RAM и Fedora20.

Php 5.5.10, через nginx + php-fpm:


siege -qb -t 30S -c 128 http://login.dev/signup

Lifting the server siege... done.

Transactions: 983 hits
Availability: 100.00 %
Elapsed time: 29.03 secs
Data transferred: 4.57 MB
Response time: 0.91 secs
Transaction rate: 34.26 trans/sec
Throughput: 0.16 MB/sec
Concurrency: 124.23
Successful transactions: 983
Failed transactions: 0
Longest transaction: 1.81
Shortest transaction: 0.42




Php 5.5.10 с включенным OPcache, через nginx + php-fpm:


siege -qb -t 30S -c 128 http://login.dev/signup
Lifting the server siege... done.

Transactions: 5298 hits
Availability: 100.00 %
Elapsed time: 29.54 secs
Data transferred: 24.15 MB
Response time: 0.70 secs
Transaction rate: 179.35 trans/sec
Throughput: 0.82 MB/sec
Concurrency: 126.43
Successful transactions: 5298
Failed transactions: 0
Longest transaction: 1.68
Shortest transaction: 0.07




Php 5.5.10 с включенным OPcache, через nginx + ReactPHP + Coroutine eventloop:


siege -qb -t 30S -c 128 http://ift.tt/1s0OmSt
Lifting the server siege... done.

Transactions: 30553 hits
Availability: 100.00 %
Elapsed time: 29.85 secs
Data transferred: 157.63 MB
Response time: 0.12 secs
Transaction rate: 1023.55 trans/sec
Throughput: 5.28 MB/sec
Concurrency: 127.43
Successful transactions: 30553
Failed transactions: 0
Longest transaction: 0.76
Shortest transaction: 0.00




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

Php 5.5.10 с включенным OPcache, через nginx + php-fpm


siege -qb -t 30S -c 256 http://login.dev/signup

siege aborted due to excessive socket failure;

Transactions: 134 hits
Availability: 10.48 %
Elapsed time: 1.58 secs
Data transferred: 0.78 MB
Response time: 1.21 secs
Transaction rate: 84.81 trans/sec
Throughput: 0.49 MB/sec
Concurrency: 102.93
Successful transactions: 134
Failed transactions: 1145
Longest transaction: 1.56
Shortest transaction: 0.00




К сожалению php-fpm свалился и отказался работать с лимитом в 32 процесса против 256 параллельных запросов.

Пробуем Php5.5.10 + ReactPHP + Coroutine eventloop


siege -qb -t 30S -c 256 http://ift.tt/1s0OmSt

Lifting the server siege... done.

Transactions: 29154 hits
Availability: 100.00 %
Elapsed time: 29.16 secs
Data transferred: 150.40 MB
Response time: 0.25 secs
Transaction rate: 999.79 trans/sec
Throughput: 5.16 MB/sec
Concurrency: 252.70
Successful transactions: 29154
Failed transactions: 0
Longest transaction: 3.66
Shortest transaction: 0.00




Заключение.




Идея запускать Symfony приложения через ReactPHP не моя, позаимствовал у Marc из его статьи, за что ему большое спасибо. Кстати он делал свои замеры и даже сравнивал с HHVM. Ниже приведён график из его статьи:



Мой вклад заключается в создании eventloop на основе работы nikic и допиливании менеджера процессов до, в целом, работоспособности, а также нюансов запуска ReactPHP с новым eventloop. Возможно с pecl event lib, будет это всё работать быстрее, не проверял. К сожалению мои текущие проекты не соответствуют требуемому качеству кода, вот наработка и пылится на полках «лаборатории», т.к. такой подход требует кода без ошибок. То есть PHP, по сути, не имеет права падать, а всеядность и динамика PHP ни как этому не способствует. Это можно исправить, дописав PHP PM, чтоб перезапускал упавшие процессы, а так же можно дописать отслеживание изменений в коде и также перезапускать процессы. Но пока не востребовано. Так же на этой базе можно запускать и websocket сервер. Что было в планах, но так там и осталось.

Оставлял такой сервер на все выходные под нагрузкой, утечек памяти не было. Есть одна проблема которую пока нет ни времени ни необходимости искать: по каким то причинам, после нагрузки, остаются не закрытыми 1-2 соединения. На малых нагрузка выявить причину не удаётся, а для больших нужно потратить время чтоб придумать как выявить причину. Пока что, добавил таймер, которые каждые 10 секунд проверяет текущие соединения на валидность (ресурс, не ресурс) и убивает мёртвые.


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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Устраняем ошибки при получении от пользователя массива вместо строки


сегодня в 11:38


Недавно я заметил, что на многих сайтах сделанных на Yii Framework есть одна и та же ошибка:

Если в каком нибудь поле ввода в атрибуте name добавить квадратные скобки и отправить форму, то можно получить сообщение об ошибке, типа:

htmlspecialchars() expects parameter 1 to be string, array given

Иногда даже можно увидеть исходный код PHP файлов, если разработчик забыл убрать константу YII_DEBUG.

Эта ошибка присутствует и на крупных проектах и даже на самом http://ift.tt/1fDpkaq



Происходит она из за того, что стандартные правила валидации генерируемые Gii предохраняют только от попытки записи некорректных данных в БД. При выводе пользовательских данных на станицу обычно используется CHtml::encode() но она передает переменную напрямую в PHP функцию htmlspecialchars() и не проверяет её тип.


Одним из способов решения данной проблемы является написание небольшого behavior:



class FilterArraysBehavior extends CModelBehavior
{
public function filterArrays($value)
{
is_array($value) && $value = null;
return $value;
}

public function beforeValidate($event)
{
$validator = new CFilterValidator();
$validator->attributes = array_keys($this->owner->attributes);
$validator->filter = array($this, 'filterArrays');
$this->owner->validatorList->add($validator);
}
}




Этот behavior добавляет фильтрующее правило валидации для всех атрибутов модели.

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

protected/components/Model.php

и

protected/components/FormModel.php

добавив следующий код:

public function init()
{
$this->attachBehavior('FilterArraysBehavior', 'behaviors.FilterArraysBehavior');
}




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


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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Два удобных приложения для работы с заметками в Chrome

Всем привет!

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


Nimbus Note — http://ift.tt/PZTQ1A


Nimbus Folder — http://ift.tt/PZTQPc


image


Nimbus Note


Начну с нашего профильного приложения. Большой плюс браузерных расширений — это их кроссплатформенность, т.е создавая приложение, можно быть уверенным, что оно будет работать на всех системах, что работает и браузер, включая MAC и Linux. Естественно, Nimbus Note для Google Chrome не является полноценной заменой десктопным программам, но вполне подходит, если вам нужно быстро создать/отредактировать заметку или сохранить информацию с интернета.


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


image


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


image


Nimbus Folder


Nimbus Folder совсем новое наше приложение и его основная функция — это отправка файлов в Nimbus Note. Можно найти разные способы применения приложения, но основных два:


— непосредственно сохранение интересных вам файлов в ваш аккаунт Nimbus-а для дальнейшей работы с ними на других устройствах;


— быстрый способ поделиться файлами;


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


image


Устанавливается Nimbus Box также, как и обычное приложение. После установки его можно найти в списке аппов и на таскбаре. Интерфейс приложения минималистичен — для отправки файлов вам достаточно перекидывать их в окно приложения. Перед отправкой, файлы можно прокомментировать, а также задать время для автоудаления заметки и файлов, если будет такая надобность.


imageimage


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


image


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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Как мотивировать разработчиков: обучение, вовлеченность и борьба с энтропией


Многим менеджерам IT-проектов приходилось сталкиваться с тем, что члены команды разработчиков работают без отдачи, без энтузиазма и спустя рукава. Высокие зарплаты и премии, угрозы и выговоры, как правило, не работают. Специально для блога на Хабре мы расспросили троих ведущих технических руководителей Mail.Ru Group: руководитель подразделения разработки IM Игоря Макарова, руководителя проекта Skyforge Ярославну Глебову и директора по информационным технологиям Александра Горного. Именно им каждый день приходится вдохновлять на новые свершения программистов нашей компании, создающих самые разные продукты: от игр до мессенджеров. Они рассказали о том, какие особенности имеет мотивация для разработчиков, почему на них нельзя кричать и насколько важно признание их идей.





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



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




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




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

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


Игорь Макаров: «Если программист не перфекционист, для которого важно качество его работы, или не горит идеей, а ему все по барабану (таких большинство), то замотивировать его сложнее всего. Одна возможность: обучение у специалиста с большим авторитетом. Если такой сотрудник пройдет интересный учебный курс, то попытается применить новые знания на практике. Если у него не получается, это его беспокоит. А если разработчика что-то беспокоит – это уже полдела. Разработчик начинает переживать за то, что над чем он работает. А неравнодушие, вовлеченность – это уже верх мотивации».




Ярославна Глебова: «Геймдев, как и все IT, это технологии, которые развиваются с бешеной скоростью. Если будешь идти, то ты опоздаешь. Здесь нужно бежать, то есть расти и развиваться. Мы стараемся вкладываться в наших людей по максимуму, наряду с результатами нам важно их развитие. Раз в год все проходят аттестацию, по итогам которой мы ставим новые цели – и профессиональные, и личные, то есть называем качества, которые каждый должен в себе развить. Сотрудники у нас разбиты по грейдам, и чем выше грейд, тем больше личных скиллов должен иметь человек. Личные умения могут быть самые разные – умение слушать, умение даже горячий спор привести к результату. Просто спорить все умеют».




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

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


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


Ярославна (про демократичный стиль руководства): «Наш стиль взаимоотношений мы называем условно-плоской системой. У наших менеджеров нет отдельного стола и возможности забрать себе лучший компьютер, они не используют приказного тона. У нас руководитель – это тот человек, который создает условия для комфортной работы, помогает ребятам лучше договариваться и понимать друг друга. Руководитель – это авторитет, и он должен его зарабатывать, чтобы обеспечивать отличную работу команды. Дело в том, что у нас работают уникальные люди – образованные, с широким кругозором, знающие историю и философию. Таких людей надо уметь слушать и слышать, а как сделать лучше, они придумают сами».





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

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


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




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

Ярославны: «В нашей плоской системе приказы не работают. Ключевые специалисты проекта не будут работать эффективно, если им просто приказать. Руководитель должен увлечь их идеей, и тогда все будут работать гораздо эффективнее. Это особый скилл, который у нас очень ценится – умение увлекаться и увлекать других, ярко и мощно. Таких людей немного даже среди руководителей».


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


Игорь: «Обязательно нужно прислушиваться к разработчику, который не равнодушен к продукту. Если он говорит: «А давайте сделаем вот здесь красную кнопку», и ты понимаешь, что он прав, то лучше сделать ровно так, как он сказал. Сотрудник будет доволен и сделает 20 других кнопок еще качественнее».




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

Ярославна: «У нас считается, что работа должна быть больше, чем работой. Если ты не вложишь в нее душу и эмоции, если ты не будешь думать о ней вечерами, ночами и даже утром, ничего стоящего не получится. Большинство людей у нас болеют играми и хотят делать лучшие игры. Это одна из наших главных ценностей в нашей команде, и мы ищем тех, кто думает так же».


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


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


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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Arduino управляет промышленным грузовым лифтом

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

Немного о том как устроены грузовые подъемники и небольшая история создания системы управления для шестиэтажного грузового лифта на базе платы Arduino совместимого контроллера Seeeduino (ATmega 328).image

Довольно много картинок и букв!



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

В общем, благодатная нива для экспериментов!

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

image


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

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

В наше время такая сложность схемы управления — это смертный грех. Разумеется, первое что предложит сделать разумный инженер — использовать программируемый логический контроллер (ПЛК). Правда стоимость комплектующих в таком случае возрастет. Но почему бы не применить здесь дешевую Arduino? Порыскав в сети, я нашел несколько интересных проектов с макетами пассажирских лифтов из всем знакомого LEGO и решил, что уж с грузовым лифтом эта платка точно должна справиться. Как раз к случаю, поступил заказ на шестиэтажный подъемник.


Кроме того, у меня уже довольно давно лежала без дела плата Seeeduino (ATmega 328), и китайский LCD дисплей. Если уж ваять контроллер — то с дисплеем! И первым делом я подсчитал количество портов на плате, которые можно использовать. А их на Seeeduino — 14 дискретных и 6 аналоговых (которые разумеется работают и как дискретные). Идея была проста: повесить каждую кнопку и концевой этажный выключатель на свой порт, а магнитные пускатели включать с помощью 5VDC/220VAC реле. Цепь безопасности также должна иметь свой порт. Итого, для шестиэтажного грузового лифта необходимо 13 дискретных входов и два выхода для реле. Но куда же подключить дисплей? На помощь приходит сдвиговый регистр PCF8574, который позволит управлять им всего по двум проводам (про это есть пост). Таким образом, используя макетную плату, 13 подтягивающих резисторов и две лампочки, имитирующие реле «пуска вверх» и реле «пуска вниз», я написал управляющую программу (там цикл на цикле и циклом погоняет мне даже стыдно ее показывать). Дисплей к сожалению у меня без поддержки кириллицы, поэтому выводит лишь сообщения типа «Lift on a Floor» и «Security Circuit Broken!».

image


Если с количеством портов все прошло гладко, то первой большой проблемой оказалось совмещение логических уровней. Дело в том, что электромонтажные изделия, применяемые у нас рассчитаны на 220VAC*. То есть при нажатии на кнопку или концевой выключатель на провод приходит самая настоящая фаза, тогда как Arduino использует логику 5VDC. Так как в электронике я отнюдь не профи, я обратился за помощью на радиофорум, и используя подсказки сгородил такую схему, основанную на оптроне:

image

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

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

image

После такого оригинального решения проблемы логических уровней, я перешел к задаче включения магнитных пускателей. Стоит упомянуть, что реле 5VDC/220VAC к портам Arduino подключаются не напрямую, а по схеме с транзистором и диодиком. Впрочем, можно просто купить готовый блок.

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



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

Вся конструкция в сборе:



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



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



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



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



Итоги:

По цене получилось вполне недорого: Реле — 80 р/шт*13шт=1040р, дисплей+Arduino=1000р, блок питания, корпус, клеммники и резисторы = 1000р.

Итого контроллер обошелся где-то в 3000р, тогда как самый бюджетный ПЛК, который справится с такой задачей выйдет не менее 7000р.

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


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

Спасибо за внимание!


PS:

*Да, я прекрасно понимаю, что 220В не самый лучший выбор для цепей управления. Дело в том, что в фирме еще до моего прихода исторически так сложилось (видимо лень было заморачиваться с преобразователями), ну а еще у поставщиков порой трудновато достать кнопки и пускатели на нужное напряжение. Чтобы Вас успокоить, скажу что многие заказчики подключают грузовые лифты через УЗО, чего я всем и советую.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Легкий python веб-фреймворк: Bottle

Введение




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



Установка




Bottle — очень легкий фреймворк и помещается всего в один файл — bottle.py. Установить его можно отсюда, либо сделать pip install bottle.

Возможности




Несмотря на свою минималистичность, Bottle предоставляет довольно широкие возможности, которых на 100% хватает для мелких и средних проектов. Вот список основных возможностей:
Routing



Роутинг в bottle, как и в большинстве фреймворков на питоне, осуществляется с помощью декораторов. Например:

@route('/hello/<name>')
def index(name):
return name




Также динамические url можно составлять на основе регулярных выражений:

@route('/news/<number:re:[0-9]*>')
def show_news(number):
pass




Templates



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

template('template_name', name=name, number=number, foo=bar)




Первый аргумент функции — название файла, в котором содержится текст шаблона (в нашем случае шаблон будет называться template_name.tpl).

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

Hello, {{name}}, glad to see you!




По-умолчанию сделано так, что если в скобках указан html код, то он не выполнится, во избежание XSS атак. Если же нам это очень надо, можно написать {{!name}}. Также Bottle предоставляет нам очень очень крутую возможность: писать любой python код внутри шаблона. Чтобы вызвать питон, достаточно в начале строки поставить %. Например:

%a = 100500
%for i in xrange(a):
<div class="image_{{i}}"><img src="......{{i}}.jpg"></div>
%end




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

%include template_num2 foo=bar, blabla=qweqwe




POST-routing и обработка форм



Какой же нормальный фреймворк может существовать без возможности обработки POST запросов с последующей обработкой форм?

Механизм для обработки POST запросов абсолютно такой же, как и для обработки GET запросов, просто слово route нужно заменить на post:

@post("/url")
def foo():
pass




Для доступа к формам используются атрибуты полей «name». Например:

<input name="age" placeholder="Возраст">




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

request.forms.get("age") # Получить содержимое одного поля age
request.forms.getall("age") # Получить содержимое всех полей age




Также можно обращаться и с файлами:

request.files.get("picture") # Получить один файл из поля picture
request.files.getall("picture") # Получить все файлы из поля (mult-upload)




Cookies



Обращаться с Cookies в bottle очень просто, чтобы установить cookie:

response.set_cookie("name", value, max_age=100500)




Чтобы взять значение:

request.get_cookie("name")




Сервер



В bottle вшит простой http сервер, который пригоден разве что для очень быстрого тестирования одной странички:

run(host='localhost', port=8080)




Естественно, что для более крупных проектов использовать его невозможно, поэтому надо как-то связать bottle с apache или nginx. Честно говоря, сам я всегда использую apache, поэтому рассказать могу только про него, но с ngninx все тоже вроде довольно просто. Bottle связывается с Apache через mod_wsgi. Для того, чтобы это реализовать, нужно сделать следующее:


  1. Создать файл adapter.wsgi с вот таким содержимым
    спойлер


    import sys, os, bottle
    sys.path.append(os.path.dirname(os.path.abspath(__file__)))
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    import index # Основной файл

    application = bottle.default_app()






  2. Установить и включить mod_wsgi

  3. Добавить настройки виртуального хоста
    спойлер


    <VirtualHost *:80>
    DocumentRoot /var/www/foo
    </VirtualHost>

    <Directory /var/www/foo>
    Options FollowSymLinks ExecCGI
    AddHandler wsgi-script .wsgi
    Order allow,deny
    AllowOverride All
    Allow from all
    </Directory>







Частые ошибки и их решения




Заключение




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

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Falcon 9-R взлетел на один километр и приземлился назад. С коровами


сегодня в 00:26


Илон Маск запостил видео взлета и посадки Falcon 9-R на высоту один километр.


В твиттере он сказал, что двигатель Merlin-1D может снижать мощность до сорока процентов.


Собственно и всё.


P.S. Я всё жду когда же они запостят видео нескольких взлетов/посадок подряд, вот тогда это будет очень круто.




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


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Оценка возможности постобработки видео в браузере

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

Эта область довольно неплохо покрыта в десктопной среде — фильтры вроде ffdshow raw video filter и madVR позволяют делать практически всё, что может потребоваться для приятного просмотра. К сожалению, веб не может похвастаться аналогичным тулкитом, и вы либо наслаждаетесь всеми недостатками очередного видео на YouTube, либо открываете его во внешнем приложении вроде MPC-BE, что не очень удобно. А было бы неплохо иметь одну волшебную кнопку, активирующую фильтрацию в месте, где она и должна быть — в вашем браузере.


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



Замечания




Во время чтения статьи следует учесть:


  1. Все приведенные демонстрации основаны на html5 video с установленным атрибутом loop. Это видео может ужасно дергаться и лагать во время переключения на начало видеоряда в некоторых браузерах, по вине этих браузеров. Я не стал переусложнять код ради возможного исправления этой проблемы.

  2. Если повторяющееся видео вас раздражает, можно добавить loop=false к GET-параметрам запроса.

  3. Демки тестировались только в хроме, лисе и IE11, в остальных браузерах может не работать.

  4. Исходный код всех демок приведен прямо внутри соответствующих html-страниц, без зависимостей.

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

  6. Мы закрываем глаза на возможные проблемы с CORS, сайтами, использующими Flash-видео и т.п. Только сферические тесты в вакууме.

  7. В JavaScript я проездом, поэтому не стоит слишком доверять приведенному ниже тексту. Для большей уверенности можете поделить приведенное время на 2. Надеюсь увидеть исправления и советы в комментариях.




Принципы реализации




Единственным вариантом, который позволил бы иметь одно ядро для всех целевых браузеров (Chrome и Firefox в первую очередь) является браузерное расширение. Альтернатива в виде Google Chrome Native Client, внезапно, работает только в Chrome, и Mozilla на данный момент не собирается поддерживать NaCl в Firefox. Кроме того, я не изучал возможности доступа NaCl к элементам на странице — вполне может оказаться, что для наших целей он не сработает.

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


Реальной проблемой у расширения является язык реализации — интерпретируемый JavaScript, а как мы знаем, интерпретируемые языки плохо подходят для серьезных расчетов. Но ведь это не беда! JavaScript последнее время получает море любви и оптимизаций, и существует довольно большое количество программистов, считающих, что JS — язык, подходящий для написания любых приложений и что вообще всё должно двигаться в веб. Более того, доступно множество новых технологий вроде asm.js, SIMD.js, WebGL и WebCL, которые, в теории, позволяют реализовывать всё, что душе угодно, со скоростью лишь немного меньше нативной. Так что мы не должны иметь никаких серьезных проблем с написанием набора фильтров в браузере, правда?


Не совсем.


Чистый JavaScript




Фильтрация в чистом JS работает по следующей схеме:


  1. Получаем оба необходимых элемента — спрятанный video и canvas, расположенный поверх него.

  2. Рисуем кадр из видео на канвасе через context.drawImage(video, 0, 0), где context — 2d контекст, полученный с канваса.

  3. Получаем буфер кадра (массив байтов цветов) через context.getImageData(0, 0, width, height).

  4. Обрабатываем буфер требуемыми фильтрами.

  5. Кладем обработанный массив обратно через context.putImageData(imageData, 0, 0).




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

outputContext.drawImage(video, 0, 0);
var imageData = outputContext.getImageData(0, 0, width, height);
var source = imageData.data;
var length = source.length;
for (var i = 0; i < length; i += 4) {
source[i ] = 255 - source[i];
source[i+1] = 255 - source[i+1];
source[i+2] = 255 - source[i+2];
// игнорируем альфу
}
outputContext.putImageData(imageData, 0, 0);




И хотя этот метод работает для демок и простых картинок, он очень быстро “сдувается” на высоких разрешениях. Хотя вызов drawImage сам по себе довольно быстр даже на 1080p, после добавления getImageData и putImageData время выполнения растет до 20-30 миллисекунд на одну итерацию. Полный код, приведенный выше, выполняется уже за 35-40мс, что является предельной скоростью для PAL-видео (25 кадров в секунду, 40мс на один кадр). Все замеры получены на 4770k, который является одним из наиболее мощных домашних процессоров на данный момент. Это означает, что выполнение любого более-менее сложного фильтра на предыдущих поколениях процессоров невозможно вне зависимости от производительности JavaScript. Любой, даже очень быстрый код, будет упираться в ужасную производительность самого канваса.

Но JavaScript не очень быстр сам по себе. Хотя обычные операции вроде инвертирования или прогона через LUT могут выполняться за разумное время, любой более-менее сложный фильтр вызывает ужасные лаги. Простая реализация фильтра добавления шума (Math.random()*10 к каждому пикселю) требует уже 55 миллисекунд, а 3х3 ядро для блюра, реализованное в приведенном ниже коде, проходит за 400мс, или 2.5 кадров в секунду.



function blur(source, width, height) {
function blur_core(ptr, offset, stride) {
return (ptr[offset - stride - 4] +
ptr[offset - stride] +
ptr[offset - stride + 4] +
ptr[offset - 4] +
ptr[offset] +
ptr[offset + 4] +
ptr[offset + stride - 4] +
ptr[offset + stride] +
ptr[offset + stride + 4]
) / 9;
}

var stride = width * 4;
for (var y = 1; y < (height - 1); ++y) {
var offset = y * stride;
for (var x = 1; x < stride - 4; x += 4) {
source[offset] = blur_core(source, offset, stride);
source[offset + 1] = blur_core(source, offset + 1, stride);
source[offset + 2] = blur_core(source, offset + 2, stride);
offset += 4;
}
}
}




Firefox показывает еще более удручающие результаты с 800 мс/проход. Что интересно, IE11 опережает даже Chrome, причем в два раза (но сам canvas у него медленный, так что это не спасает). В любом случае, становится ясно, что чистый JavaScript — неправильное средство для реализации фильтров.

asm.js




Новомодный asm.js — средство от компании Mozilla для оптимизации выполнения JavaScript кода. Генерируемый код по-прежнему будет работать в хроме, однако надеяться не серьезный прирост производительности не стоит, поскольку поддержка asm.js, по всей видимости, еще не добавлена.

К сожалению, я не смог найти простой путь компиляции выбранных функций в asm.js-оптимизированный код. Emscripten генерирует около 4.5 тысяч строк кода при компиляции простой двустрочной функции, и я не понял, как можно вытащить из него только нужный код за разумное время. Писать же asm.js руками — то ещё удовольствие. В любом случае, asm.js упрётся в производительность 2d-контекста канваса, аналогично чистому JavaScript.


SIMD.js




SIMD.js — очень новая технология ручной оптимизации JS-приложений, которая в настоящий момент “поддерживается” только в Firefox Nightly, но очень скоро может получить поддержку всех целевых браузеров. К сожалению, API сейчас работает только с двумя типами данных, float32x4 и uint32x4, что делает всю затею бесполезной для большинства реальных 8-битных фильтров. Более того, тип Int32x4Array пока не реализован даже в Nightly, поэтому любая запись и чтение данных из памяти будут происходить медленно и страшно (когда реализованы подобным образом). Однако, приведу код реализации обычного фильтра инвертирования (на этот раз работающего через XOR):

function invert_frame_simd(source) {
var fff = SIMD.int32x4.splat(0x00FFFFFF);
var length = source.length / 4;
var int32 = new Uint32Array(source.buffer);
for (var i = 0; i < length; i += 4) {
var src = SIMD.int32x4(int32[i], int32[i+1], int32[i+2], int32[i+3]);
var dst = SIMD.int32x4.xor(src, fff);
int32[i+0] = dst.x;
int32[i+1] = dst.y;
int32[i+2] = dst.z;
int32[i+3] = dst.w;
}
}




На данный момент приведенный код выполняется значительно медленней чистого JS — 1600мс/проход (пользователи Nighly могут попробовать очередное демо). Похоже, придется подождать еще достаточное количество времени, прежде чем можно будет делать хоть что-то полезное с этой технологией. К сожалению, не ясно, как будет реализована поддержка 256-битных YMM регистров (int32x4 — обычный 128-битный xmm из SSE2), и будут ли доступны инструкции из более новых технологий вроде SSSE3. Ну и SIMD.js не спасает от медленного канваса. Зато фанаты SIMD могут уже сейчас получить некоторые привычные баги, прямо в браузере!

WebGL




Совершенно другой способ реализации фильтров — WebGL. В самом базовом понимании WebGL — JS-интерфейс для нативной технологии OpenGL, которая позволяет выполнять разнообразный код на GPU. Обычно она используется для программирования графики в играх и т.п., однако никто не мешает обрабатывать картинки или даже видео с её помощью. WebGL также не требует вызовов getImageData, что в теории позволяет избежать типичного 20мс-лага.

Но ничто не бывает бесплатно — WebGL не является средством общего назначения и использовать это API для абстрактного неграфического кода — ужасная боль. Потребуется определять бесполезные вертексы (которые всегда будут покрывать весь кадр), правильно позиционировать текстуру (которая будет закрывать весь кадр), а затем использовать видео в качестве текстуры. К счастью, WebGL достаточно умён, чтобы запрашивать нужные кадры из видео автоматом. По крайней мере, в хроме и лисе. IE11 же обрадует ошибкой WEBGL11072: INVALID_VALUE: texImage2D: This texture source is not supported.


Наконец, для написания фильтров придётся использовать шейдеры, реализуемые на немного ущербном языке GLSL, который (по крайней мере в WebGL-варианте) даже не поддерживает установку константных массивов, поэтому любые массивы надо будет либо передавать с помощью uniforms (такие типа-глобальные переменные), либо использовать индийский способ:



float core1[9];
core1[0] = 1.0;
core1[1] = 1.0;
core1[2] = 0.0;
core1[3] = 1.0;
core1[4] = 0.0;
core1[5] = -1.0;
core1[6] = 0.0;
core1[7] = -1.0;
core1[8] = -1.0;




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

В общем, технологии вроде CUDA и OpenCL были придуманы не от хорошей жизни.


В оправдание WebGL, он имеет действительно потрясающую для веба производительность (которую вы не можете измерить). По крайней мере, он может обработать фильтр prewitt из masktools (выбор максимального значения из четырех 3х3 ядер) в реальном времени на 1080p и выше. Если вы ненавидите себя и не боитесь получить немного неподдерживаемый код, WebGL позволяет делать с видео довольно интересные вещи. Более разумным может быть использование библиотеки seriously.js, которая прячет часть шаблонного WebGL-кода, однако может оказаться недостаточно продвинутой для обработки изменения разрешения видео или реализации временных фильтров.


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


WebCL




Но не получится. Википедия говорит, что WebCL 1.0 был финализирован 19-ого марта этого года, что делает технологию самой молодой из всего списка, моложе даже SIMD.js. И, в отличие от SIMD.js, она не будет поддерживаться в Firefox в ближайшем времени. Где-то читал об аналогичном решении для Chrome, но потерял ссылку. Так что WebCL на данный момент является мёртвой технологией без ясного будущего.

Заключение




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

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.