Nutch 2.0 之 Apache Gora介绍
-----------------
1. 什么是Apache Gora
Apache Gora是一个开源的ORM框架,主要为大数据提供内存数据模型与数据的持久化。目前Gora支持对于列数据、key-value数据,文档数据与RDBMS数据的存储,还支持使用Apache Hadoop来对对大数据进行分析。
2. 为什么要使用Apache Gora
虽然目前市面上有很多不错的关系数据库的ORM框架,但是基于数据模型的框架如JDO还是有一些不足,如对于列数据模型的存储与持久化。Gora正好弥补了这个问题,它能使用户很容易对大数据时行 内存建模与持久化,而且支持Hadoop来对大数据进行分析。
说白了,Gora就是一个大数据的表示与持久化框架,它有如下特点
- 数据持久化:它可以对列数据进行持久化,如HBase,Cassandra,Hypertable; key-value数据进行持久化,如Voldermort,Redis等,SQL数据库进行持久化,如MySQL,HSQLDB,也可以对文件进行HDFS存储 。
- 数据访问:可以使用Java API对数据进行轻松的访问
- 索引:可以持久化对象到Lucene或者Solr中去,可以使用Gora API来进行查询
- 分析:可以使用Apache Pig,Hive,cascading来对数据进行分析
- MR的支持:原生支持Hadoop的MR框架,这个已经被用在Nutch 2.0上了
3. Gora的一个源代码结构
Gora源代码以模块的形式来组织,其中gora-core是主要核心模块。所有其它模块都依赖这个核心模块,当然你可以扩展自己的模块,当前实现的模块如下
- gora-core : 核心模块
- gora-cassandra : apache cassandra模块
- gora-hbase : apache hbase模块
- gora-sql : sql数据库模块
4. 一个简单的例子
这个例子主要完成两上功能,一个是把目录中的测试数据放到HBase中,另一个是对HBase中的数据进行MR分析。
下面先来看第一个功能:
目前gora 0.2只支持hbase 0.90,hbase可以在hbase.apache.org上进行下载,下载先完成以后启动一个简单的服务,它把namenode+regionserver还有zookeeper都启动在一个jvm进程中,命令如下
- $ bin/start-hbase.sh
启动完成以后可以使用如下命令来测试
- bin/hbase shell
下来源代码中的测试数据进行解析,命令如下
- $ tar zxvf src/main/resources/access.log.tar.gz -C src/main/resources/
数据格式如下:
- 88.254.190.73 - - [10/Mar/2009:20:40:26 +0200] "GET / HTTP/1.1" 200 43 "http://www.buldinle.com/" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; GTB5; .NET CLR 2.0.50727; InfoPath.2)"
- 78.179.56.27 - - [11/Mar/2009:00:07:40 +0200] "GET /index.php?i=3&a=1__6x39kovbji8&k=3750105 HTTP/1.1" 200 43 "http://www.buldinle.com/index.php?i=3&a=1__6X39Kovbji8&k=3750105" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; OfficeLiveConnector.1.3; OfficeLivePatch.0.0)"
- 78.163.99.14 - - [12/Mar/2009:18:18:25 +0200] "GET /index.php?a=3__x7l72c&k=4476881 HTTP/1.1" 200 43 "http://www.buldinle.com/index.php?a=3__x7l72c&k=4476881" "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; InfoPath.1)"
下来是定义数据模型,Gora使用Apache Avro来定义数据的对象模型,avro可以方便的提供跟踪数对象的持久化状态与对象的持久化功能。定义数据模型还是比较简单的,它是以json格式来定义的,其中使用的PageView(src/main/avro/pageview.json)的定义格式如下:
- {
- "type": "record",
- "name": "Pageview",
- "namespace": "org.apache.gora.tutorial.log.generated",
- "fields" : [
- {"name": "url", "type": "string"},
- {"name": "timestamp", "type": "long"},
- {"name": "ip", "type": "string"},
- {"name": "httpMethod", "type": "string"},
- {"name": "httpStatusCode", "type": "int"},
- {"name": "responseSize", "type": "int"},
- {"name": "referrer", "type": "string"},
- {"name": "userAgent", "type": "string"}
- ]
- }
我们可以看到,其中的数据类型是record,name是生成的类的名称,而namespace是对应java中package的名字,而fields中定义的字段的名称与类型。
下来是对定义好的数据模型进行编译,自动生成相应的代码,命令如下
- $ bin/gora compile
- $ Usage: SpecificCompiler <schema file> <output dir>
- $ bin/gora compile gora-tutorial/src/main/avro/pageview.json gora-tutorial/src/main/java/
编译以后生成文件 gora-tutorial/src/main/java/org/apache/gora/tutorial/log/generated/Pageview.java
这里Gora的解析器扩展了Avro的SpecificCompiler方法,因为生成的对象模型在扩展自Gora自己的Persistent接口,而Persistent这个接口定义了对象的持久化,对象状态的跟踪等方法。下面是部分Pageview.java的代码
- public class Pageview extends PersistentBase {
- private Utf8 url;
- private long timestamp;
- private Utf8 ip;
- private Utf8 httpMethod;
- private int httpStatusCode;
- private int responseSize;
- private Utf8 referrer;
- private Utf8 userAgent;
- ...
- public static final Schema _SCHEMA = Schema.parse("{\"type\":\"record\", ... ");
- public static enum Field {
- URL(0,"url"),
- TIMESTAMP(1,"timestamp"),
- IP(2,"ip"),
- HTTP_METHOD(3,"httpMethod"),
- HTTP_STATUS_CODE(4,"httpStatusCode"),
- RESPONSE_SIZE(5,"responseSize"),
- REFERRER(6,"referrer"),
- USER_AGENT(7,"userAgent"),
- ;
- private int index;
- private String name;
- Field(int index, String name) {this.index=index;this.name=name;}
- public int getIndex() {return index;}
- public String getName() {return name;}
- public String toString() {return name;}
- };
- public static final String[] _ALL_FIELDS = {"url","timestamp","ip","httpMethod"
- ,"httpStatusCode","responseSize","referrer","userAgent",};
- ...
- }
我们可以看到字段的声明,要注意的是在Avro中,使用Utf8这个类来实现string,我们还可以看到一个Avro Schema的声明,还有内嵌的枚举类型Field.
Gora可以方便的定义不同类型的数据模型,如列数据模型(HBase,Cassandra),也有SQL数据模型,还有文件模型,如json,xml,并且还有key-value数据模型,而这些数据模型与数据存储之间的映射关系被定义在一个xml文件中,每一个数据存储抽象都有一个相对应的映射格式,这个映射文件声明了Avro schema中定义的类字段到数据存储抽象的一个映射关系,下面上面这个例子的一个HBase的映射文件, gora-hbase-mappings.xml
- <gora-orm>
- <table name="Pageview"> <!-- optional descriptors for tables -->
- <family name="common"/> <!-- This can also have params like compression, bloom filters -->
- <family name="http"/>
- <family name="misc"/>
- </table>
- <class name="org.apache.gora.tutorial.log.generated.Pageview" keyClass="java.lang.Long" table="AccessLog">
- <field name="url" family="common" qualifier="url"/>
- <field name="timestamp" family="common" qualifier="timestamp"/>
- <field name="ip" family="common" qualifier="ip" />
- <field name="httpMethod" family="http" qualifier="httpMethod"/>
- <field name="httpStatusCode" family="http" qualifier="httpStatusCode"/>
- <field name="responseSize" family="http" qualifier="responseSize"/>
- <field name="referrer" family="misc" qualifier="referrer"/>
- <field name="userAgent" family="misc" qualifier="userAgent"/>
- </class>
- ...
- </gora-orm>
我们可以看到,这个映射文件以<gora-orm>标记为头节点,Hbase的Gora文件有两类节点,一个是table,别一个是class. 这里的table是可选的,它一般是用来来定义表的一些属性,如压缩,块缓冲等信息。而其中的class定义的Avro定义的类结构与数据存储之间的映射,其中的name定义的类名,keyClass定义的输入的<K,V>对中的K的类型,最后的那个table表示HBase所对应的表名,而field有三个属性,第一个是name,它表示类的成员名,并且它在HBase也表示column family的label,第二个是family,在HBase中表示column family,第三个是qualifier,它表示HBase中的column family。
下面我们来运行其中的logmanager的例子
- $ bin/gora logmanager
- which lists the usage as:
- LogManager -parse <input_log_file>
- -get <lineNum>
- -query <lineNum>
- -query <startLineNum> <endLineNum>
- -delete <lineNum>
- -deleteByQuery <startLineNum> <endLineNum>
如下命令来运行解析程式
- $ bin/gora logmanager -parse gora-tutorial/src/main/resources/access.log
你可以使用如下命令来查看HBase中的结果:
- hbase(main):004:0> scan 'AccessLog', {LIMIT=>1}
- ROW COLUMN+CELL
- \x00\x00\x00\x00\x00\x00\x00\x00 column=common:ip, timestamp=1342791952462, value=88.240.129.183
- \x00\x00\x00\x00\x00\x00\x00\x00 column=common:timestamp, timestamp=1342791952462, value=\x00\x00\x01\x1F\xF1\xAElP
- \x00\x00\x00\x00\x00\x00\x00\x00 column=common:url, timestamp=1342791952462, value=/index.php?a=1__wwv40pdxdpo&k=218978
- \x00\x00\x00\x00\x00\x00\x00\x00 column=http:httpMethod, timestamp=1342791952462, value=GET
- \x00\x00\x00\x00\x00\x00\x00\x00 column=http:httpStatusCode, timestamp=1342791952462, value=\x00\x00\x00\xC8
- \x00\x00\x00\x00\x00\x00\x00\x00 column=http:responseSize, timestamp=1342791952462, value=\x00\x00\x00+
- \x00\x00\x00\x00\x00\x00\x00\x00 column=misc:referrer, timestamp=1342791952462, value=http://www.buldinle.com/index.php?a=1__WWV
- 40pdxdpo&k=218978
- \x00\x00\x00\x00\x00\x00\x00\x00 column=misc:userAgent, timestamp=1342791952462, value=Mozilla/4.0 (compatible; MSIE 6.0; Window
- s NT 5.1)
- 1 row(s) in 0.0180 seconds
下来我们来分析一个这个程式,这个文件在gora-tutorial/src/main/java/org/apache/gora/tutorial/log/LogManager.java下面。
- 初始化
- public LogManager() {
- try {
- init();
- } catch (IOException ex) {
- throw new RuntimeException(ex);
- }
- }
- private void init() throws IOException {
- dataStore = DataStoreFactory.getDataStore(Long.class, Pageview.class);
- }
这个方法主要是用于初始化DataStore,使用DataStoreFactory的静态方法,其中二个参数代表<K,V>,其中的DataStore是Gora中的一个重要的抽象,它用于操作对象的持久化,我们要吧用它来实现不同的module,如HBase,SQl等.
下面的代码是用于解析日志与生成pageView对象。
- private void parse(String input) throws IOException, ParseException {
- BufferedReader reader = new BufferedReader(new FileReader(input));
- long lineCount = 0;
- try {
- String line = reader.readLine();
- do {
- Pageview pageview = parseLine(line);
- if(pageview != null) {
- //store the pageview
- storePageview(lineCount++, pageview);
- }
- line = reader.readLine();
- } while(line != null);
- } finally {
- reader.close();
- }
- }
- private Pageview parseLine(String line) throws ParseException {
- StringTokenizer matcher = new StringTokenizer(line);
- //parse the log line
- String ip = matcher.nextToken();
- ...
- //construct and return pageview object
- Pageview pageview = new Pageview();
- pageview.setIp(new Utf8(ip));
- pageview.setTimestamp(timestamp);
- ...
- return pageview;
- }
最后是存储对象到后台存储模型中,用完以后别忘了关闭存储
- /** Stores the pageview object with the given key */
- ivate void storePageview(long key, Pageview pageview) throws IOException {
- dataStore.put(key, pageview);
- private void close() throws IOException {
- //It is very important to close the datastore properly, otherwise
- //some data loss might occur.
- if(dataStore != null)
- dataStore.close();
从数据库中取数据
- /** Fetches a single pageview object and prints it*/
- private void get(long key) throws IOException {
- Pageview pageview = dataStore.get(key);
- printPageview(pageview);
- }
查询对象
- /** Queries and prints pageview object that have keys between startKey and endKey*/
- private void query(long startKey, long endKey) throws IOException {
- Query<Long, Pageview> query = dataStore.newQuery();
- //set the properties of query
- query.setStartKey(startKey);
- query.setEndKey(endKey);
- Result<Long, Pageview> result = query.execute();
- printResult(result);
- }
删除对象
- /**Deletes the pageview with the given line number */
- private void delete(long lineNum) throws Exception {
- dataStore.delete(lineNum);
- dataStore.flush(); //write changes may need to be flushed before
- //they are committed
- }
- /** This method illustrates delete by query call */
- private void deleteByQuery(long startKey, long endKey) throws IOException {
- //Constructs a query from the dataStore. The matching rows to this query will be deleted
- QueryLong, Pageview> query = dataStore.newQuery();
- //set the properties of query
- query.setStartKey(startKey);
- query.setEndKey(endKey);
- dataStore.deleteByQuery(query);
- }
5. 参考
http://gora.apache.org/docs/current/quickstart.htmlhttp://gora.apache.org/docs/current/tutorial.html
转载:http://blog.youkuaiyun.com/amuseme_lu/article/details/7769017