go實(shí)現(xiàn)thrift的網(wǎng)絡(luò)傳輸性能及需要注意問(wèn)題示例解析
thrift簡(jiǎn)介
thrift應(yīng)該是目前支持編程語(yǔ)言種類(lèi)最多的跨語(yǔ)言 rpc服務(wù)框架, http://thrift.apache.org/
thrift實(shí)現(xiàn)了完整的網(wǎng)絡(luò)服務(wù),所以一般使用thrift時(shí),會(huì)使用到thrift的服務(wù)框架。當(dāng)然,也可以用自己已經(jīng)實(shí)現(xiàn)的網(wǎng)絡(luò)服務(wù),用io流對(duì)接thrift接口的輸入輸出流實(shí)現(xiàn)thrift的接入。
無(wú)論是用thrift的網(wǎng)絡(luò)實(shí)現(xiàn),還是自己實(shí)現(xiàn)的網(wǎng)絡(luò)服務(wù),只要對(duì)接thrift,在調(diào)用thrift接口實(shí)現(xiàn)rpc時(shí),都是走thrift的網(wǎng)絡(luò)傳輸方式。
thrift的網(wǎng)絡(luò)傳輸實(shí)現(xiàn)方式 不適合也不支持壓力較大的網(wǎng)絡(luò)傳輸需求。實(shí)際上,調(diào)用一次thrift接口,并不是只調(diào)一次網(wǎng)絡(luò)io寫(xiě)數(shù)據(jù),而是拆分為多次寫(xiě)數(shù)據(jù)傳送。
調(diào)用一個(gè)thrift 的接口發(fā)送數(shù)據(jù)時(shí),thrift會(huì)將這個(gè)操作拆分為幾個(gè)操作:
調(diào)用thrift的方法時(shí):thrift會(huì)找到這個(gè)方法所在的對(duì)象,調(diào)用write方法,
在write方法在,分別對(duì)各個(gè)參數(shù),依次調(diào)用 writeFieldBegin,writeXXX(具體參數(shù)類(lèi)型) ,WriteFieldStop 等函數(shù)
每次調(diào)用也同時(shí)調(diào)用網(wǎng)絡(luò)io寫(xiě)相應(yīng)數(shù)據(jù).
以目前最新的thrift-0.18.1實(shí)現(xiàn)為例
go的實(shí)現(xiàn)
func (p *ItnetPonMergeArgs) Write(ctx context.Context, oprot thrift.TProtocol) error {
if err := oprot.WriteStructBegin(ctx, "PonMerge_args"); err != nil {
return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) }
if p != nil {
if err := p.writeField1(ctx, oprot); err != nil { return err }
if err := p.writeField2(ctx, oprot); err != nil { return err }
}
if err := oprot.WriteFieldStop(ctx); err != nil {
return thrift.PrependError("write field stop error: ", err) }
if err := oprot.WriteStructEnd(ctx); err != nil {
return thrift.PrependError("write struct stop error: ", err) }
return nil
}
//第一個(gè)參數(shù)
func (p *ItnetPonMergeArgs) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) {
if err := oprot.WriteFieldBegin(ctx, "pblist", thrift.LIST, 1); err != nil { //WriteFieldBegin
return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:pblist: ", p), err) }
if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Pblist)); err != nil {
return thrift.PrependError("error writing list begin: ", err)
}
for _, v := range p.Pblist {
if err := v.Write(ctx, oprot); err != nil {
return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err)
}
}
if err := oprot.WriteListEnd(ctx); err != nil {
return thrift.PrependError("error writing list end: ", err)
}
if err := oprot.WriteFieldEnd(ctx); err != nil {
return thrift.PrependError(fmt.Sprintf("%T write field end error 1:pblist: ", p), err) }
return err
}
//第二個(gè)參數(shù),操作類(lèi)似第一個(gè)參數(shù)
func (p *ItnetPonMergeArgs) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) {
if err := oprot.WriteFieldBegin(ctx, "id", thrift.I64, 2); err != nil {
return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:id: ", p), err) }
if err := oprot.WriteI64(ctx, int64(p.ID)); err != nil {
return thrift.PrependError(fmt.Sprintf("%T.id (2) field write error: ", p), err) }
if err := oprot.WriteFieldEnd(ctx); err != nil {
return thrift.PrependError(fmt.Sprintf("%T write field end error 2:id: ", p), err) }
return err
}調(diào)用Pon(ItnetPonMergeArgs)方法的thrift傳輸順序
Write->
- writeField1->WriteFieldBegin-> WriteByte-> io
- -> Write16 -> io
- ->WriteBinary-> Write32 -> io
- ->Write -> io
- writeField2->WriteFieldBegin-> WriteByte -> io
- ->Write16 -> io
- ->Write64-> Write -> io
- WriteFieldStop ->io
可以看到,一次簡(jiǎn)單的方法調(diào)用,如果方法中有兩個(gè)參數(shù), 則至少有8次io流寫(xiě)數(shù)據(jù)調(diào)用。
如果參數(shù)多時(shí),或是參數(shù)中一個(gè)結(jié)構(gòu)體的變量多時(shí),則會(huì)有更多的io流寫(xiě)數(shù)據(jù)調(diào)用。
在海量的網(wǎng)絡(luò)傳輸中,這樣的傳輸方式,網(wǎng)絡(luò)io流寫(xiě)數(shù)據(jù)調(diào)用成倍增加,海量網(wǎng)絡(luò)io數(shù)據(jù)寫(xiě)入導(dǎo)致性能急劇下降。
thrift設(shè)計(jì)的傳輸層提供了zlib協(xié)議壓縮,在zlib壓縮發(fā)送的情況下,將數(shù)據(jù)進(jìn)行了整體壓縮收發(fā),zlib分為2次發(fā)送后,接收端再解壓;
以go為例子:
可以在 compress/flate 看到zlib的寫(xiě)數(shù)據(jù)最終io寫(xiě)入調(diào)用:
func (d *compressor) syncFlush() error {
if d.err != nil {
return d.err
}
d.sync = true
d.step(d)
if d.err == nil {
d.w.writeStoredHeader(0, false) //第一次調(diào)用
d.w.flush() //第二次調(diào)用
d.err = d.w.err
}
d.sync = false
return d.err
}
//兩次io數(shù)據(jù)寫(xiě)入所以,在海量調(diào)用thrift方法的情況下,zlib模式的性能要遠(yuǎn)超非zlib的情況。但是zlib壓縮會(huì)比較消耗內(nèi)存,大量使用時(shí)可能導(dǎo)致頻繁gc,也可能導(dǎo)致性能下降。當(dāng)然,即使如此,大部分情況下zlib傳輸依然比非zlib傳輸?shù)男阅芤迷S多。
其他語(yǔ)言的實(shí)現(xiàn)
比如 java:
public void write(org.apache.thrift.protocol.TProtocol oprot, SelectByIdxLimit_args struct) throws org.apache.thrift.TException {
struct.validate();
oprot.writeStructBegin(STRUCT_DESC);
if (struct.name != null) {
oprot.writeFieldBegin(NAME_FIELD_DESC); //io調(diào)用
oprot.writeString(struct.name); //io調(diào)用
oprot.writeFieldEnd();
}
if (struct.column != null) {
oprot.writeFieldBegin(COLUMN_FIELD_DESC); //io調(diào)用
oprot.writeString(struct.column); //io調(diào)用
oprot.writeFieldEnd();
}
if (struct.value != null) {
oprot.writeFieldBegin(VALUE_FIELD_DESC); //io調(diào)用
{
oprot.writeListBegin(new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRING, struct.value.size()));
for (java.nio.ByteBuffer _iter49 : struct.value)
{
oprot.writeBinary(_iter49); //io調(diào)用
}
oprot.writeListEnd();
}
oprot.writeFieldEnd();
}
oprot.writeFieldBegin(START_ID_FIELD_DESC); //io調(diào)用
oprot.writeI64(struct.startId); //io調(diào)用
oprot.writeFieldEnd();
oprot.writeFieldBegin(LIMIT_FIELD_DESC); //io調(diào)用
oprot.writeI64(struct.limit); //io調(diào)用
oprot.writeFieldEnd();
oprot.writeFieldStop(); //io調(diào)用
oprot.writeStructEnd();
}傳輸方式都是相似的
實(shí)現(xiàn)方式各個(gè)語(yǔ)言都相似,當(dāng)然,數(shù)據(jù)寫(xiě)入順序肯定是一樣的。
以上就是go實(shí)現(xiàn)thrift的網(wǎng)絡(luò)及傳輸性能需要注意問(wèn)題示例解析的詳細(xì)內(nèi)容,更多關(guān)于go thrift的網(wǎng)絡(luò)傳輸?shù)馁Y料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
GO語(yǔ)言實(shí)現(xiàn)TCP服務(wù)器的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何通過(guò)GO語(yǔ)言實(shí)現(xiàn)TCP服務(wù)器,文中的示例代碼講解詳細(xì),對(duì)我們深入了解Go語(yǔ)言有一定的幫助,需要的可以參考一下2023-03-03
Go中調(diào)用JS代碼(otto)的實(shí)現(xiàn)示例
Otto是一個(gè)用Go語(yǔ)言實(shí)現(xiàn)的JavaScript解釋器,可用于執(zhí)行和操作JavaScript代碼,適合在Go項(xiàng)目中執(zhí)行簡(jiǎn)單的JS腳本,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-10-10
Go語(yǔ)言中調(diào)用外部命令的方法總結(jié)
在工作中,我們時(shí)不時(shí)地會(huì)需要在Go中調(diào)用外部命令。本文為大家總結(jié)了Go語(yǔ)言中調(diào)用外部命令的幾種姿勢(shì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-11-11
Golang?errgroup?設(shè)計(jì)及實(shí)現(xiàn)原理解析
這篇文章主要為大家介紹了Golang?errgroup?設(shè)計(jì)及實(shí)現(xiàn)原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
一文帶你輕松學(xué)會(huì)Go語(yǔ)言動(dòng)態(tài)調(diào)用函數(shù)
這篇文章主要是帶大家學(xué)習(xí)一下Go語(yǔ)言是如何動(dòng)態(tài)調(diào)用函數(shù)的,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語(yǔ)言有一定的幫助,需要的可以參考下2022-11-11

