一、自定义spring mvc protostuff消息转换器
1.protostuff是一个序列化和反序列化的高性能类型,它比json字节数少,且快,适合做rpc调用
2.自定义protostuff消息转换器,代码如下:
public class ProtostuffHttpMessageConverter extends AbstractHttpMessageConverter<Object> { /** * 避免每次序列化都重新申请Buffer空间 */ private static LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); /** * 缓存Schema */ private static Map<Class<?>, Schema<?>> schemaCache = new ConcurrentHashMap<>(); public ProtostuffHttpMessageConverter() { super(new MediaType("application", "xxx-protostuff", Charset.forName("UTF-8"))); } @Override protected boolean supports(Class aClass) { return true; } @Override protected Object readInternal(Class aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException { InputStream in = httpInputMessage.getBody(); byte[] outByte = IOUtils.toByteArray(in); return this.deserialize(outByte, aClass); } public <T> T deserialize(byte[] data, Class<T> clazz) { Schema<T> schema = getSchema(clazz); T obj = schema.newMessage(); ProtostuffIOUtil.mergeFrom(data, obj, schema); return obj; } @Override protected void writeInternal(Object o, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException { httpOutputMessage.getBody().write(this.serialize(o)); } private <T> byte[] serialize(T obj) { Class<T> clazz = (Class<T>) obj.getClass(); Schema<T> schema = getSchema(clazz); byte[] data; try { data = ProtostuffIOUtil.toByteArray(obj, schema, buffer); } finally { buffer.clear(); } return data; } private static <T> Schema<T> getSchema(Class<T> clazz) { Schema<T> schema = (Schema<T>) schemaCache.get(clazz); if (Objects.isNull(schema)) { //这个schema通过RuntimeSchema进行懒创建并缓存 //所以可以一直调用RuntimeSchema.getSchema(),这个方法是线程安全的 schema = RuntimeSchema.getSchema(clazz); if (Objects.nonNull(schema)) { schemaCache.put(clazz, schema); } } return schema; } } |
①在构造方法中定义一个支持的媒体类型:application/xxx-protostuff
②readInternal方法用protostuff对输入的参数做反序列化工作
③writeInternal方法使用protostuff对返回的对象做序列化操作
3.注册自定义的消息转换器到spring中,代码如下:
@Configuration public class MvcConfig extends WebMvcConfigurationSupport { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(protostuffHttpMessageConverter()); } @Bean public ProtostuffHttpMessageConverter protostuffHttpMessageConverter() { return new ProtostuffHttpMessageConverter(); } } |
4.暴露一个服务接口,供下面的的feign调用,代码如下:
@Api(tags = "protostuff消息转换器服务") @RestController @RequestMapping(ApiConstants.PATH + "/protostuff") public class ProtostuffController { @ApiOperation(value = "反序列化protostuff二进制数据") @PostMapping(value = "/deserialize") public ResponseVO<?> deserialize(@RequestBody ProtostuffEntity entity) { return ResponseVO.ok(entity); } @ApiOperation(value = "反序列化protostuff二进制数据1") @PostMapping(value = "/deserialize1") public ResponseVO<?> deserialize1(@RequestBody ProtostuffEntity entity) { return ResponseVO.ok(entity); } } |
二、自定义feign protostuff消息转换器
1.自定义feign protostuff消息转换器,代码如下:
public class ProtostuffHttpMessageConverter implements GenericHttpMessageConverter<Object> { /** * 避免每次序列化都重新申请Buffer空间 */ private static LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); /** * 缓存Schema */ private static Map<Class<?>, Schema<?>> schemaCache = new ConcurrentHashMap<>(); @Override public boolean canRead(Type type, Class<?> aClass, MediaType mediaType) { if (Objects.isNull(mediaType)) { return false; } MediaType supportType = new MediaType("application", "xxx-protostuff", Charset.forName("UTF-8")); return supportType.toString().startsWith(mediaType.toString()); } @Override public boolean canRead(Class<?> aClass, MediaType mediaType) { if (Objects.isNull(mediaType)) { return false; } MediaType supportType = new MediaType("application", "xxx-protostuff", Charset.forName("UTF-8")); return supportType.toString().startsWith(mediaType.toString()); } @Override public boolean canWrite(Class<?> aClass, MediaType mediaType) { if (Objects.isNull(mediaType)) { return false; } MediaType supportType = new MediaType("application", "xxx-protostuff", Charset.forName("UTF-8")); return supportType.toString().startsWith(mediaType.toString()); } @Override public List<MediaType> getSupportedMediaTypes() { return Stream.of(new MediaType("application", "xxx-protostuff", Charset.forName("UTF-8"))).collect(Collectors.toList()); } @Override public Object read(Class<?> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException { return readInternal(aClass, httpInputMessage); } @Override public void write(Object o, MediaType mediaType, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException { writeInternal(o, httpOutputMessage); } @Override public Object read(Type type, Class<?> aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException { ParameterizedTypeImpl type1 = (ParameterizedTypeImpl) type; return readInternal(type1.getRawType(), httpInputMessage); } @Override public boolean canWrite(Type type, Class<?> aClass, MediaType mediaType) { if (Objects.isNull(mediaType)) { return false; } MediaType supportType = new MediaType("application", "xxx-protostuff", Charset.forName("UTF-8")); return supportType.toString().startsWith(mediaType.toString()); } @Override public void write(Object o, Type type, MediaType mediaType, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException { writeInternal(o, httpOutputMessage); } protected Object readInternal(Class aClass, HttpInputMessage httpInputMessage) throws IOException, HttpMessageNotReadableException { InputStream in = httpInputMessage.getBody(); byte[] outByte = IOUtils.toByteArray(in); return this.deserialize(outByte, aClass); } public <T> T deserialize(byte[] data, Class<T> clazz) { Schema<T> schema = getSchema(clazz); T obj = schema.newMessage(); ProtostuffIOUtil.mergeFrom(data, obj, schema); return obj; } protected void writeInternal(Object o, HttpOutputMessage httpOutputMessage) throws IOException, HttpMessageNotWritableException { httpOutputMessage.getBody().write(this.serialize(o)); } private <T> byte[] serialize(T obj) { Class<T> clazz = (Class<T>) obj.getClass(); Schema<T> schema = getSchema(clazz); byte[] data; try { data = ProtostuffIOUtil.toByteArray(obj, schema, buffer); } finally { buffer.clear(); } return data; } private static <T> Schema<T> getSchema(Class<T> clazz) { Schema<T> schema = (Schema<T>) schemaCache.get(clazz); if (Objects.isNull(schema)) { //这个schema通过RuntimeSchema进行懒创建并缓存 //所以可以一直调用RuntimeSchema.getSchema(),这个方法是线程安全的 schema = RuntimeSchema.getSchema(clazz); if (Objects.nonNull(schema)) { schemaCache.put(clazz, schema); } } return schema; } } |
①fegin中统一解码的入口类是SpringEncoder,SpringEncoder获取的converter需要继承GenericHttpMessageConverter类才可生效,否则获取到的responseClass为null,会报no suitable HttpMessageConverter found for response type错误
②canRead和canWirte方法中对媒体类型做判断,如果为application/xxx-protostuff媒体类型,就返回true
2.注册自定义的feign protostuff消息转换,如上面3.注册相同
3.写一个测试fegin client,调用上面的服务,代码如下:
@FeignClient(value = "tsc", url = "http://localhost:8099/api/tsc/protostuff") public interface ProtostuffFeignClient { @ApiOperation(value = "protostuff二进制数据") @PostMapping(value = "/deserialize", consumes = {"application/xxx-protostuff"}, produces = {"application/xxx-protostuff"}) ResponseVO<ProtostuffEntity> deserialize(@RequestBody ProtostuffEntity entity); @ApiOperation(value = "反序列化protostuff二进制数据1") @PostMapping(value = "/deserialize1") public ResponseVO<ProtostuffEntity> deserialize1(@RequestBody ProtostuffEntity entity); } |
4.写一个测试调用的controller,调用上面的feign服务,代码如下:
@RestController @RequestMapping(ApiConstants.PATH + "/rpc-client") public class ProtostuffClientController { @Autowired private ProtostuffFeignClient feignClient; @ApiOperation(value = "调试protostuff") @GetMapping(value = "/protostuff") public ResponseVO<?> query() { ProtostuffEntity entity = new ProtostuffEntity(); entity.setDesc("protostuff"); entity.setId("protostuff-xxx"); ResponseVO<ProtostuffEntity> ret = feignClient.deserialize(entity); return ResponseVO.ok(ret); } } |