jvisualvm
分析cpu,執行緒,垃圾回收情況等;運用火焰圖async-profiler
分析系統效能,找出程式中佔用CPU資源時間最長的程式碼塊。聊到序列化與反序列化,先看看這個這個是什麼或者是幹嘛的
定義:序列化是指把
物件
轉換為位元組序列
的過程;反序列化
是指把位元組序列
恢復為物件
的過程;
一般都會知道有兩個目的:物件持久化
和網路傳輸
。
總結:序列化的目的是將物件變成位元組序列,這樣一來方便持久化儲存到磁碟,避免程式執行結束後物件就從記憶體裡消失,另外位元組序列也更便於網路運輸和傳播
這裡並不是說哪個是最好,只能說是各有千秋,所以面對不同的背景採用不同的技術方案。
在同等情況下,編碼後的位元組陣列越大,儲存佔空間,儲存硬體成本高,網路傳輸時也佔頻寬,導致系統的吞吐量降低。
這個要結合當時的專案背景了。當時的專案痛點是:
所以,存在MessagePack也有不好的地方,如果是針對業務變化比較多,那就不適合,需要比較不同的版本,然後選擇不同版本去解析。
我在京東內部文章有看到,參照的MessagePack反序列化出現了一些線上的問題,原因是:新增了一些欄位導致反序列化失敗。
這裡對比了JDK(不支援跨語言),JSON,Protobuf,MessagePack幾種序列化的方式。
當然,還有其他的XML、Hessian、Kryo(不支援跨語言)、FST(不支援跨語言)Thrift等。
JDK序列化是Java預設自帶的序列化方式。在Java中,一個物件要想實現序列化,實現Serializable
介面或者Externalizable
介面即可。其中Externalizable
介面繼承自Serializable
介面。
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name = null;
private Integer age = null;
private SerializeDemo01.Sex sex;
//....
}
serialVersionUID
版本控制的作用
serialVersionUID
也會被寫入二進位制序列serialVersionUID
是否和當前類的serialVersionUID
一致。不一致則會丟擲InvalidClassException
異常(序列化之後給類增加欄位再反序列化的時候就會報錯)serialVersionUID必須保證實體類在序列化前後嚴格一致,否則將會導致無法反序列化。
JDK預設的序列化機制。需要實現java.io.Serializable介面
JDK預設的序列化機制很差。所以我們通常不會選擇Java預設序列化這種
json格式也是常見的一種,但是在json在解析的時候非常耗時,而且json結構非常佔記憶體。JSON不適合存數位,特別是DOUBLE
這裡基於Fastjson ,載入maven依賴
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
序列化物件
利用JSON.toJSONString方法序列化物件:
UserVO user = ...;String text = JSON.toJSONString(user);
序列化陣列
利用JSON.toJSONString方法序列化陣列:
UserVO[] users = ...;String text = JSON.toJSONString(users);
序列化集合
利用JSON.toJSONString方法序列化集合(繼承至Collection,比如List、Set等集合):
List<UserVO> userList = ...;String text = JSON.toJSONString(userList);
序列化對映
利用JSON.toJSONString方法序列化對映:
Map<Long, UserVO> userMap = ...;String text = JSON.toJSONString(userMap, SerializerFeature.MapSortField);
其中,為了保證每次序列化的對映字串一致,需要指定序列化引數MapSortField進行排序。
序列化模板物件
利用JSON.toJSONString方法序列化模板物件:
Result<UserVO> result = ...;String text = JSON.toJSONString(result);
package com.nateshao.source.code.serializable.json_Serializable.serializable;
import com.alibaba.fastjson.annotation.JSONField;
/**
* @date Created by 邵桐傑 on 2023/2/25 17:11
* @微信公眾號 千羽的程式設計時光
* @部落格 https://nateshao.gitlab.io
* @GitHub https://github.com/nateshao
* Description:
*/
public class User {
/**
* @JSONField 作用:自定義物件屬性所對應的 JSON 鍵名
* @JSONField 的作用物件:
* 1. Field
* 2. Setter 和 Getter 方法
* 注意:
* 1. 若屬性是私有的,必須要有 set 方法,否則反序列化會失敗。
* 2. 若沒有 @JSONField 註解,則直接使用屬性名。
*/
@JSONField(name = "NAME")
private String name;
@JSONField(name = "AGE")
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
package com.nateshao.source.code.serializable.json_Serializable.serializable;
import com.alibaba.fastjson.JSON;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @date Created by 邵桐傑 on 2023/2/25 17:12
* @微信公眾號 千羽的程式設計時光
* @部落格 https://nateshao.gitlab.io
* @GitHub https://github.com/nateshao
* Description:
*/
public class ObjectTest {
private static List<User> userList = new ArrayList<User>();
@BeforeAll
public static void setUp() {
userList.add(new User("千羽", 18));
userList.add(new User("千尋", 19));
}
@DisplayName("序列化物件")
@Test
public void testObjectToJson() {
String userJson = JSON.toJSONString(userList.get(0));
System.out.println(userJson); // {"AGE":18,"NAME":"千羽"}
}
@DisplayName("序列化集合")
@Test
public void testListToJson() {
String userListJson = JSON.toJSONString(userList);
System.out.println(userListJson); // [{"AGE":18,"NAME":"千羽"},{"AGE":19,"NAME":"千尋"}]
}
@DisplayName("序列化陣列")
@Test
public void testArrayToJson() {
User[] userArray = new User[5];
userArray[0] = new User("zhangsan", 20);
userArray[1] = new User("lisi", 21);
String userArrayJson = JSON.toJSONString(userArray);
System.out.println(userArrayJson); // [{"AGE":20,"NAME":"zhangsan"},{"AGE":21,"NAME":"lisi"},null,null,null]
}
@DisplayName("序列化對映")
@Test
public void testMapToJson() {
Map<Integer, User> userMap = new HashMap<Integer, User>();
userMap.put(1, new User("xiaotie", 10));
userMap.put(2, new User("xiaoliu", 11));
String userMapJson = JSON.toJSONString(userMap);
System.out.println(userMapJson); // {1:{"AGE":10,"NAME":"xiaotie"},2:{"AGE":11,"NAME":"xiaoliu"}}
}
}
package com.nateshao.source.code.serializable.json_Serializable.un_serializable;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @date Created by 邵桐傑 on 2023/2/25 17:16
* @微信公眾號 千羽的程式設計時光
* @部落格 https://nateshao.gitlab.io
* @GitHub https://github.com/nateshao
* Description:
*/
public class UN_ObjectTest {
@DisplayName("反序列化物件")
@Test
public void testJsonToObject() {
String text = "{"age":18,"name":"千羽"}";
User user = JSON.parseObject(text, User.class);
System.out.println(user); // User{name='千羽', age=18}
}
@DisplayName("反序列化陣列")
@Test
public void testJsonToArray() {
String text = "[{"age":18,"name":"千羽"}, {"age":19,"name":"千尋"}]";
User[] users = JSON.parseObject(text, User[].class);
System.out.println(Arrays.toString(users)); // [User{name='千羽', age=18}, User{name='千尋', age=19}]
}
@DisplayName("反序列化集合")
@Test
public void testJsonToCollection() {
String text = "[{"age":18,"name":"千羽"}, {"age":19,"name":"千尋"}]";
// List 集合
List<User> userList = JSON.parseArray(text, User.class);
System.out.println(Arrays.toString(userList.toArray())); // [User{name='千羽', age=18}, User{name='千尋', age=19}]
// Set 集合
Set<User> userSet = JSON.parseObject(text, new TypeReference<Set<User>>() {
});
System.out.println(Arrays.toString(userSet.toArray())); // [User{name='千尋', age=19}, User{name='千羽', age=18}]
}
@DisplayName("反序列化對映")
@Test
public void testJsonToMap() {
String text = "{1:{"age":18,"name":"千羽"}, 2:{"age":19,"name":"千尋"}}";
Map<Integer, User> userList = JSON.parseObject(text, new TypeReference<Map<Integer, User>>() {
});
for (Integer i : userList.keySet()) {
System.out.println(userList.get(i));
}
/*
User{name='千羽', age=18}
User{name='千尋', age=19}
*/
}
}
json序列化總結
好處
缺點
Protobuf是谷歌推出的,是一種語言無關、平臺無關、可延伸的序列化結構資料的方法,它可用於通訊協定、資料儲存等。序列化後體積小,一般用於對傳輸效能有較高要求的系統。前期需要額外設定環境變數,學習他的語法也是需要時間。
但是要使用Protobuf會相對來說麻煩一些,因為他有自己的語法,有自己的編譯器,如果需要用到的話必須要去投入成本在這個技術的學習中。
Protobuf有個缺點就是傳輸的每一個類的結構都要生成相對應的proto檔案,如果某個類發生修改,還要重新生成該類對應的proto檔案。另外,Protobuf物件冗餘,欄位很多,生成的類較大,佔用空間。維護成本較高。
// 宣告語法,不顯示宣告預設是2.0的語法。
syntax = "proto3";
// 指定模板類的包路徑
option java_package = "com.test.basic.java.serialize.proto.dto";
// 指定模板類的名稱,名稱必須是有實際業務意義的
option java_outer_classname = "UserProto";
// 定義使用者物件
message User {
// 名字
string name = 1;
// 年齡
int32 age = 2;
}
// 宣告語法,不顯示宣告預設是2.0的語法。
syntax = "proto3";
// 指定模板類的包路徑
option java_package = "com\nateshao\source\code\serializable\protobuf_Serializable\User.proto";
// 指定模板類的名稱,名稱必須是有實際業務意義的
option java_outer_classname = "UserProto";
// 定義使用者物件
message User {
// 名字
string name = 1;
// 年齡
int32 age = 2;
}
不好的地方:
通過設定msgpack的maven依賴
<dependency>
<groupId>org.msgpack</groupId>
<artifactId>msgpack</artifactId>
<version>0.6.12</version>
</dependency>
封裝MsgPackTemplate抽象類,對原有msgpack進行二次加工,比如int,Short,Byte,BigDecimal進行read和write處理
MsgPackTemplate.java
package com.nateshao.source.code.serializable.msgpack_Serializable;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import org.msgpack.packer.Packer;
import org.msgpack.template.CharacterTemplate;
import org.msgpack.template.Template;
import org.msgpack.unpacker.Unpacker;
/**
* @date Created by 邵桐傑 on 2023/2/25 23:11
* @微信公眾號 千羽的程式設計時光
* @部落格 https://nateshao.gitlab.io
* @GitHub https://github.com/nateshao
* Description:
*/
public abstract class MsgPackTemplate <T> implements Template<T> {
public void write(Packer pk, T v) throws IOException {
write(pk, v, false);
}
public T read(Unpacker u, T to) throws IOException {
return read(u, to, false);
}
public BigDecimal readBigDecimal(Unpacker u) throws IOException {
if (u.trySkipNil()) {
return null;
}
String temp = u.readString();
if (temp != null) {
return new BigDecimal(temp);
}
return null;
}
public Date readDate(Unpacker u) throws IOException {
if (u.trySkipNil()) {
return null;
}
long temp = u.readLong();
if (temp > 0) {
return new Date(temp);
}
return null;
}
public String readString(Unpacker u) throws IOException {
if (u.trySkipNil()) {
return null;
}
return u.readString();
}
public Integer readInteger(Unpacker u) throws IOException {
if (u.trySkipNil()) {
return null;
}
return u.readInt();
}
public Byte readByte(Unpacker u) throws IOException {
if (u.trySkipNil()) {
return null;
}
return u.readByte();
}
public Short readShort(Unpacker u) throws IOException {
if (u.trySkipNil()) {
return null;
}
return u.readShort();
}
public Float readFloat(Unpacker u) throws IOException {
if (u.trySkipNil()) {
return null;
}
return u.readFloat();
}
public Double readDouble(Unpacker u) throws IOException {
if (u.trySkipNil()) {
return null;
}
return u.readDouble();
}
public Character readCharacter(Unpacker u) throws IOException {
if (u.trySkipNil()) {
return null;
}
return u.read(CharacterTemplate.getInstance());
}
public Long readLong(Unpacker u) throws IOException {
if (u.trySkipNil()) {
return null;
}
return u.readLong();
}
public Boolean readBoolean(Unpacker u) throws IOException {
if (u.trySkipNil()) {
return null;
}
return u.readBoolean();
}
public void writeBigDecimal(Packer pk, BigDecimal v) throws IOException {
if (v == null) {
pk.writeNil();
} else {
pk.write(v.toString());
}
}
public void writeDate(Packer pk, Date v) throws IOException {
if (v == null) {
pk.writeNil();
} else {
pk.write(v.getTime());
}
}
public void writeString(Packer pk, String v) throws IOException {
if (v == null) {
pk.writeNil();
} else {
pk.write(v);
}
}
public void writeInt(Packer pk, int v) throws IOException {
pk.write(v);
}
public void writeInteger(Packer pk, Integer v) throws IOException {
if (v == null) {
pk.writeNil();
} else {
pk.write(v.intValue());
}
}
public void writeByte(Packer pk, Byte v) throws IOException {
if (v == null) {
pk.writeNil();
} else {
pk.write(v.byteValue());
}
}
public void writeShort(Packer pk, Short v) throws IOException {
if (v == null) {
pk.writeNil();
} else {
pk.write(v.shortValue());
}
}
public void writeFloat(Packer pk, Float v) throws IOException {
if (v == null) {
pk.writeNil();
} else {
pk.write(v.floatValue());
}
}
public void writeDouble(Packer pk, Double v) throws IOException {
if (v == null) {
pk.writeNil();
} else {
pk.write(v.doubleValue());
}
}
public void writeCharacter(Packer pk, Character v) throws IOException {
if (v == null) {
pk.writeNil();
} else {
pk.write(v.charValue());
}
}
public void writeLong(Packer pk, Long v) throws IOException {
if (v == null) {
pk.writeNil();
} else {
pk.write(v.longValue());
}
}
public void writeLong(Packer pk, long v) throws IOException {
pk.write(v);
}
public void writeBoolean(Packer pk, Boolean v) throws IOException {
if (v == null) {
pk.writeNil();
} else {
pk.write(v.booleanValue());
}
}
}
然後針對實體類進行讀和寫的序列化與反序列化操作。
User.java
package com.nateshao.source.code.serializable.msgpack_Serializable;
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
private static final long serialVersionUID = 4719921525393585541L;
protected String id;
private String userID;
private String type;
private BigDecimal rate;
private Date createTime;
private Date UpdateTime;
}
UserTemplate.java
package com.nateshao.source.code.serializable.msgpack_Serializable;
import org.msgpack.packer.Packer;
import org.msgpack.unpacker.Unpacker;
import java.io.IOException;
/**
* protected String id;
* private String userID;
* private String type;
* private BigDecimal rate;
* private Date createTime;
* private Date UpdateTime;
*/
public class UserTemplate extends MsgPackTemplate<User> {
public void write(Packer packer, User user, boolean b) throws IOException {
if (user == null) {
packer.write(user);
return;
}
// 欄位保持一致
writeString(packer, user.id);
writeString(packer, user.getUserID());
writeString(packer, user.getType());
writeBigDecimal(packer, user.getRate());
writeDate(packer, user.getCreateTime());
writeDate(packer, user.getUpdateTime());
}
public User read(Unpacker unpacker, User user, boolean req) throws IOException {
if (!req && unpacker.trySkipNil()) {
return null;
}
if (unpacker == null) return new User();
user.setId(readString(unpacker));
user.setUserID(readString(unpacker));
user.setType(readString(unpacker));
user.setRate(readBigDecimal(unpacker));
user.setCreateTime(readDate(unpacker));
user.setUpdateTime(readDate(unpacker));
return user;
}
}
參考文獻:
作者:京東零售 邵桐傑
來源:京東雲開發者社群