Dropwizard and Protocol Buffers by example

こ雲淡風輕ζ 提交于 2019-12-10 10:27:47

问题


Please note: Although this question specifically mentions Dropwizard, I believe anyone with Jersey/JAX-RS experience should be able to answer this question, as I would imagine Dropwizard is just following Jersey/JAX-RS conventions under the hood.


I have a Dropwizard service that reds/writes in JSON and works beautifully.

I would like to now switch it to read/write binary data (to minimize network bandidth). I see there is the Dropwizard-Protobuf lib but I have a few concerns about implementing binary serialization in Dropwizard.

First off, here's the important stuff from my current (JSON-centric) code:

// 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)
    }
}

So as you can see, the GET /fizz endpoint expect a JSON request entity that contains an element called id of type int. It returns a Fizz response entity that matches the provided id.

I want to switch this from JSON to binary via Google Protocol Buffers.

According to the Dropwizard-Protobuf docs, this is as simple as just adding this to my FizzService#run(...) method:

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

The problem is that currently my whole app is wired to serialize/deserialize to/from JSON. The @JsonProperty annotations on my Fizz class have meaning to Dropwizard. The @Produces(MediaType.APPLICATION_JSON) annotation on the FizzResource also plays a critical role. I'm worried that making my Dropwizard app read/write protobuf-generated binary is not as easy as the 1-liner posted in the docs.

I'm not married to this library. If anyone has any experience setting up REST endpoints in a Dropwizard app to accept/receive protobuf-generated binary, all I care about is a working, efficient solution. Ideas?


回答1:


You're right, it's not as easy as the one liner. You need to have protobuf generate code for it to work. Check out the Protocol Buffers Documentation. You first need to have a proto file that you compile with the protobuf compiler, which generates the code for you. This generated code is what you use to build your domain/model objects. The protobuf provider from Dropwizard works off this compiled code. Whether or not you use the Dropwizard provider, you well still need to use the generated code. See the section "How do I start" in the above link.

After you have the generated code, then in your resource method, the generated class/type is what you will need to return for the provider to be able to serialize it. You will also need to have @Produces("application/x-protobuf") or @Produces(ProtocolBufferMediaType.APPLICATION_PROTOBUF) on your resource method or resource class, so Jersey knows how to find the provider for the media type.

You can support both application/json and application/x-protobuf, as you can have more that one media type in the @Produces. Just use the syntax @Produces({ .. , .. }).

That's not all though. Since you will need to return two different types, i.e your simple POJO for JSON, or the generated type for Protobuf, you will either need to check for the header in the resource method

@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());
    }
}

Or you can have two different method, one for each type

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

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

Whatever the client sends as its Accept header, that is the type that will be sent out. For example Accept: application/json or Accept: application/x-protobuf

See Also:

  • Combining Google proto buffers with Jersey/JAX-RS


来源:https://stackoverflow.com/questions/32510936/dropwizard-and-protocol-buffers-by-example

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!