Я работаю в команде, которая отвечает за настольное приложение для Windows, которое позволяет отображать и обрабатывать медицинские изображения. Медицинские изображения — это то, что вы себе представляете: рентген, ультразвук, МРТ, компьютерная томография и т. Д.
Эти изображения большие — сотни мегабайт (больше для ультразвукового видео) — и в этом заключается одна из наших больших проблем: высокая производительность для пользователей во всем мире. И многие из наших пользователей сидят в Южной Индии или Европе, в то время как наши изображения находятся в центре обработки данных на восточном побережье Америки.
Теперь, когда у нас есть роскошные большие трубы, соединяющие эти места, они неизбежно страдают от довольно высокой задержки (~ 300 мс для Индии) из-за физического расстояния между ними. Этот уровень задержки и TCP
не очень хорошо сочетаются .
Это приводит к очень разочаровывающей скорости пропускной способности даже при наличии достаточной пропускной способности. Улучшение этой ситуации можно свести к применению всего лишь трех методов:
- Переместить данные быстрее
- Переместить меньше данных
- Переместите данные, прежде чем они вам понадобятся
Для нас возможность перенести данные до того, как они были необходимы, уже не обсуждалась. Предварительное кэширование данных изображений — это подход, который мы использовали в течение многих лет с устаревшим продуктом. Хотя он работал довольно хорошо, он был далеко не прозрачен для конечных пользователей. С некоторой регулярностью данные, которые люди ожидали получить от удаленных пользователей, не делали это своевременно, и за этим последовало большое скрежетание зубами и подача билетов в службу поддержки. Кэширование было, так сказать, четырехбуквенным словом.
Таким образом, мы сосредоточились на более быстром перемещении данных и уменьшении их загрузки. Именно то, как мы это сделали, на самом деле не является темой этого поста (но чтобы не оставить вас в покое, масштабирование изображений и UDP были ключевыми).
На самом деле, эта статья о том, как мы использовали
SimpleDB на основе облака Amazon в качестве сверхпростого средства для сбора метрик в полевых условиях производительности и использования нашего приложения.
На нашей самой ранней пилотной фазе мы отображали время загрузки и пропускную способность в строке состояния приложения, и пользователи, очевидно, могли передавать эту информацию нам (да, это было неудовлетворительно, как кажется). И, конечно же, было в равной степени возможно регистрировать вещи на компьютере пользователя, например, с помощью Log4Net или с помощью журнала событий Windows. Но такой подход делает доступ к захваченным данным и их консолидацию немного более сложным, чем мы хотели. Поэтому, хотя это не значит, что мы не могли бы получить эти данные без облачного решения, ясно, что какой-то один централизованный захват этих данных был привлекательным вариантом.
Один из способов, которым мы могли бы получить централизованные средства для сбора этих данных, — это создать небольшую базу данных, создать для нее набор веб-сервисов и использовать ее. Но усилия, связанные с выполнением и развертыванием, как и во многих организациях, нетривиальны, и мы стремились внедрить эту функциональность как можно скорее.
Для сравнения, Amazon SimpleDB — это просто.
Это «без схемы», поэтому нет никаких DDL для создания таблиц и так далее. Вместо этого данные хранятся в доменах (схожих с таблицами; их нужно создавать заранее), состоящих из элементов (как строки) в виде набора атрибутов (немного похожего на столбец, но лучше воспринимаемого как имя пары-значения).
Но достаточно английского, вот немного C #, чтобы показать, насколько легко можно сохранить некоторые данные в SimpleDB. На самом деле это может быть даже проще, так как здесь я использую асинхронный подход для ввода данных.
private void LogMetricToCloud(IMetric metric) { string domain = metric.GetType().FullName; CreateDomainIfNeeded(domain); try { PutAttributesRequest request = new PutAttributesRequest() .WithDomainName(domain) .WithItemName(Guid.NewGuid().ToString()); foreach(var attribute in metric.Attributes()) { request.WithAttribute(new ReplaceableAttribute() .WithName(attribute.Name) .WithValue(attribute.Value)); } string state = metric.GetType().FullName + ":" + metric.CSVData(); IAsyncResult asyncResult = _db.BeginPutAttributes(request, new AsyncCallback(SimpleDBCallBack), state); } catch (Exception e) { TryLogError(e.Message); } } private void SimpleDBCallBack(IAsyncResult result) { string state = result.AsyncState as string; try { // If there was an error during the put attributes operation it will be thrown as part of the EndPutAttributes method. _db.EndPutAttributes(result); } catch (Exception e) { TryLogError(string.Format("Exception: {0}\nFailed to log: {1}", e.Message, state)); } }
Я думаю, что то, что делает код и как, самоочевидно, но это может быть просто потому, что я работал с ним. Просто чтобы убедиться, что это ясно, вот разбивка:
- У меня есть несколько различных классов метрики, все в соответствии с интерфейсом IMetric
- У меня есть домен (думаю: таблица) для каждого вида метрики
- В качестве конкретного примера, одна из моих метрик измеряет использование функции; особенности такие вещи, как переворачивание и маскировка изображений. Для использования функций я записываю, кто, где, когда, какой проект и какие функции они использовали.
- Метод LogMetricToCloud просто перебирает все атрибуты метрики и создает запрос для сохранения этого в моем экземпляре SimpleDB.
- Запрос выполняется асинхронно, с творчески названным методом SimpleDBCallBack, который вызывается после завершения. Вызов здесь EndPutAttributes позволит нам узнать, возникли ли какие-либо проблемы с исходным запросом.
Кажется, что все это работает довольно хорошо, и я надеюсь, что в ближайшем будущем мы сможем еще больше использовать SimpleDB. Следующим в списке для меня является реализация функциональности типа «функция переключения» для нашего приложения. Этим мы можем развить некоторые идеи, описанные в этой
замечательной статье Джеза Хамбла о базовых методах непрерывной доставки.