《從零開始搭建遊戲伺服器》 序列化工具(最優版Protostuff)
前言:
之前使用protobuf工具來解析表格資料和定製網路協議,但是為了網路安全和壓縮資料大小,有時候需要對資料進行序列化,這就需要設計一個序列化工具類來完成序列化和反序列化的操作。
框架的對比:
Java中幾個常用的序列化框架對比,包括:kryo
、Hessian
、Protostuff
、Protostuff-Runtime
和java.io
:
框架 | 優點 | 缺點 |
---|---|---|
kryo | 速度快,序列化後體積小 | 跨語言支援比較複雜 |
Hessian | 預設支援跨語言 | 速度比較慢 |
java.io | JDK自帶功能,使用方便,可序列化所有類 | 速度慢,佔空間大 |
Protostuff | 速度快,基於protobuf | 需要靜態編譯 |
Protostuff-Runtime | 無需靜態編譯,但序列化之前需要預先傳入Schema |
不支援無預設建構函式的類,反序列化時需要使用者自己初始化序列化後的物件,而此工具只負責對初始化後的物件進行賦值 |
1.詳細分析:
protobuf
的一個缺點是需要資料結構的預編譯過程,首先要編寫.proto
格式的配置檔案,再通過protobuf
提供的工具生成各種語言響應的程式碼。由於java具有反射和動態程式碼生成的能力,這個預編譯過程不是必須的,可以在程式碼執行時來實現。有個 protostuff外掛 已經實現了這個功能。protostuff
基於Google protobuf,但是提供了更多的功能和更簡易的用法。其中,protostuff-runtime
實現了無需預編譯對java bean進行protobuf序列化/反序列化的能力。protostuff-runtime
的侷限是序列化前需預先傳入schema
,反序列化不負責物件的建立只負責複製,因而必須提供預設建構函式。此外,protostuff
還可以按照protobuf的配置序列化成json/yaml/xml等格式。
2.坑點解決:
沒經過修改過的 protostuff
,效能方法還是有些缺陷,這裡我在一篇關於 輕量級分散式 RPC 框架 部落格中找到了據說是當前效能最優的 優化版Protostuff 的使用案例。
在原生的 Protostuff
中通過反射例項化java類的時候,是通過使用 Class.newInstance()
方法來實現的,而前提就是這個類java類必須提供預設建構函式。
假如希望在java類不提供預設建構函式的時候也能實現反射例項化,可以選擇使用 objenesis
來例項化java類,使用方式可以參考 objenesis官網。
3.框架選擇:
綜合上述的分析,最終我還是選擇了 Protostuff
框架來完成Protobuf資料的序列化,關於不支援無預設建構函式類序列化的缺陷接下來通過使用 objenesis
也會得到解決。
自定義序列化工具類
這裡我們建立此工具類,取名為 SerializationUtil
,使用Protostuff
來序列化和反序列化Protobuf資料:
1.庫引入:
首先要在pom.xml裡新增com.dyuproject.protostuff
和objenesis
的jar包:
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.0.8</version>
</dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.0.8</version>
</dependency>
<!-- Objenesis -->
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>2.1</version>
</dependency>
2.工具類編寫:
主要包含兩個核心的函式:序列化函式 Serializer
和反序列化函式 Deserializer
:
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;
import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
public class SerializationUtil {
private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>();
private static Objenesis objenesis = new ObjenesisStd(true);
private SerializationUtil() {
}
@SuppressWarnings("unchecked")
private static <T> Schema<T> getSchema(Class<T> cls) {
Schema<T> schema = (Schema<T>) cachedSchema.get(cls);
if (schema == null) {
schema = RuntimeSchema.createFrom(cls);
if (schema != null) {
cachedSchema.put(cls, schema);
}
}
return schema;
}
@SuppressWarnings("unchecked")
public static <T> byte[] serialize(T obj) {
Class<T> cls = (Class<T>) obj.getClass();
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
try {
Schema<T> schema = getSchema(cls);
return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
buffer.clear();
}
}
public static <T> T deserialize(byte[] data, Class<T> cls) {
try {
T message = (T) objenesis.newInstance(cls);
Schema<T> schema = getSchema(cls);
ProtostuffIOUtil.mergeFrom(data, message, schema);
return message;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
上面是引入 objenesis
之後的版本,優點就是支援沒有提供預設建構函式的java類的例項化,可以看到這裡通過 Class<T> cls = (Class<T>) obj.getClass();
來例項化java類的。ConcurrentHashMap
是適用於高併發的Map資料結構。
3.工具類呼叫:
隨便定義一個Protobuf的協議類,假設為User
,下面就是具體的序列化工具使用操作:
序列化:
byte[] data = SerializationUtil.serialize(user,User.class);
反序列化:
User user2 = SerializationUtil.deserialize(data,User.class);
參考資料:
相關文章
- 基於protostuff的序列化工具類開發
- VUE從零開始環境搭建Vue
- 從零開始搭建webpack應用Web
- 從零開始搭建腳手架
- 從零開始實現放置遊戲(一):整體框架搭建遊戲框架
- 從零開始的 Jupyter 雲伺服器完全搭建指南伺服器
- 在Ubuntu機器上從零開始搭建SVN伺服器Ubuntu伺服器
- 樹莓派從零開始搭建Samba檔案伺服器樹莓派Samba伺服器
- M1版Mac從零開始搭建Golang開發環境MacGolang開發環境
- 教你從零開始搭建一款前端腳手架工具前端
- 從零開始搭建部落格系列
- 從零開始React伺服器渲染React伺服器
- 《從零開始搭建遊戲伺服器》 網路資料壓縮——Zlib演算法遊戲伺服器演算法
- 從零開始搭建本地 Docker 開發環境Docker開發環境
- 從零開始學mitmproxy抓包工具MIT
- 從零開始搭建React應用(一)——基礎搭建React
- java 從零開始手寫 RPC (04) -序列化JavaRPC
- 從零開始實現放置遊戲(三):後臺管理系統搭建遊戲
- 從零開始搭建一個 hexo 部落格。Hexo
- 從零開始搭建你的nvim ideIDE
- 從零開始搭建React全家桶環境React
- 從零開始搭建一個vue專案Vue
- VuePress從零開始搭建自己的部落格Vue
- 從零開始搭建vue.js專案Vue.js
- 從零開始搭建一個mock服務Mock
- 從零開始實現放置遊戲(一)遊戲
- 從零開始搭建webpack+react開發環境WebReact開發環境
- 從零開始做一個SLG遊戲(四):UI系統之主介面搭建遊戲UI
- 從零開始搭建屬於自己的網站網站
- 從零開始搭建一個舒適的ubuntuUbuntu
- 從零開始機器學習機器學習
- 從零開始 OpenCVOpenCV
- 《MySQL 8從零開始學(影片教學版)》MySql
- 從零開始一個微信小程式版知乎微信小程式
- vuePress從零開始搭建自己專屬的文件集合Vue
- flutter之從零開始搭建(二)之 Navigator路由Flutter路由
- 從零開始使用 webpack5 搭建 react 專案WebReact
- 從零開始搭建 gRPC 服務 – Golang 篇(一)RPCGolang