Обозначенное вами решение - отправка частей документа в виде отдельных сообщений - вероятно, лучше всего подходит для вашего случая использования. По сути, Cap'n Proto не предназначен для потоковой передачи фрагментов одного сообщения, поскольку это не соответствует его свойствам произвольного доступа (например, что происходит, когда вы пытаетесь следовать указателю, указывающему на фрагмент, который вы не получили). пока что?). Вместо этого, если вам нужна потоковая передача, вам следует разделить большое сообщение на серию сообщений меньшего размера.
Тем не менее, в отличие от других подобных систем (например, Protobuf), Cap'n Proto не требует строгого размещения сообщений в памяти. В частности, вы можете проделать некоторые трюки, используя mmap(2)
. Если данные вашего документа поступают из файла на диске, вы можете mmap()
сохранить этот файл в памяти, а затем включить его в свое сообщение. При использовании mmap()
операционная система фактически не считывает данные с диска, пока вы не попытаетесь получить доступ к памяти, и ОС также может очистить страницы из памяти после обращения к ним, поскольку она знает, что у нее все еще есть копия на диске. Это часто позволяет вам писать гораздо более простой код, поскольку вам больше не нужно думать об управлении памятью.
Чтобы включить фрагмент mmap()
ed в сообщение Cap'n Proto, вам нужно использовать capnp::Orphanage::referenceExternalData()
. Например, учитывая:
struct MyDocument {
body @0 :Data;
# (other fields)
}
Вы можете написать:
// Map file into memory.
void* ptr = (kj::byte*)mmap(
nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (ptr == MAP_FAILED) {
KJ_FAIL_SYSCALL("mmap", errno);
}
auto data = capnp::Data::Reader((kj::byte*)ptr, size);
// Incorporate it into a message.
capnp::MallocMessageBuilder message;
auto root = message.getRoot<MyDocument>();
root.adoptDocumentBody(
message.getOrphanage().referenceExternalData(data));
Поскольку Cap'n Proto является нулевым копированием, он в конечном итоге будет записывать память mmap()
ed напрямую в сокет, даже не обращаясь к ней. Затем ОС должна прочитать содержимое с диска и вывести его в сокет по мере необходимости.
Конечно, у вас все еще есть проблема на принимающей стороне. Вы обнаружите, что гораздо сложнее спроектировать принимающую сторону для чтения в память mmap()
ed. Одна из стратегий может состоять в том, чтобы сначала сбросить весь поток непосредственно в файл (без использования библиотеки Cap'n Proto), затем mmap()
этот файл и использовать capnp::FlatArrayMessageReader
для чтения mmap()
ed данных на месте.
Я описываю все это, потому что это изящная вещь, которая возможна с Cap'n Proto, но не с большинством других фреймворков сериализации (например, вы не можете сделать это с Protobuf). Иногда очень полезно пошутить с mmap()
— я успешно использовал это в нескольких местах в Sandstorm, родителя Cap'n Proto. проект. Однако я подозреваю, что для вашего варианта использования разбиение документа на серию сообщений, вероятно, имеет больше смысла.
person
Kenton Varda
schedule
16.01.2016