protobuff-java 替代品 prorostuff

陳子昂發表於2020-03-12

網路主題

傳送方需要將類物件轉換成二進位制壓入專案自定義資料結構,接收方接收到資料後,需要將二進位制轉換成類物件。
專案接收方通過訊息號和回執ID,可以知道該訊息如何去處理。
prorostuff相比.proto檔案,不用先轉換和編譯.proto檔案,不需要做一層中間檔案的轉換。這層中間檔案經常會和版本嚴格匹配時出現格式語法錯誤。
Pom如下:

<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.4.0</version>
</dependency>

<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.4.0</version>
</dependency>

netty的編碼類

netty只用4是Java網路的一個主流方式
這裡需要通過繼承Netty4MessageToByteEncoder Message是Bean物件,根據自己專案定製來的

public class MessageEncoder extends MessageToByteEncoder<NettyMessage>{
@Override
protected void encode(ChannelHandlerContext ctx, NettyMessage msg, ByteBuf out) throws Exception {
//ProrostuffSerialize提供了序列化方式。
out.writeBytes(ProrostuffSerialize.serialize(msg));
}
}

關於對ProrostuffSerialize實現,本身還是建立有序快取空間,schema流式,提供正反序列化方式。

import com.dyuproject.protostuff.LinkedBuffer;

下面的ProtostuffIOUtil等都是這個包下面的。

public class ProrostuffSerialize{

private static class SerializeObject{
private Object target;
}

@SuppressWarnings("unchecked")
public static byte[] serialize(Object object) {
SerializeObject serializeObject = new SerializeObject();
serializeObject .target = object;
Class<serializeObject > serializeObjectClass = (Class<serializeObject >) serializeObject.getClass();
LinkedBuffer buffer= LinkedBuffer.allocate(1024 * 4);
try {
Schema<serializeObject > schema = RuntimeSchema.getSchema(serializeObjectClass );
return ProtostuffIOUtil.toByteArray(serializeObject , schema, buffer);
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
linkedBuffer.clear();
}
}


//clazz泛型是Bean物件Message
@SuppressWarnings("unchecked")
public static <T> T deserialize(byte[] data, Class<T> clazz) {
try {
//建立SerializeObject的schema物件
Schema<SerializeObject> schema = RuntimeSchema.getSchema(SerializeObject.class);
SerializeObject serializeObject= schema.newMessage();
ProtostuffIOUtil.mergeFrom(data, serializeObject, schema);
return (T) serializeObject.target;
} catch (Exception e) {
log.error("not found schema obj");
throw new IllegalStateException(e.getMessage(), e);
}
}

public static <T> byte[] serializeList(List<T> objList) {
if (CollectionUtils.isEmpty(objList)) {
log.error("objList is empty");
throw new RuntimeException("Failed to serializer");
}
@SuppressWarnings("unchecked") Schema<T> schema =
(Schema<T>) RuntimeSchema.getSchema(objList.get(0).getClass());
LinkedBuffer buffer = LinkedBuffer.allocate(1024 * 1024);
byte[] bytes = null;
ByteArrayOutputStream outputStream= null;
try {
outputStream= new ByteArrayOutputStream();
ProtostuffIOUtil.writeListTo(outputStream, objList, schema, buffer);
bytes = outputStream.toByteArray();
} catch (Exception e) {
log.error("Failed to serializer, objList={}", objList);
throw new RuntimeException("Failed to serializer");
} finally {
buffer.clear();
outputStream.close();
}
return bytes;
}
}

serialize時候,使用這個ProtobufIOUtil.toByteArray(T message, Schema schema, LinkedBuffer buffer)壓到不同資料型別,預設是byte[]
想變成字串在外面包一層new String(ProtobufIOUtil.toByteArray())就行。
上面的反序列化陣列就不寫了。

netty的解碼類

LengthFieldBasedFrameDecoder繼承自MessageToByteDecoder,也是使用netty對Message進行自行處理分流和處理分包。

public class MessageDecoder extends LengthFieldBasedFrameDecoder{

public MessageDecoder(int maxLength, int offset, int lengthFieldLength) {
super(maxLength, offset, lengthFieldLength);
}

@Override
public Object decode(ChannelHandlerContext ctx, ByteBuf input) throws Exception {
try {
//要使用readBytes防止會讀取不到,需要先給他申請可讀部分的空間,這塊是用隱式指標進行位移。
byte[] dstBytes = new byte[input.readableBytes()];
//讀取快取區接收到剩餘部分二進位制
input.readBytes(dstBytes,0,input.readableBytes());
//接收到Message物件
return SerializeUtil.deserialize(dstBytes, Message.class);
} catch (Exception e) {
log.error("exception when decoding: {}",e);
return null;
}
}
}

然後把編碼器和解碼器新增到nettyOptions管道容器的地方

@Component
public class ChannelInitializer extends ChannelInitializer<SocketChannel> {

//自定義Handler
@Autowired
ChannelHandler channelHandler;

@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline channel= socketChannel.pipeline();
channel.addLast("idleStateHandler",
new IdleStateHandler(5, 0, 0, TimeUnit.MINUTES));
//字串編解碼器
channel.addLast("encoder", new NettyMessageEncoder());
channel.addLast("decoder",new MessageDecoder(1024 * 1024, 4, 4));
pipeline.addLast("channelHandler", ChannelHandler);
}
}

施工中

相關文章