将Google原型缓冲区与Jersey / JAX-RS相结合

目前我有一个RESTful Web服务,其端点通过Jersey / JAX-RS公开:

@Path("/widgets") public class WidgetResource { @GET List getAllWidgets() { // gets Widgets somehow } @POST Widget save(Widget w) { // Save widget and return it } } 

我使用Jackson将我的POJO序列化/反序列化为JSON,我的服务都响应并将我的POJO作为application/json发回。

我现在希望可能使用Google协议缓冲区(或等效技术)来帮助压缩/优化客户端和服务之间的通信,因为JSON /文本相当庞大/浪费。

实际上,我有一个由“微服务”架构组成的大型后端; 数十种REST服务相互通信; 这就是为什么我希望优化在所有这些之间来回发送的消息。

所以我问:是否仍然可以让Jersey / JAX-RS提供我的服务端点,但要删除Jackson / JSON的内容并用Google协议缓冲区替换它? 如果是这样,这段代码可能是什么样的?

JAX-RS使用MessageBodyReaderMessageBodyWriter来对不同的媒体类型进行序列化/反序列化。 您可以在JAX-RS实体提供商处阅读更多内容。 您可以自己编写protobuf对象的序列化/派生的句柄。 然后只需通过发现显式或隐式地向应用程序注册“提供者”。

widgets.proto

 package widget; option java_package = "protobuf.example"; option java_outer_classname = "WidgetsProtoc"; message Widget { required string id = 1; required string name = 2; } message WidgetList { repeated Widget widget = 1; } 

当编译它时,我将留下一个带有静态内部WidgetWidgetList类的WidgetList类。

WidgetResource

 import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; import protobuf.example.WidgetsProtoc.Widget; import protobuf.example.WidgetsProtoc.WidgetList; @Path("/widgets") public class WidgetResource { @GET @Produces("application/protobuf") public Response getAllWidgets() { Widget widget1 = Widget.newBuilder().setId("1").setName("widget 1").build(); Widget widget2 = Widget.newBuilder().setId("2").setName("widget 2").build(); WidgetList list = WidgetList.newBuilder() .addWidget(widget1).addWidget(widget2).build(); return Response.ok(list).build(); } @POST @Consumes("application/protobuf") public Response postAWidget(Widget widget) { StringBuilder builder = new StringBuilder("Saving Widget \n"); builder.append("ID: ").append(widget.getId()).append("\n"); builder.append("Name: ").append(widget.getName()).append("\n"); return Response.created(null).entity(builder.toString()).build(); } } 

您会注意到"application/protobuf"媒体类型的使用。 这不是标准的媒体类型 ,但工作中有一个草案 。 此外, Guava库已将此媒体类型定义为MediaType.PROTOBUF ,其转换为"application/protobuf" ,因此我选择坚持使用

MessageBodyReaderMessageBodyWriter都在一个类中定义。 您可以选择单独进行。 没有区别。

 import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import javax.ws.rs.BadRequestException; import javax.ws.rs.Consumes; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.MessageBodyWriter; import javax.ws.rs.ext.Provider; import protobuf.example.WidgetsProtoc.Widget; import protobuf.example.WidgetsProtoc.WidgetList; @Provider @Produces("application/protobuf") @Consumes("application/protobuf") public class WidgetProtocMessageBodyProvder implements MessageBodyReader, MessageBodyWriter { @Override public boolean isReadable(Class type, Type type1, Annotation[] antns, MediaType mt) { return Widget.class.isAssignableFrom(type) || WidgetList.class.isAssignableFrom(type); } @Override public Object readFrom(Class type, Type type1, Annotation[] antns, MediaType mt, MultivaluedMap mm, InputStream in) throws IOException, WebApplicationException { if (Widget.class.isAssignableFrom(type)) { return Widget.parseFrom(in); } else if (WidgetList.class.isAssignableFrom(type)) { return WidgetList.parseFrom(in); } else { throw new BadRequestException("Can't Deserailize"); } } @Override public boolean isWriteable(Class type, Type type1, Annotation[] antns, MediaType mt) { return Widget.class.isAssignableFrom(type) || WidgetList.class.isAssignableFrom(type); } @Override public long getSize(Object t, Class type, Type type1, Annotation[] antns, MediaType mt) { return -1; } @Override public void writeTo(Object t, Class type, Type type1, Annotation[] antns, MediaType mt, MultivaluedMap mm, OutputStream out) throws IOException, WebApplicationException { if (t instanceof Widget) { Widget widget = (Widget)t; widget.writeTo(out); } else if (t instanceof WidgetList) { WidgetList list = (WidgetList)t; list.writeTo(out); } } } 

TestCase (确保提供程序已在服务器和客户端注册)

 @Test public void testGetIt() { // Get all list WidgetList list = target.path("widgets") .request().get(WidgetList.class); System.out.println("===== Response from GET ====="); for (Widget widget: list.getWidgetList()) { System.out.println("id: " + widget.getId() + ", name: " + widget.getName()); } // Post one Widget widget = Widget.newBuilder().setId("10") .setName("widget 10").build(); Response responseFromPost = target.path("widgets").request() .post(Entity.entity(widget, "application/protobuf")); System.out.println("===== Response from POST ====="); System.out.println(responseFromPost.readEntity(String.class)); responseFromPost.close(); } 

结果:

 ===== Response from GET ===== id: 1, name: widget 1 id: 2, name: widget 2 ===== Response from POST ===== Saving Widget ID: 10 Name: widget 10