Điều này phần nào là ảnh chụp trong bóng tối trong trường hợp ai đó hiểu biết về việc triển khai Java của Apache Avro đang đọc.Trong Java, làm cách nào tôi có thể tạo một tệp tương đương với tệp vùng chứa Avro Apache mà không bị buộc phải sử dụng Tệp làm phương tiện?
Mục tiêu cấp cao của tôi là có một số cách để truyền tải một số dữ liệu avro qua mạng (ví dụ, chỉ cần nói HTTP, nhưng giao thức cụ thể không quan trọng cho mục đích này). Trong bối cảnh của tôi, tôi có một HttpServletResponse tôi cần phải viết dữ liệu này bằng cách nào đó.
ban đầu tôi cố gắng ghi dữ liệu như những gì lên tới một phiên bản ảo của một tập tin chứa Avro (giả sử rằng "phản ứng" là loại HttpServletResponse):
response.setContentType("application/octet-stream");
response.setHeader("Content-transfer-encoding", "binary");
ServletOutputStream outStream = response.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(outStream);
Schema someSchema = Schema.parse(".....some valid avro schema....");
GenericRecord someRecord = new GenericData.Record(someSchema);
someRecord.put("somefield", someData);
...
GenericDatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<GenericRecord>(someSchema);
DataFileWriter<GenericRecord> fileWriter = new DataFileWriter<GenericRecord>(datumWriter);
fileWriter.create(someSchema, bos);
fileWriter.append(someRecord);
fileWriter.close();
bos.flush();
Đây là tất cả tiền phạt và dandy, ngoại trừ mà nó quay ra Avro không thực sự cung cấp một cách để đọc một tập tin chứa ngoài từ một tập tin thực tế: các DataFileReader chỉ có hai cấu trúc:
public DataFileReader(File file, DatumReader<D> reader);
và
public DataFileReader(SeekableInput sin, DatumReader<D> reader);
trong đó SeekableInput là một số biểu mẫu tùy chỉnh dành riêng cho từng người có sáng tạo cũng kết thúc đọc từ một tệp. Bây giờ cho rằng, trừ khi có một số cách để bằng cách nào đó ép buộc một InputStream vào một File (http://stackoverflow.com/questions/578305/create-a-java-file-object-or-equivalent-using-a-byte- array-in-memory-without-a gợi ý rằng không có, và tôi đã thử tìm kiếm xung quanh tài liệu Java), cách tiếp cận này sẽ không hoạt động nếu người đọc ở đầu kia của OutputStream nhận được tệp chứa avro đó (Tôi không chắc chắn lý do tại sao họ cho phép một trong những đầu ra tập tin container nhị phân avro để một OutputStream tùy ý mà không cung cấp một cách để đọc chúng từ InputStream tương ứng ở đầu bên kia, nhưng đó là bên cạnh điểm). Dường như việc triển khai trình đọc tệp chứa yêu cầu chức năng "có thể tìm kiếm" mà một Tệp cụ thể cung cấp.
Được rồi, do đó, có vẻ như cách tiếp cận đó sẽ không làm những gì tôi muốn. Làm thế nào về việc tạo ra một phản ứng JSON bắt chước tập tin container avro?
public static Schema WRAPPER_SCHEMA = Schema.parse(
"{\"type\": \"record\", " +
"\"name\": \"AvroContainer\", " +
"\"doc\": \"a JSON avro container file\", " +
"\"namespace\": \"org.bar.foo\", " +
"\"fields\": [" +
"{\"name\": \"schema\", \"type\": \"string\", \"doc\": \"schema representing the included data\"}, " +
"{\"name\": \"data\", \"type\": \"bytes\", \"doc\": \"packet of data represented by the schema\"}]}"
);
Tôi không chắc đây có phải là cách tốt nhất để tiếp cận những hạn chế nêu trên không, nhưng có vẻ như điều này có thể làm được điều đó. Ví dụ, tôi sẽ đặt lược đồ (ví dụ "Schema someSchema" ở trên) dưới dạng một String bên trong trường "schema", và sau đó đặt vào biểu mẫu tuần tự hóa nhị phân của một bản ghi phù hợp với lược đồ đó (ví dụ: "GenericRecord someRecord ") bên trong trường" dữ liệu ". Tôi thực sự muốn biết về một chi tiết cụ thể được mô tả dưới đây, nhưng tôi nghĩ rằng nó sẽ là đáng giá để cung cấp cho một bối cảnh lớn hơn là tốt, để nếu có một cách tiếp cận cao cấp tốt hơn tôi có thể tham gia (phương pháp này hoạt động nhưng không cảm thấy tối ưu) xin vui lòng cho tôi biết.
Câu hỏi của tôi là, giả sử tôi đi với phương pháp dựa trên JSON này, làm cách nào để viết biểu diễn nhị phân avro của Bản ghi của tôi vào trường "dữ liệu" của lược đồ AvroContainer? Ví dụ, tôi đứng dậy để ở đây:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GenericDatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<GenericRecord>(someSchema);
Encoder e = new BinaryEncoder(baos);
datumWriter.write(resultsRecord, e);
e.flush();
GenericRecord someRecord = new GenericData.Record(someSchema);
someRecord.put("schema", someSchema.toString());
someRecord.put("data", ByteBuffer.wrap(baos.toByteArray()));
datumWriter = new GenericDatumWriter<GenericRecord>(WRAPPER_SCHEMA);
JsonGenerator jsonGenerator = new JsonFactory().createJsonGenerator(baos, JsonEncoding.UTF8);
e = new JsonEncoder(WRAPPER_SCHEMA, jsonGenerator);
datumWriter.write(someRecord, e);
e.flush();
PrintWriter printWriter = response.getWriter(); // recall that response is the HttpServletResponse
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
printWriter.print(baos.toString("UTF-8"));
ban đầu tôi cố gắng bỏ qua mệnh đề ByteBuffer.wrap, nhưng sau đó thì dòng
datumWriter.write(someRecord, e);
ném một ngoại lệ mà tôi không thể đúc một mảng byte vào ByteBuffer.Khá công bằng, có vẻ như khi lớp Encoder (trong đó JsonEncoder là một lớp con) được gọi để viết một đối tượng Byte avro, nó đòi hỏi một ByteBuffer được đưa ra như một đối số. Vì vậy, tôi đã thử đóng gói byte [] bằng java.nio.ByteBuffer.wrap, nhưng khi dữ liệu được in ra, nó được in dưới dạng một chuỗi byte thẳng, mà không được chuyển qua đại diện hệ thập lục phân avro:
"data": {"bytes": ".....some gibberish other than the expected format...}
Điều đó có vẻ không đúng. Theo tài liệu hướng dẫn avro, đối tượng byte ví dụ mà chúng đưa ra nói rằng tôi cần đưa vào một đối tượng json, một ví dụ trong đó trông giống như "\ u00FF", và những gì tôi đưa vào đó rõ ràng không phải là định dạng đó. Những gì tôi muốn biết là:
- Ví dụ về định dạng byte avro là gì? Nó trông giống như "\ uDEADBEEFDEADBEEF ..."?
- Làm cách nào để ép buộc dữ liệu nhị phân của tôi (như đầu ra của BinaryEncoder thành mảng byte []) thành định dạng mà tôi có thể gắn vào đối tượng GenericRecord và in chính xác trong JSON? Ví dụ, tôi muốn một DATA đối tượng mà tôi có thể gọi trên một số GenericRecord "someRecord.put (" dữ liệu ", DATA);" với dữ liệu tuần tự hóa của tôi ở bên trong?
- Làm thế nào sau đó tôi sẽ đọc dữ liệu đó trở lại thành một mảng byte ở đầu kia (người tiêu dùng), khi nó được biểu diễn văn bản JSON và muốn tạo lại GenericRecord như được biểu diễn bằng định dạng AvroContainer JSON?
- (nhắc lại câu hỏi từ trước) Có cách nào tốt hơn tôi có thể làm tất cả những điều này không?
org.apache.avro.file.DataFileStream? – Chikei
SeekableInput không chỉ là một số biểu mẫu tùy chỉnh cụ thể cho từng công ty mà việc tạo ra kết thúc đọc từ một tệp. Có [SeekableByteArrayInput] (http://avro.apache.org/docs/current/api/java/org/apache/avro/file/SeekableByteArrayInput.html) đọc từ một mảng byte trong bộ nhớ. –
Rất tốt câu hỏi - và yêu cầu cần truy cập ngẫu nhiên là rất lạ, vì nó là không thể đáp ứng mà không có bộ đệm rất lớn. Và có vẻ như không cần thiết phải làm tốt ... Tôi không biết tại sao nó được cảm thấy truy cập ngẫu nhiên là cần thiết. Nhiều định dạng dữ liệu khác không thêm các yêu cầu như vậy để xử lý. – StaxMan