溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

常見(jiàn)Serialize技術(shù)有哪些

發(fā)布時(shí)間:2021-12-22 13:47:43 來(lái)源:億速云 閱讀:300 作者:iii 欄目:云計(jì)算

本篇內(nèi)容主要講解“常見(jiàn)Serialize技術(shù)有哪些”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“常見(jiàn)Serialize技術(shù)有哪些”吧!

【一、常見(jiàn)的在API及消息通信調(diào)的用中Serialize方案】:

方案1、基于Java原生的ObjectOutputStream.write()和ObjectInputStream.read()來(lái)進(jìn)行對(duì)象序列化和反序列化。

方案2、基于JSON進(jìn)行序列化和反序列化。

方案3、基于XML進(jìn)行序列化和反序列化。

【方案1淺析,ObjectXXXStream】:

優(yōu)點(diǎn):

(1)、由Java自帶API序列化,簡(jiǎn)單、方便、無(wú)第三方依賴。

(2)、不用擔(dān)心其中的數(shù)據(jù)解析會(huì)丟失精度、丟失字段、Object的反序列化類型不確定等問(wèn)題。

缺點(diǎn):

(1)、雙方調(diào)試麻煩,發(fā)送方和接收方最好是同版本的對(duì)象描述,否則會(huì)有奇怪的問(wèn)題,調(diào)試周期相對(duì)長(zhǎng),跨團(tuán)隊(duì)合作升級(jí)問(wèn)題很多。

(2)、傳遞的對(duì)象中包含了元數(shù)據(jù)信息,占用空間較大。

【方案2淺析,JSON序列化】:

優(yōu)點(diǎn):

(1)、簡(jiǎn)單、方便,無(wú)需關(guān)注要序列化的對(duì)象格式。

(2)、開(kāi)源界有較多的組件可以支持,例如FastJSON性能非常好。

(3)、在現(xiàn)在很多RPC的框架中,基本都支持這樣的方案。

缺點(diǎn):

(1)、對(duì)象屬性中如果包含Object類型,在反序列化的時(shí)候如果業(yè)務(wù)也本身也不明確數(shù)據(jù)類型,處理起來(lái)會(huì)很麻煩。

(2)、由于文本類型,所以一定會(huì)占用較大的數(shù)據(jù)空間,例如下圖。

(3)、比較比較依賴于JSON的解析包的兼容性和性能,在JSON的一些細(xì)節(jié)處理上(例如一些非標(biāo)的JSON),各自處理方式可能不一樣。

(4)、序列化無(wú)論任何數(shù)據(jù)類型先要轉(zhuǎn)換為String,轉(zhuǎn)成byte[],會(huì)增加內(nèi)存拷貝的次數(shù)。

(5)、反序列化的時(shí)候,必須將整個(gè)JSON反序列化成對(duì)象后才能進(jìn)行讀取,大家應(yīng)該知道,Java對(duì)象尤其是層次嵌套較多的對(duì)象,占用的內(nèi)存空間將會(huì)遠(yuǎn)遠(yuǎn)大于數(shù)據(jù)本身的空間。

數(shù)據(jù)放大的極端案例1:

傳遞數(shù)據(jù)描述信息為:

class PP {

    long userId                       = 102333320132133L;

    int    passportNumber      = 123456;

}

此時(shí)傳遞JSON的格式為:

{

     "userId":102333320132133,

     "passportNumber":123456

}

我們要傳遞的數(shù)據(jù)是1個(gè)long、1個(gè)int,也就是12個(gè)字節(jié)的數(shù)據(jù),這個(gè)JSON的字符串長(zhǎng)度將是實(shí)際的字節(jié)數(shù)(不包含回車、空格,這里只是為了可讀性,同時(shí)注意,這里的long在JSON里面是字符串了),這個(gè)字符串有:51個(gè)字節(jié),也就是數(shù)據(jù)放到了4.25倍左右。

數(shù)據(jù)放大極端案例2:

當(dāng)你的對(duì)象內(nèi)部有數(shù)據(jù)是byte[]類型,JSON是文本格式的數(shù)據(jù),是無(wú)法存儲(chǔ)byte[]的,那么要序列化這樣的數(shù)據(jù),只有一個(gè)辦法就是把byte轉(zhuǎn)成字符,通常的做法有兩種:

(1)使用BASE64編碼,目前JSON中比較常用的做法。

(2)按照字節(jié)進(jìn)行16進(jìn)制字符編碼,例如字符串:“FF”代表的是0xFF這個(gè)字節(jié)。

不論上面兩種做法的那一種,1個(gè)字節(jié)都會(huì)變成2個(gè)字符來(lái)傳遞,也就是byte[]數(shù)據(jù)會(huì)被放大2倍以上。為什么不用ISO-8859-1的字符來(lái)編碼呢?因?yàn)檫@樣編碼后,在最終序列化成網(wǎng)絡(luò)byte[]數(shù)據(jù)后,對(duì)應(yīng)的byte[]雖然沒(méi)變大,但是在反序列化成文本的時(shí)候,接收方并不知道是ISO-8859-1,還會(huì)用例如GBK、UTF-8這樣比較常見(jiàn)的字符集解析成String,才能進(jìn)一步解析JSON對(duì)象,這樣這個(gè)byte[]可能在編碼的過(guò)程中被改變了,要處理這個(gè)問(wèn)題會(huì)非常麻煩。

【方案2淺析,XML序列化】:

優(yōu)點(diǎn):

(1)、使用簡(jiǎn)單、方便,無(wú)需關(guān)注要序列化的對(duì)象格式

(2)、可讀性較好,XML在業(yè)界比較通用,大家也習(xí)慣性在配置文件中看到XML的樣子

(3)、大量RPC框架都支持,通過(guò)XML可以直接形成文檔進(jìn)行傳閱

缺點(diǎn):

(1)、在序列化和反序列化的性能上一直不是太好。

(2)、也有與JSON同樣的數(shù)據(jù)類型問(wèn)題,和數(shù)據(jù)放大的問(wèn)題,同時(shí)數(shù)據(jù)放大的問(wèn)題更為嚴(yán)重,同時(shí)內(nèi)存拷貝次數(shù)也和JSON類型,不可避免。

XML數(shù)據(jù)放大說(shuō)明:

XML的數(shù)據(jù)放大通常比JSON更為嚴(yán)重,以上面的JSON案例來(lái)講,XML傳遞這份數(shù)據(jù)通常會(huì)這樣傳:

<Msg>

     <userId>102333320132133</userId>

     <passportNumber>123456<passportNumber>

<Msg>

這個(gè)消息就有80+以上的字節(jié)了,如果XML里面再搞一些Property屬性,對(duì)象再嵌套嵌套,那么這個(gè)放大的比例有可能會(huì)達(dá)到10倍都是有可能的,因此它的放大比JSON更為嚴(yán)重,這也是為什么現(xiàn)在越來(lái)越多的API更加喜歡用JSON,而不是XML的原因。

【放大的問(wèn)題是什么】:

(1)、花費(fèi)更多的時(shí)間去拼接字符串和拷貝內(nèi)存,占用更多的Java內(nèi)存,產(chǎn)生更多的碎片。

(2)、產(chǎn)生的JSON對(duì)象要轉(zhuǎn)為byte[]需要先轉(zhuǎn)成String文本再進(jìn)行byte[]編碼,因?yàn)檫@本身是文本協(xié)議,那么自然再多一次內(nèi)存全量的拷貝。

(3)、傳輸過(guò)程由于數(shù)據(jù)被放大,占用更大的網(wǎng)絡(luò)流量。

(4)、由于網(wǎng)絡(luò)的package變多了,所以TCP的ACK也會(huì)變多,自然系統(tǒng)也會(huì)更大,同等丟包率的情況下丟包數(shù)量會(huì)增加,整體傳輸時(shí)間會(huì)更長(zhǎng),如果這個(gè)數(shù)據(jù)傳送的網(wǎng)絡(luò)延遲很大且丟包率很高,我們要盡量降低大??;壓縮是一條途徑,但是壓縮會(huì)帶來(lái)巨大的CPU負(fù)載提高,在壓縮前盡量降低數(shù)據(jù)的放大是我們所期望的,然后傳送數(shù)據(jù)時(shí)根據(jù)RT和數(shù)據(jù)大小再判定是否壓縮,有必要的時(shí)候,壓縮前如果數(shù)據(jù)過(guò)大還可以進(jìn)行部分采樣數(shù)據(jù)壓縮測(cè)試壓縮率。

(5)、接收方要處理數(shù)據(jù)也會(huì)花費(fèi)更多的時(shí)間來(lái)處理。

(6)、由于是文本協(xié)議,在處理過(guò)程中會(huì)增加開(kāi)銷,例如數(shù)字轉(zhuǎn)字符串,字符串轉(zhuǎn)數(shù)字;byte[]轉(zhuǎn)字符串,字符串轉(zhuǎn)byte[]都會(huì)增加額外的內(nèi)存和計(jì)算開(kāi)銷。

不過(guò)由于在平時(shí)大量的應(yīng)用程序中,這個(gè)開(kāi)銷相對(duì)業(yè)務(wù)邏輯來(lái)講簡(jiǎn)直微不足道,所以優(yōu)化方面,這并不是我們關(guān)注的重點(diǎn),但面臨一些特定的數(shù)據(jù)處理較多的場(chǎng)景,即核心業(yè)務(wù)在數(shù)據(jù)序列化和反序列化的時(shí)候,就要考慮這個(gè)問(wèn)題了,那么下面我繼續(xù)討論問(wèn)題。

此時(shí)提出點(diǎn)問(wèn)題:

(1)、網(wǎng)絡(luò)傳遞是不是有更好的方案,如果有,為什么現(xiàn)在沒(méi)有大面積采用?

(2)、相對(duì)底層的數(shù)據(jù)通信,例如JDBC是如何做的,如果它像上面3種方案?jìng)鬟f結(jié)果集,會(huì)怎么樣?

【二、MySQL JDBC數(shù)據(jù)傳遞方案】:

在前文中提到數(shù)據(jù)在序列化過(guò)程被放大數(shù)倍的問(wèn)題,我們是否想看看一些相對(duì)底層的通信是否也是如此呢?那么我們以MySQL JDBC為例子來(lái)看看它與JDBC之間進(jìn)行通信是否也是如此。

JDBC驅(qū)動(dòng)程序根據(jù)數(shù)據(jù)庫(kù)不同有很多實(shí)現(xiàn),每一種數(shù)據(jù)庫(kù)實(shí)現(xiàn)細(xì)節(jié)上都有巨大的區(qū)別,本文以MySQL JDBC的數(shù)據(jù)解析為例(MySQL 8.0以前),給大家說(shuō)明它是如何傳遞數(shù)據(jù)的,而傳遞數(shù)據(jù)的過(guò)程中,相信大家最為關(guān)注的就是ResultSet的數(shù)據(jù)是如何傳遞的。

拋開(kāi)結(jié)果集中的MetaData等基本信息,單看數(shù)據(jù)本身:

(1)JDBC會(huì)讀取數(shù)據(jù)行的時(shí)候,首先會(huì)從緩沖區(qū)讀取一個(gè)row packege,row package就是從網(wǎng)絡(luò)package中拿到的,根據(jù)協(xié)議中傳遞過(guò)來(lái)的package的頭部判定package大小,然后從網(wǎng)絡(luò)緩沖中讀取對(duì)應(yīng)大小的內(nèi)容,下圖想表達(dá)網(wǎng)絡(luò)傳遞的package和業(yè)務(wù)數(shù)據(jù)中的package之間可能并不是完全對(duì)應(yīng)的。另外,網(wǎng)絡(luò)中的package如果都到了本地緩沖區(qū),邏輯上講它們是連續(xù)的(圖中故意分開(kāi)是讓大家了解到網(wǎng)絡(luò)中傳遞是分不同的package傳遞到本地的),JDBC從本地buffer讀取row package這個(gè)過(guò)程就是內(nèi)核package到JVM的package拷貝過(guò)程,對(duì)于我們Java來(lái)講,我們主要關(guān)注row package(JDBC中可能存在一些特殊情況讀取過(guò)來(lái)的package并不是行級(jí)別的,這種特殊情況請(qǐng)有興趣的同學(xué)自行查閱源碼)。

我們先不考慮按照bit有31個(gè)bit是0,先按照字節(jié)來(lái)看有7個(gè)0,代表字節(jié)沒(méi)有數(shù)據(jù),只有1個(gè)字節(jié)是有值的,大家可以去看一下自己的數(shù)據(jù)庫(kù)中大量的自動(dòng)增長(zhǎng)列,在id小于4194303之前,前面5個(gè)字節(jié)是浪費(fèi)掉的,在增長(zhǎng)到4個(gè)字節(jié)(2的32次方-1)之前前面4個(gè)自己都是0,浪費(fèi)掉的。另外,即使8個(gè)字節(jié)中的第一個(gè)字節(jié)開(kāi)始使用,也會(huì)有大量的數(shù)據(jù),中間字節(jié)是為:0的概率極高,就像十進(jìn)制中進(jìn)入1億,那么1億下面最多會(huì)有8個(gè)0,越高位的0約難補(bǔ)充上去。

如果真的想去嘗試,可以用這個(gè)辦法:用1個(gè)字節(jié)來(lái)做標(biāo)志,但會(huì)占用一定的計(jì)算開(kāi)銷,所以是否為了這個(gè)空間去做這個(gè)事情,由你決定,本文僅僅是技術(shù)性探討:

方法1:表達(dá)目前有幾個(gè)低位被使用的字節(jié)數(shù),由于long只有8個(gè)字節(jié),所以用3個(gè)bit就夠了,另外5個(gè)bit是浪費(fèi)掉的,也無(wú)所謂了,反序列化的時(shí)候按照高位數(shù)量補(bǔ)充0x00即可。

方法2:相對(duì)方法1,更徹底,但處理起來(lái)更復(fù)雜,用1這個(gè)字節(jié)的8個(gè)bit的0、1分別代表long的8個(gè)字節(jié)是被使用,序列化和反序列化過(guò)程根據(jù)標(biāo)志位和數(shù)據(jù)本身進(jìn)行字節(jié)補(bǔ)0x00操作,補(bǔ)充完整8個(gè)字節(jié)就是long的值了,最壞情況是9個(gè)字節(jié)代表long,最佳情況0是1個(gè)字節(jié),字節(jié)中只占用了2個(gè)字節(jié)的時(shí)候,即使數(shù)據(jù)變得相當(dāng)大,也會(huì)有大量的數(shù)據(jù)的字節(jié)存在空位的情況,在這些情況下,就通??梢杂蒙儆?個(gè)字節(jié)的情況來(lái)表達(dá),要用滿7個(gè)字節(jié)才能夠與原數(shù)字long的占用空間一樣,此時(shí)的數(shù)據(jù)已經(jīng)是比2的48次方-1更大的數(shù)據(jù)了。

【三、Google Protocol Buffer技術(shù)方案】:

這個(gè)對(duì)于很多人來(lái)講未必用過(guò),也不知道它是用來(lái)干什么的,不過(guò)我不得不說(shuō),它是目前數(shù)據(jù)序列化和反序列化的一個(gè)神器,這個(gè)東西是在谷歌內(nèi)部為了約定好自己內(nèi)部的數(shù)據(jù)通信設(shè)計(jì)出來(lái)的,大家都知道谷歌的全球網(wǎng)絡(luò)非常牛逼,那么自然在數(shù)據(jù)傳輸方面做得那是相當(dāng)極致,在這里我會(huì)講解下它的原理,就本身其使用請(qǐng)大家查閱其它人的博客,本文篇幅所限沒(méi)法step by step進(jìn)行講解。

看到這個(gè)名字,應(yīng)該知道是協(xié)議Buffer,或者是協(xié)議編碼,其目的和上文中提到的用JSON、XML用來(lái)進(jìn)行RPC調(diào)用類似,就是系統(tǒng)之間傳遞消息或調(diào)用API。但是谷歌一方面為了達(dá)到類似于XML、JSON的可讀性和跨語(yǔ)言的通用性,另一方面又希望達(dá)到較高的序列化和反序列化性能,數(shù)據(jù)放大能夠進(jìn)行控制,所以它又希望有一種比底層編碼更容易使用,而又可以使用底層編碼的方式,又具備文檔的可讀性能力。

它首先需要定義一個(gè)格式文件,如下:

syntax = "proto2";

package com.xxx.proto.buffer.test;

message TestData2 {
   optional int32 id = 2;
   optional int64 longId = 1;
   optional bool  boolValue = 3;
   optional string name = 4;
   optional bytes bytesValue = 5;
   optional int32 id2 = 6;
}

這個(gè)文件不是Java文件,也不是C文件,和語(yǔ)言無(wú)關(guān),通常把它的后綴命名為proto(文件中1、2、3數(shù)字代表序列化的順序,反序列化也會(huì)按照這個(gè)順序來(lái)做),然后本地安裝了protobuf后(不同OS安裝方式不同,在官方有下載和說(shuō)明),會(huì)產(chǎn)生一個(gè)protoc運(yùn)行文件,將其加入環(huán)境變量后,運(yùn)行命令指定一個(gè)目標(biāo)目錄:

protoc --java_out=~/temp/ TestData2.proto

此時(shí)會(huì)在指定的目錄下,產(chǎn)生package所描述的目錄,在其目錄內(nèi)部有1個(gè)Java源文件(其它語(yǔ)言的會(huì)產(chǎn)生其它語(yǔ)言),這部分代碼是谷歌幫你生成的,你自己寫(xiě)的話太費(fèi)勁,所以谷歌就幫你干了;本地的Java project里面要引入protobuf包,maven引用(版本自行選擇):

<dependency>

     <groupId>com.google.protobuf</groupId>

    <artifactId>protobuf-java</artifactId>

    <version>3.6.1</version>

</dependency>

此時(shí)生成的代碼會(huì)調(diào)用這個(gè)谷歌包里面提供的方法庫(kù)來(lái)做序列化動(dòng)作,我們的代碼只需要調(diào)用生成的這個(gè)類里面的API就可以做序列化和反序列化操作了,將這些生成的文件放在一個(gè)模塊里面發(fā)布到maven倉(cāng)庫(kù)別人就可以引用了,關(guān)于測(cè)試代碼本身,大家可以參考目前有很多博客有提供測(cè)試代碼,還是很好用的。

谷歌編碼比較神奇的是,你可以按照對(duì)象的方式定義傳輸數(shù)據(jù)的格式,可讀性極高,甚至于相對(duì)XML和JSON更適合程序員閱讀,也可以作為交流文檔,不同語(yǔ)言都通用,定義的對(duì)象還是可以嵌套的,但是它序列化出來(lái)的字節(jié)比原始數(shù)據(jù)只大一點(diǎn)點(diǎn),這尼瑪太厲害了吧。

經(jīng)過(guò)測(cè)試不同的數(shù)據(jù)類型,故意制造數(shù)據(jù)嵌套的層數(shù),進(jìn)行二進(jìn)制數(shù)組多層嵌套,發(fā)現(xiàn)其數(shù)據(jù)放大的比例非常非常小,幾乎可以等價(jià)于二進(jìn)制傳輸,于是我把序列化后的數(shù)據(jù)其二進(jìn)制進(jìn)行了輸出,發(fā)現(xiàn)其編碼方式非常接近于上面的JDBC,雖然有一些細(xì)節(jié)上的區(qū)別,但是非常接近,除此之外,它在序列化的時(shí)候有幾大特征:

(1)、如果字段為空,它不會(huì)產(chǎn)生任何字節(jié),如果整合對(duì)象的屬性都為null,產(chǎn)生的字節(jié)將是0

(2)、對(duì)int32、int64這些數(shù)據(jù)采用了變長(zhǎng)編碼,其思路和我們上面描述有一些共通之處,就是一個(gè)int64值在比較小的時(shí)候用比較少的字節(jié)就可以表達(dá)了,其內(nèi)部有一套字節(jié)的移位和異或算法來(lái)處理這個(gè)事情。

(3)、它對(duì)字符串、byte[]沒(méi)有做任何轉(zhuǎn)換,直接放入字節(jié)數(shù)組,和二進(jìn)制編碼是差不多的道理。

(4)、由于字段為空它都可以不做任何字節(jié),它的做法是有數(shù)據(jù)的地方會(huì)有一個(gè)位置編碼信息,大家可以嘗試通過(guò)調(diào)整數(shù)字順序看看生成出來(lái)的byte是否會(huì)發(fā)生改變;那么此時(shí)它就有了很強(qiáng)兼容性,也就是普通的加字段是沒(méi)問(wèn)題的,這個(gè)對(duì)于普通的二進(jìn)制編碼來(lái)講很難做到。

(5)、序列化過(guò)程沒(méi)有產(chǎn)生metadata信息,也就是它不會(huì)把對(duì)象的結(jié)構(gòu)寫(xiě)在字節(jié)里面,而是反序列化的接收方有同一個(gè)對(duì)象,就可以反解析出來(lái)了。

這與我自己寫(xiě)編碼有何區(qū)別?

(1)、自己寫(xiě)編碼有很多不確定性,寫(xiě)不好的話,數(shù)據(jù)可能放得更大,也容易出錯(cuò)。

(2)、google的工程師把內(nèi)部規(guī)范后,谷歌開(kāi)源的產(chǎn)品也大量使用這樣的通信協(xié)議,越來(lái)越多的業(yè)界中間件產(chǎn)品開(kāi)始使用該方案,就連MySQL數(shù)據(jù)庫(kù)最新版本在數(shù)據(jù)傳輸方面也會(huì)開(kāi)始兼容protobuf。

(3)、谷歌相當(dāng)于開(kāi)始在定義一個(gè)業(yè)界的新的數(shù)據(jù)傳輸方案,即有性能又降低代碼研發(fā)的難度,也有跨語(yǔ)言訪問(wèn)的能力,所以才會(huì)有越來(lái)越多的人喜歡使用這個(gè)東西。

那么它有什么缺點(diǎn)呢?還真不多,它基本兼顧了很多序列化和反序列化中你需要考慮的所有的事情,達(dá)到了一種非常良好的平衡,但是硬要挑缺陷,我們就得找場(chǎng)景才行:

(1)、protobuf需要雙方明確數(shù)據(jù)類型,且定義的文件中每一個(gè)對(duì)象要明確數(shù)據(jù)類型,對(duì)于Object類型的表達(dá)沒(méi)有方案,你自己必須提前預(yù)知這個(gè)Object到底是什么類型。

(2)、使用repeated可以表達(dá)數(shù)組,但是只能表達(dá)相同類型的數(shù)據(jù),例如上面提到的JDBC一行數(shù)據(jù)的多個(gè)列數(shù)據(jù)類型不同的時(shí)候,要用這個(gè)表達(dá),會(huì)比較麻煩;另外,默認(rèn)情況下數(shù)組只能表達(dá)1維數(shù)組,要表達(dá)二維數(shù)組,需要使用對(duì)象嵌套來(lái)間接完成。

(3)、它提供的數(shù)據(jù)類型都是基本數(shù)據(jù)類型,如果不是普通類型,要自己想辦法轉(zhuǎn)換為普通類型進(jìn)行傳輸,例如從MongoDB查處一個(gè)Docment對(duì)象,這個(gè)對(duì)象序列化是需要自己先通過(guò)別的方式轉(zhuǎn)換為byte[]或String放進(jìn)去的,而相對(duì)XML、JSON普通是提供了遞歸的功能,但是如果protobuf要提供這個(gè)功能,必然會(huì)面臨數(shù)據(jù)放大的問(wèn)題,通用和性能永遠(yuǎn)是矛盾的。

(4)、相對(duì)于自定義byte的話,序列化和反序列化是一次性完成,不能逐步完成,這樣如果傳遞數(shù)組嵌套,在反序列化的時(shí)候會(huì)產(chǎn)生大量的Java對(duì)象,另外自定義byte的話可以進(jìn)一步減少內(nèi)存拷貝,不過(guò)谷歌這個(gè)相對(duì)文本協(xié)議來(lái)講內(nèi)存拷貝已經(jīng)少很多了。

到此,相信大家對(duì)“常見(jiàn)Serialize技術(shù)有哪些”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI