Недорогая бизнес-аналитика с Apache Hadoop и Dojo: Часть 1. Обработка данных с использованием Apache Hadoop

Источник: IBM

 

Apache Hadoop и бизнес-аналитика

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

Начните с понимания ваших пользователей

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

Генерирование необходимых данных

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

Для Web-приложений с высокой интерактивностью, возможно, придется даже написать JavaScript для выполнения Ajax-вызовов или динамического размещения маячков (обычно это изображения размером 1x1 пиксел с некоторым специфичным событием, добавленным в строку запроса его URL) для получения информации о том, как пользователь взаимодействует с Web-приложением. В примере этой статьи мы соберем информацию о браузерах, используемых для доступа к вашими Web-приложениям. Возможно, вы уже собираете такую информацию. Если нет, обычно это нетрудно реализовать. Например, при использовании Web-сервера Apache необходимо просто добавить пару строк в файл httpd.conf. В листинге 1 приведен пример.

Листинг 1. Регистрация информации о браузере пользователей с использованием Web-сервера Apache

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" common
CustomLog /dev/logs/apache_access_log common

Конфигурация, приведенная в листинге 1, довольно обычна для Apache. При такой конфигурации регистрируется много полезной информации, в том числе: IP-адрес пользователя, запрашиваемый ресурс (страница, изображение и т.д.), реферер (referrer) (страница, с которой пришел пользователь) и, естественно, Web-браузер, которым пользуется пользователь. Можно настроить LogFormat для регистрации большего или меньшего объема данных.

Примечание. Генерирование необходимых данных может показаться простым делом, но есть и подводные камни. Например, сбор большого количества данных о поведении пользователей приведет к увеличению числа операторов журналирования. Это может вызвать нежелательные побочные эффекты. Может возрасти время обработки пользовательских запросов. Для Web-приложений это означает замедление работы страниц и увеличение времени реакции на действия пользователей, что всегда плохо. Это может также означать, что для обработки того же числа запросов необходимо будет установить больше серверов приложений, т.е. увеличить затраты. Возможно, стоит обдумать использование неблокирующей системы журналирования, чтобы ваши пользователи никогда не ждали окончания журналирования. Может быть, вы захотите детализировать только случайную выборку запросов и при последующем анализе экстраполировать информацию. Растущий объем журналирования может также потребовать значительного увеличения дискового пространства. Возможно, придется приобрести дополнительное оборудование. Это очевидные затраты, но дополнительная детализация информации о вашей деятельности приносит существенную дополнительную ценность.

Обработка данных

Получение всех хранящихся где-то полезных данных - это относительно простой шаг в этом процессе. Теперь их необходимо обработать и превратить в нечто более полезное. Чтобы получить представление о том, насколько это нетривиальная задача, просто выполните Web-поиск по словосочетанию "бизнес-аналитика" или "business intelligence". Вы получите не только множество результатов, но и множество платных предложений. Имеется большое количество очень дорогого программного обеспечения для анализа (и составления отчетов).

Но даже это дорогостоящее программное обеспечение обычно требует интеграции, что бывает довольно сложно сделать (конечно же, его поставщики обычно имеют подразделение профессиональной поддержки, которое может прийти на помощь; просто не прячьте вашу чековую книжку). Здесь приходит на помощь Hadoop. Он прекрасно подходит для обработки больших объемов данных. Просто соберите терабайты журнальных файлов, возьмите кластер готовых серверов и получите готовую систему. Этот шаг и является предметом рассмотрения данной статьи. В нашем примере мы напишем задание map/reduce, преобразующее журнальные файлы Web-сервера Apache в небольшой набор данных с полезной информацией, которую можно легко использовать в Web-приложении для создания интерактивных отчетов. Останется лишь сделать последний шаг.

Создание отчетов

После обработки всех данных у вас может остаться очень ценная сжатая информация, находящаяся либо в базе данных, либо где-то в XML-формате. Это хорошо, но эта информация, возможно, нужна бизнес-аналитикам и руководству компании. Им нужен отчет определенного рода, который будет и интерактивным, и визуально привлекательным. Если отчет позволяет конечным пользователям представлять данные различными способами, он мало того что принесет больше пользы, но и вам не придется возвращаться назад и создавать еще один отчет, основанный на тех же данных. Конечно же, всем нравится что-то радующее глаз. Во второй части я расскажу, как можно использовать для этого Dojo Toolkit. А пока мы сосредоточимся на обработке данных при помощи Hadoop. Начнем с обработки журналов Apache.


Анализ журналов доступа

Пакет Hadoop основан на парадигме map/reduce (отобразить/сократить). Идея в том, чтобы собрать некоторый объемный набор данных, преобразовать его в данные, которые представляют интерес (шаг отображения), и агрегировать результат (шаг сокращения). В данном примере мы возьмем журналы доступа Apache и преобразуем их в набор данных, содержащий только количество запросов, полученных от различных браузеров. Т.е. входными данными для процесса является журнал (или несколько журналов). В листинге 2 приведен пример содержимого журнала.

Листинг 2. Пример входных данных

127.0.0.1 - - [11/Jul/2010:15:25:29 -0700] "GET /MAMP/images/pro_timeEdition.jpg 
HTTP/1.1" 304 - "http://localhost:8888/MAMP/?language=English" "Mozilla/5.0 (Windows; U;
Windows NT 6.1; it; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 ( .NET CLR 3.5.30729)"
127.0.0.1 - - [11/Jul/2010:15:25:29 -0700] "GET /MAMP/images/welogo.gif HTTP/1.1" 304 -
 "http://localhost:8888/MAMP/?language=English" "Mozilla/5.0 (Macintosh; U; Intel Mac 
OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.458.1 Safari/534.3"
127.0.0.1 - - [11/Jul/2010:15:25:29 -0700] "GET /MAMP/images/madeonamac.gif HTTP/1.1" 
304 - "http://localhost:8888/MAMP/?language=English" "Mozilla/4.0 (compatible; MSIE 
8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; Media Center PC 6.0; InfoPath.2; 
MS-RTC LM 8)"
127.0.0.1 - - [11/Jul/2010:15:25:29 -0700] "GET /MAMP/images/bullet.gif HTTP/1.1" 304 -
 "http://localhost:8888/MAMP/?language=English" "Mozilla/5.0 (Macintosh; U; Intel 
 Mac OS X 10_6_3; en-us) AppleWebKit/534.1+ (KHTML, like Gecko) Version/5.0 
 Safari/533.16"
127.0.0.1 - - [11/Jul/2010:15:25:29 -0700] "GET /MAMP/images/valid-xhtml10.png HTTP/1.1"
304 - "http://localhost:8888/MAMP/?language=English" "Mozilla/5.0 (Macintosh; U; PPC 
Mac OS X 10.5; en-US; rv:1.9.2.4) Gecko/20100611 Firefox/3.6.4 GTB7.0"

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

Листинг 3. Промежуточные результаты, созданные на шаге отображения

FIREFOX 1
CHROME 1
IE 1
SAFARI 1
FIREFOX 1

Как можно увидеть в листинге 3, мы значительно упростили проблему, интересуясь только четырьмя видами браузеров: Microsoft Internet Explorer®, Mozilla Firefox®, Apple Safari® и Google Chrome®. Нас даже не интересуют версии браузеров (не делайте этого дома, особенно с Internet Explorer). Шаг сокращения данных работает с этими файлами, агрегируя результаты в конечный файл. В листинге 4 приведен пример окончательных данных.

Листинг 4. Окончательные выходные данные, созданные на шаге сокращения

{"IE":"8678891",
"FIREFOX":"4677201",
"CHROME":"2011558",
"SAFARI":"733549"}

Данные, показанные в листинге 4, на шаге сокращения не только были агрегированы, но и преобразованы в формат JSON. Это облегчает использование их в Web-приложении для составления отчетности. Естественно, можно получать менее обработанные данные, обращаться к ним посредством приложения на стороне сервера и предоставлять их Web-приложению. Ни Hadoop, ни Dojo не налагают каких-либо ограничений или требований на этом шаге.

Надеюсь, что все выглядит просто и понятно. Однако Hadoop - это не только простая инфраструктура, инкапсулирующая парадигму map/reduce. Он реализует эту парадигму распределенным способом. Журналы могут быть огромными, но работа будет поделена между разными машинами (узлами) кластера Hadoop. Эти кластеры обычно масштабируются горизонтально, т.е. если вы захотите обрабатывать больший объем данных или ускорить обработку, просто добавьте еще несколько машин. Настройка кластера и оптимизация конфигурации (например, разбивка данных для отправки на различные машины) - это большие темы для обсуждения сами по себе. В данной статье мы не будем их подробно рассматривать, а сосредоточимся на отображении, сокращении и форматировании данных. Начнем с шага отображения.

Шаг 1: фаза отображения

Среда времени исполнения Hadoop будет разделять обрабатываемые данные (журналы) и отправлять каждому узлу кластера фрагмент данных. К этим данным необходимо применить функцию map. В листинге 5 показано, как указать функцию map для нашего примера анализатора журнала:

Листинг 5. Функция Mapper для примера журнала доступа

public class LogMapper extends Mapper<Object, Text, Text, IntWritable> {
    private final static IntWritable one = new IntWritable(1);
      private static final Pattern regex = 
          Pattern.compile("(.*?)\"(.*?)\"(.*?)\"(.*?)\"(.*?)\"(.*?)\");    
    @Override
    protected void map(Object key, Text value, Context context)
            throws IOException, InterruptedException {
        Matcher m = regex.matcher(value.toString());
        if (m.matches()){
            String ua = m.group(6).toLowerCase();
            Agents agent = IE; // default
            if (ua.contains("chrome")){
                agent = CHROME;
            } else if (ua.contains("safari")){
                agent = SAFARI;
            } else if (ua.contains("firefox")){
                agent = FIREFOX;
            }
            Text agentStr = new Text(agent.name());
            context.write(agentStr, one);
        }
    }
}

Hadoop интенсивно использует Java™ Generics для обеспечения типизированного способа написания функций map и reduce. Примером является листинг 5. Обратите внимание, что мы расширяем класс org.apache.hadoop.mapreduce.Mapper инфраструктуры Hadoop. Этот класс принимает пару (ключ,значение) в качестве входных данных и отображает их в новую пару (ключ,значение). Этот класс параметризуется на основе типов входной и выходной пар (ключ,значение). В данном случае он принимает пару (ключ,значение) типа (Object,text) и отображает ее в пару (ключ,значение) типа (text,IntWritable). Имеется единственный метод реализации, называемый map (который параметризуется в зависимости от типов входной пары (ключ,значение)).

В примере, приведенном в листинге 5, одна строка журнала (как показано в листинге 2) будет входным объектом Text, передаваемым в метод map. Для этого объекта выполняется регулярное выражение для извлечения строки с информацией о браузере пользователя (user agent). Затем строка user agent сопоставляется со значениями Enum под названием Agents. Наконец, эти данные записываются в объект Context (чей метод write параметризуется на основе типов пары (ключ,значение), генерируемой функцией map). Выражение write сгенерирует строку, аналогичную приведенной в листинге 3. Теперь все готово к фазе сокращения.

Шаг 2: фаза сокращения

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

Листинг 6. Функция Reducer для примера журнала доступа

public class AgentSumReducer extends 
    Reducer<Text,IntWritable,Text,IntWritable> {
    private IntWritable result = new IntWritable();
    @Override
    public void reduce(Text key, Iterable<IntWritable> values, 
          Context context) throws IOException, InterruptedException {
        int sum = 0;
        for (IntWritable val : values) {
            sum += val.get();
        }
        result.set(sum);
        context.write(key, result);
    }
}

На этот раз мы расширяем класс org.apache.hadoop.mapreduce.Reducer и реализуем его метод reduce. Как показано в листинге 6, для типизации всего исходного кода опять используются возможности Generics. Однако нужно гарантировать, что типы, генерируемые Mapper, аналогичны типам, потребляемым Reducer. В метод reduce передается Iterable, тип которого аналогичен типу пар (ключ,значение), являющихся входными для него. Все, что здесь нужно сделать, - добавить эти значения. Затем они записываются в объект Context точно так же, как и в функции Mapper. Теперь все готово для форматирования выходных данных.

Шаг 3: форматирование выходных данных

Этот шаг на самом деле необязателен. Можно было бы использовать необработанные данные, выдаваемые Hadoop (имя и значение в каждой строке, разделенные пробелом). Однако нам нужно сделать эти данные доступными Web-приложению для составления отчетов, поэтому преобразуем их в формат JSON, как показано в листинге 4. В листинге 7 показано, как это сделать.

Листинг 7. Формат вывода для примера журнала доступа

public class JsonOutputFormat extends TextOutputFormat<Text, IntWritable> {
    @Override
    public RecordWriter<Text, IntWritable> getRecordWriter(
            TaskAttemptContext context) throws IOException, 
                  InterruptedException {
        Configuration conf = context.getConfiguration();
        Path path = getOutputPath(context);
        FileSystem fs = path.getFileSystem(conf);
        FSDataOutputStream out = 
                fs.create(new Path(path,context.getJobName()));
        return new JsonRecordWriter(out);
    }

    private static class JsonRecordWriter extends 
          LineRecordWriter<Text,IntWritable>{
        boolean firstRecord = true;
        @Override
        public synchronized void close(TaskAttemptContext context)
                throws IOException {
            out.writeChar('{');
            super.close(null);
        }

        @Override
        public synchronized void write(Text key, IntWritable value)
                throws IOException {
            if (!firstRecord){
                out.writeChars(",\r\n");
                firstRecord = false;
            }
            out.writeChars("\" + key.toString() + "\":\"+
                    value.toString()+"\");
        }

        public JsonRecordWriter(DataOutputStream out) 
                throws IOException{
            super(out);
            out.writeChar('}');
        }
    }
}

Код в листинге 7 может показаться сложноватым, но на самом деле он прост. Мы расширяем класс org.apache.hadoop.mapreduce.lib.output.TextOutputFormat - вспомогательный класс для вывода данных в виде текста (обратите внимание, что опять используются Generics, и убедитесь в соответствии типов пар (ключ,значение), генерируемых классом Reducer). Единственное, что нужно сделать, - реализовать метод getRecordWriter, который возвратит экземпляр org.apache.hadoop.mapreduce.RecordWriter (параметризованный). Мы возвращаем экземпляр JsonRecordWriter - внутренний класс, получающий строку данных из листинга 3 и генерирующий строку данных из листинга 4. При этом будут сгенерированы JSON-данные, готовые для потребления основанным на Dojo исходным кодом в приложении составления отчетов.

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


Заключение

В статье рассмотрен простой пример обработки большого объема данных с использованием Hadoop. Это простой способ обработки больших объемов данных, который может предоставить ценную информацию о вашем бизнесе. В статье мы напрямую использовали возможности Hadoop map/reduce. Если вы начнете широко использовать Hadoop, вам определенно следует взглянуть на инфраструктуры более высокого порядка, основанные на Hadoop, которые облегчают написание заданий map/reduce. Две такие отличные инфраструктуры с открытым исходным кодом, Pig и Hive, тоже являются проектами Apache. Обе используют более описательный синтаксис с меньшим объемом программирования. Pig - это язык потоков данных, разработанный членами основной проектной команды Hadoop в Yahoo®, тогда как Hive - это язык, больше напоминающий SQL, разработанный в Facebook. Для генерирования выходных данных, потребляемых Web-приложением, можно использовать любой из них наравне с базовыми заданиями map/reduce.



Загрузка

Описание

Имя

Размер

Метод загрузки

Исходный код статьи AccessLogAnalyzer.zip 6 КБ HTTP

 

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

В данной статье для обработки больших объемов данных будет использоваться система Apache Hadoop, а именно Apache Hadoop 0.20. Для работы с Hadoop необходимо установить пакет программ Java development kit (в статье используется JDK 1.6.0_20). Hadoop предъявляет свои собственные требования, например, наличие установленных SSH и RSYNC.

Страница сайта http://www.interface.ru
Оригинал находится по адресу http://www.interface.ru/home.asp?artId=34855