Avro数据格式学习及其Java操作

标签: Avro 分类: Java 创建时间:2019-10-10 06:56:59 更新时间:2024-11-15 10:49:42

先吐槽一波,对于一个半吊子的后台和前端来说,老板提了这么一个需求,将大数据的压缩数据格式Avro,使用javascript读取,具体的意思我觉得是:avro这种数据格式压缩率很高,后台生产出来,传递到前台,js将其解析。这是什么牛逼操作,咱也不懂,咱也不敢问。老板说能行就行呗,不行也得行,前几天不是开会反复强调了吗,要服从公司的价值观,什么是公司的价值观?老板的价值观就是公司的价值观。得嘞,那就开始吧。

1.什么是Avro?

Avro是一种远程过程调用和数据序列化框架,是在Apache的Hadoop项目之内开发的。它使用JSON来定义数据类型和通讯协议,使用压缩二进制格式来序列化数据。它主要用于Hadoop,它可以为持久化数据提供一种序列化格式,并为Hadoop节点间及从客户端程序到Hadoop服务的通讯提供一种电报格式。它类似于Thrift,但当数据库模式改变时,它不要求运行代码生成程序,除非是对静态类型的语言。虽然理论上任何语言都可以使用Avro,但是以下语言有专门为其编写的API:C、C++、C#、Go、Java、Perl、PHP、Python、Ruby、Scala。

参考文章:
1.维基百科Apache Avro

2.怎么样生成Avro格式?

知道了Avro是干什么的,那就撸起袖子加油干吧。不是说支持Java吗,那就先用java生成Avro格式的数据先看看。
(1) 编写模式schema,example.avsc文件,内容如下。

1
2
3
4
5
6
7
8
9
{"namespace": "example.avro",
"type": "record",
"name": "User",
"fields": [
{"name": "name", "type": "string"},
{"name": "favorite_number", "type": ["int", "null"]},
{"name": "favorite_color", "type": ["string", "null"]}
]
}

(2) 在官网 ,找到下载链接,把avro-tool工具下载下来,执行压缩命令,将schema转成

1
2
3
4
5
java -jar /path/to/avro-tools-1.9.1.jar compile schema <schema file> <destination>

## 示例
java -jar avro-tools-1.7.7.jar compile schema example.avsc .

结果就生成了这么一个java类:User.java,有了这个类,我们就可以对数据进行序列化和反序列化了。

3.使用avro-tool将json数据序列化位avro格式

根据参考文章描述,新建一个twitter.json文件,里面存放数据

1
2
{"username":"miguno","tweet":"Rock: Nerf paper, scissors is fine.","timestamp": 1366150681 } 
{"username":"BlizzardCS","tweet":"Works as intended. Terran is IMBA.","timestamp": 1366154481 }

新建twitter.avsc文件,内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"type" : "record",
"name" : "twitter_schema",
"namespace" : "com.miguno.avro",
"fields" : [
{ "name" : "username",
"type" : "string",
"doc" : "Name of the user account on Twitter.com" },
{
"name" : "tweet",
"type" : "string",
"doc" : "The content of the user‘s Twitter message" },
{
"name" : "timestamp",
"type" : "long",
"doc" : "Unix epoch time in seconds" }
],
"doc:" : "A basic schema for storing Twitter messages"
}

这样就可以使用命令,将json文件转化位avro文件了。

1
2
3
4
## 测试一
java -jar ./avro-tools-1.9.1.jar fromjson --schema-file twitter.avsc twitter.json > twitter.avro
## 测试二
java -jar ./avro-tools-1.9.1.jar fromjson --schema-file twitter.avsc --code deflate twitter.json > twitter.avro

但是在实际的操作中,虽然生成了twitter.avro文件,但是命令行中报错了:

4.java代码序列化和反序列化avro文件

(1) 新建maven工程,在pom.xml中添加如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>AvroJava</artifactId>
<version>1.0-SNAPSHOT</version>

<name>AvroJava</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<!-- 添加avro支持 -->
<dependency>
<groupId>org.apache.avro</groupId>
<artifactId>avro</artifactId>
<version>1.9.1</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>1.9.1</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>schema</goal>
</goals>
<configuration>
<sourceDirectory>${project.basedir}/src/main/avro/</sourceDirectory>
<outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

(2) 参考文章中说:“如果你使用了 Avro Maven plugin,就会自动执行代码生成。”,我始终没有测试成功自动生成User类(有更新,可以通过 mvn compile 自动生成)。只能通过命令行工具avro-tools,使用:

1
java -jar avro-tools-1.7.7.jar compile schema example.avsc .

将example.avsc生成了User.java类(生成什么类,是你在avsc文件中定义name时指定的,包名也是通过定义namespace指定的),这里给一个example.avsc的例子

1
2
3
4
5
6
7
8
9
{"namespace": "example.avro",
"type": "record",
"name": "User",
"fields": [
{"name": "name", "type": "string"},
{"name": "favorite_number", "type": ["int", "null"]},
{"name": "favorite_color", "type": ["string", "null"]}
]
}

(3) 将生成的User复制到工程中,修改包名(如果你已经指定的和工程中的包名一样,就不用改了)

(4) 在main函数中编写如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import org.apache.avro.file.DataFileReader;
import org.apache.avro.file.DataFileWriter;
import org.apache.avro.io.DatumReader;
import org.apache.avro.io.DatumWriter;
import org.apache.avro.specific.SpecificDatumReader;
import org.apache.avro.specific.SpecificDatumWriter;

import java.io.File;

/**
* Hello world!
*
*/
public class Main
{
public static void main( String[] args )
{
User user1 = new User();

user1.setName("Alyssa");
user1.setFavoriteNumber(256);
// Leave favorite color null

// Alternate constructor
User user2 = new User("Ben", 7, "red");

// Construct via builder
User user3 = User.newBuilder()
.setName("Charlie")
.setFavoriteColor("blue")
.setFavoriteNumber(null)
.build();
// Serialize user1, user2 and user3 to disk
DatumWriter<User> userDatumWriter = new SpecificDatumWriter<User>(User.class);
DataFileWriter<User> dataFileWriter = new DataFileWriter<User>(userDatumWriter);
try {
dataFileWriter.create(user1.getSchema(), new File("users.avro"));
dataFileWriter.append(user1);
dataFileWriter.append(user2);
dataFileWriter.append(user3);
dataFileWriter.close();
}catch (Exception e){
System.out.println();
}

//反序列化
// Deserialize Users from disk
DatumReader<User> userDatumReader = new SpecificDatumReader<User>(User.class);

// 使用这种方式可以避免因为对象太多而造成的对 gc 的不良影响。
User user = null;
try{
DataFileReader<User> dataFileReader = new DataFileReader<User>( new File("users.avro"),
userDatumReader);
while (dataFileReader.hasNext()) {
// Reuse user object by passing it to next(). This saves us from
// allocating and garbage collecting many objects for files with
// many items.
user = dataFileReader.next(user);
System.out.println(user);
}
}catch (Exception e){
System.out.println(e);
}
}
}

这里为了测试的File对象里面写上了文件的绝对路径。

(5) 运行Main函数,打印输出

这里的经过可以不用管,我也没有深究,等有空的时候,再深入探讨吧。这样就完成了一个读写avro格式文件的示例了。

(2019年10月11日更新)
今天通过执行:mvn compile,确实是自动根据定义的sources中的avro目录下的user.avsc,自动生成了user类。

5.浏览器解析avro格式数据

理论上avro是跨平台的数据格式,但是偏偏找不到浏览器解析avro格式的数据的例子。既然是压缩的二进制数据,应该有办法读取。

(1) 浏览器可下载
通常在IIS中配置不支持的数据类型的时候,会出现404错误(实际上明明就有这个路径的),添加mime类型: application/octet-stream,浏览器就可以下载了,使用ajax也可以获取到数据并读取了。

(2) js解析avro

这个问题很大,也没有现成的例子,所以我另开了一篇文章来探讨如何解决这个问题的过程 javascript解析avro格式数据的探讨

小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。