Dropwizard и Protocol Buffers на примере

Обратите внимание: хотя в этом вопросе конкретно упоминается Dropwizard, я полагаю, что любой, кто имеет опыт работы с Jersey/JAX-RS, должен быть в состоянии ответить на этот вопрос, поскольку я полагаю, что Dropwizard просто следует Соглашения Джерси/JAX-RS под капотом.


У меня есть служба Dropwizard, которая записывает/красит в JSON и прекрасно работает.

Теперь я хотел бы переключить его на чтение/запись двоичных данных (чтобы минимизировать пропускную способность сети). Я вижу, что есть Dropwizard-Protobuf lib, но у меня есть несколько опасений по поводу реализации двоичной сериализации в Dropwizard. .

Во-первых, вот важные вещи из моего текущего (ориентированного на JSON) кода:

// Groovy pseudo-code

// Domain entity/POJO
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
class Fizz {
    @JsonProperty
    String name

    @JsonProperty
    boolean isBuzz    
}

// The Dropwizard app entry point
class FizzService extends Application<FizzConfiguration> {
    @Override
    void run(FizzConfiguration fizzCfg, Environment env) throws Exception {
        // ... lots of stuff

        env.jersey().register(new FizzService())
    }
}

// JAX-RS resource with a sample GET endpoint
@Path(value = "/fizz")
@Produces(MediaType.APPLICATION_JSON)
class FizzResource {
    @GET
    @Path("/{id}")
    Fizz getFizzById(@PathParam("id") int id) {
        // Look up a 'Fizz' in a DB and return it.
        lookupFizzinDB(id)
    }
}

Как видите, конечная точка GET /fizz ожидает объект запроса JSON, который содержит элемент с именем id типа int. Он возвращает объект ответа Fizz, который соответствует предоставленному id.

Я хочу переключить это с JSON на двоичный с помощью буферов протокола Google.

Согласно документам Dropwizard-Protobuf, это так же просто, как добавить это к моему методу FizzService#run(...):

environment.jersey().register(new ProtocolBufferMessageBodyProvider())

Проблема в том, что в настоящее время все мое приложение подключено к сериализации/десериализации в/из JSON. Аннотации @JsonProperty в моем классе Fizz имеют значение для Dropwizard. Аннотация @Produces(MediaType.APPLICATION_JSON) на FizzResource также играет важную роль. Я беспокоюсь, что заставить мое приложение Dropwizard читать/записывать двоичный файл, сгенерированный protobuf, не так просто, как 1-строчный текст, опубликованный в документации.

Я не женат на этой библиотеке. Если у кого-то есть опыт настройки конечных точек REST в приложении Dropwizard для приема/получения бинарного файла, сгенерированного protobuf, все, что мне нужно, — это работающее и эффективное решение. Идеи?


person smeeb    schedule 10.09.2015    source источник


Ответы (1)


Вы правы, это не так просто, как один лайнер. Вам нужно, чтобы protobuf сгенерировал код, чтобы он работал. Ознакомьтесь с документацией по буферам протоколов. Сначала вам нужно иметь прото-файл, который вы скомпилируете с помощью компилятора protobuf, который сгенерирует для вас код. Этот сгенерированный код — это то, что вы используете для создания объектов домена/модели. Поставщик protobuf от Dropwizard работает с этим скомпилированным кодом. Независимо от того, используете ли вы поставщика Dropwizard или нет, вам все равно нужно использовать сгенерированный код. См. раздел «Как начать» по ссылке выше.

После того, как у вас есть сгенерированный код, в вашем ресурсном методе сгенерированный класс/тип — это то, что вам нужно будет вернуть поставщику, чтобы он мог его сериализовать. Вам также потребуется @Produces("application/x-protobuf") или @Produces(ProtocolBufferMediaType.APPLICATION_PROTOBUF) в вашем методе ресурса или классе ресурса, поэтому Джерси знает, как найти поставщика для типа мультимедиа.

Вы можете поддерживать как application/json, так и application/x-protobuf, так как у вас может быть более одного типа мультимедиа в файле @Produces. Просто используйте синтаксис @Produces({ .. , .. }).

Но это еще не все. Поскольку вам нужно будет вернуть два разных типа, то есть ваш простой POJO для JSON или сгенерированный тип для Protobuf, вам нужно либо проверить заголовок в методе ресурса

@Produces({"application/json", "application/x-protobuf"})
public Response getFoo(@Context HttpHeaders headers) {
    List<MediaType> accepts = headers.getAcceptableMediaTypes();
    if (accepts.contains(MediaType.APPLICATION_JSON_TYPE) {
        return Response.ok(new Foo());
    } else if (accepts.contains(ProtocolBufferMediaType.APPLICATION_PROTOBUF_TYPE) {
        return Reponse.ok(new ProtoBufFoo());
    } else {
        // default
        return Response.ok(new Foo());
    }
}

Или у вас может быть два разных метода, по одному для каждого типа

@Produces("application/json")
public Response getFooJson() {
    return Response.ok(new Foo());
}

@Produces("application/x-protobuf")
public Response getFooProto() {
    return Response.ok(new ProtoBufFoo());
}

Независимо от того, что клиент отправляет в качестве своего заголовка Accept, это тип, который будет отправлен. Например Accept: application/json или Accept: application/x-protobuf

См. также:

person Paul Samsotha    schedule 11.09.2015
comment
Удивительный, потрясающий ответ @peeskillet (+1) - одно быстрое продолжение, если вы не возражаете: это запускает мой OCD, что я должен поддерживать 2 разных набора POJO (один для JSON, один для Protobuf). Есть ли способ объединить их в один POJO? Может быть, это хакерство, но я думал, что задача Gradle/Maven сгенерирует Protobuf POJO из файла .proto, а затем добавит в последующую задачу необходимые аннотации JSON в сгенерированный исходный файл? Это сработает?!? Или в поколении Protobuf есть нечто большее, чем просто это? Еще раз спасибо! - person smeeb; 11.09.2015
comment
Не знаю, то, что вы говорите, кажется довольно сложным. На самом деле я никогда не использовал Protobuf с Jersey, поэтому я никогда не изучал какие-либо варианты их слияния. Извини :-( - person Paul Samsotha; 11.09.2015
comment
Имеет смысл, и да, это, по общему признанию, неуклюже :-) Извините, я забыл одно важное (последнее - я обещаю!) Дополнение: если я использую ваш код выше в качестве руководства, мне все еще нужно использовать этот 1-лайн из Dropwizard -Protobuf lib (где я регистрирую провайдера Protobuf)? Я предполагаю, что это то, что ищет запросы, где тип Accept установлен на application/x-protobuf? Еще раз большое спасибо! - person smeeb; 11.09.2015
comment
да. Это то, что на самом деле регистрирует MessageBodyWriter для обработки сериализации. @Produces только для того, чтобы сказать Джерси искать поставщика, который может справиться с этим типом. Это не означает, что уже есть провайдер, который справится с этим. Если у вас нет провайдера для его обработки, то Джерси выдаст вам исключение с чем-то вроде No MessageBodyWriter для обработки типа application/x-protobuf и типа ProtobufFoo - person Paul Samsotha; 11.09.2015