高性能的序列化與反序列化:kryo的簡單使用

  • 2019 年 11 月 1 日
  • 筆記

版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。

本文鏈接:https://blog.csdn.net/eguid_1/article/details/79316403

前言:kryo是個高效的java序列化/反序列化庫,目前Twitter、yahoo、Apache、strom等等在使用該技術,比如Apache的spark、hive等大數據領域用的較多。

為什麼使用kryo而不是其他?

因為性能足夠好。比kyro更高效的序列化庫就只有google的protobuf了(而且兩者性能很接近),protobuf有個缺點就是要傳輸的每一個類的結構都要生成對應的proto文件(也可以都放在同一個proto文件中,如果考慮到擴展性的話,不建議放在一個proto文件中),如果某個類發生修改,還得重新生成該類對應的proto文件;另外考慮到項目中用的全部是java技術棧,不存在不同程式語言間的兼容性問題,因此最終採用了kryo作為序列化庫。

使用場景:(數據交換或數據持久化)比如使用kryo把對象序列化成位元組數組發送給消息隊列或者放到redis等nosql中等等應用場景。

注意:由於kryo不是執行緒安全的,針對多執行緒情況下的使用,要對kryo進行一個簡單的封裝設計,從而可以多執行緒安全的使用序列化和反序列化

序列化和反序列化介面設計

/**   * 序列化工具(程式調用該介面來實現obj<->byte[]之間的序列化/反序列化)   * @author eguid   *   */  public interface Serializer{    	/**  	 * 序列化  	 * @param t  	 * @param bytes  	 */  	public void serialize(Object t,byte[] bytes);    	/**  	 * 序列化  	 * @param obj  	 * @param bytes  	 * @param offset  	 * @param count  	 */  	public void serialize(Object obj, byte[] bytes, int offset, int count);    	/**  	 * 反序列化  	 * @param bytes -位元組數組  	 * @return T<T>  	 */  	public <T>T deserialize(byte[] bytes);      	/**  	 * 反序列化  	 * @param bytes  	 * @param offset  	 * @param count  	 * @return  	 */  	public <T>T deserialize(byte[] bytes, int offset, int count);    }

使用kryo實現上面的介面

/**   * 基於kyro的序列化/反序列化工具   *   * @author eguid   *   */  public class kryoSerializer implements Serializer {    	// 由於kryo不是執行緒安全的,所以每個執行緒都使用獨立的kryo  	final ThreadLocal<Kryo> kryoLocal = new ThreadLocal<Kryo>() {  		@Override  		protected Kryo initialValue() {  			Kryo kryo = new Kryo();  			kryo.register(ct, new BeanSerializer<>(kryo, ct));  			return kryo;  		}  	};  	final ThreadLocal<Output> outputLocal = new ThreadLocal<Output>();  	final ThreadLocal<Input> inputLocal = new ThreadLocal<Input>();  	private Class<?> ct = null;    	public kryoSerializer(Class<?> ct) {  		this.ct = ct;  	}    	public Class<?> getCt() {  		return ct;  	}    	public void setCt(Class<?> ct) {  		this.ct = ct;  	}    	@Override  	public void serialize(Object obj, byte[] bytes) {  		Kryo kryo = getKryo();  		Output output = getOutput(bytes);  		kryo.writeObjectOrNull(output, obj, obj.getClass());  		output.flush();  	}    	@Override  	public void serialize(Object obj, byte[] bytes, int offset, int count) {  		Kryo kryo = getKryo();  		Output output = getOutput(bytes, offset, count);  		kryo.writeObjectOrNull(output, obj, obj.getClass());  		output.flush();  	}    	/**  	 * 獲取kryo  	 *  	 * @param t  	 * @return  	 */  	private Kryo getKryo() {  		return kryoLocal.get();  	}    	/**  	 * 獲取Output並設置初始數組  	 *  	 * @param bytes  	 * @return  	 */  	private Output getOutput(byte[] bytes) {  		Output output = null;  		if ((output = outputLocal.get()) == null) {  			output = new Output();  			outputLocal.set(output);  		}  		if (bytes != null) {  			output.setBuffer(bytes);  		}  		return output;  	}    	/**  	 * 獲取Output  	 *  	 * @param bytes  	 * @return  	 */  	private Output getOutput(byte[] bytes, int offset, int count) {  		Output output = null;  		if ((output = outputLocal.get()) == null) {  			output = new Output();  			outputLocal.set(output);  		}  		if (bytes != null) {  			output.writeBytes(bytes, offset, count);  		}  		return output;  	}    	/**  	 * 獲取Input  	 *  	 * @param bytes  	 * @param offset  	 * @param count  	 * @return  	 */  	private Input getInput(byte[] bytes, int offset, int count) {  		Input input = null;  		if ((input = inputLocal.get()) == null) {  			input = new Input();  			inputLocal.set(input);  		}  		if (bytes != null) {  			input.setBuffer(bytes, offset, count);  		}  		return input;  	}    	@SuppressWarnings("unchecked")  	@Override  	public <T> T deserialize(byte[] bytes, int offset, int count) {  		Kryo kryo = getKryo();  		Input input = getInput(bytes, offset, count);  		return (T) kryo.readObjectOrNull(input, ct);  	}    	@Override  	public <T> T deserialize(byte[] bytes) {  		return deserialize(bytes, 0, bytes.length);  	}

測試一下kryo的序列化和反序列化

為什麼使用納秒,而不用毫秒?與java原生的序列化反序列化要耗時幾毫秒不同,kryo序列化和反序列化太快了,單個對象的序列化反序列化速度都在0.0x毫秒左右(如果電腦性能更好的話,會更快)

Serializer ser = new kryoSerializer(Msg.class);  		for (int i = 0; i < 10; i++) {    			Msg msg = new Msg();    			msg.setVersion_flag(new byte[] { 1, 2, 3 });  			msg.setCrc_code((short) 1);  			msg.setMsg_body(new byte[] { 123, 123, 123, 43, 42, 1, 12, 45, 57, 98 });  			byte[] bytes = new byte[300];  			long start = System.nanoTime();  			ser.serialize(msg, bytes);  			System.err.println("序列化耗時:" + (System.nanoTime() - start));  			System.out.println(msg);  			System.out.println(Arrays.toString(bytes));    			Msg newmsg = null;  			start = System.nanoTime();  			newmsg = ser.deserialize(bytes);  			System.err.println("反序列化耗時:" + (System.nanoTime() - start));  			System.out.println(newmsg);  		}

—-end—-