Dubbo是 Alibaba 開源的分布式服務框架遠程調用框架,在網絡間傳輸數據,就需要通信協議和序列化。
一 通信協議
Dubbo支持dubbo、rmi、hessian、http、webservice、thrift、redis等多種協議,但是Dubbo官網是推薦我們使用Dubbo協議的,默認也是用的dubbo協議。
先介紹幾種常見的協議:
1. dubbo協議
缺省協議,使用基於mina1.1.7+hessian3.2.1的tbremoting交互。
連接個數:單連接
連接方式:長連接
傳輸協議:TCP
傳輸方式:NIO異步傳輸
序列化:Hessian二進制序列化
適用范圍:傳入傳出參數數據包較小(建議小於100K),消費者比提供者個數多,單一消費者無法壓滿提供者,盡量不要用dubbo協議傳輸大文件或超大字符串。
適用場景:常規遠程服務方法調用
1、dubbo默認采用dubbo協議,dubbo協議采用單一長連接和NIO異步通訊,適合於小數據量大並發的服務調用,以及服務消費者機器數遠大於服務提供者機器數的情況
2、他不適合傳送大數據量的服務,比如傳文件,傳視頻等,除非請求量很低。
配置如下:
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name=“dubbo” port=“9090” server=“netty” client=“netty” codec=“dubbo”
serialization=“hessian2” charset=“UTF-8” threadpool=“fixed” threads=“100” queues=“0” iothreads=“9”
buffer=“8192” accepts=“1000” payload=“8388608” />
3、Dubbo協議缺省每服務每提供者每消費者使用單一長連接,如果數據量較大,可以使用多個連接。
<dubbo:protocol name="dubbo" connections="2" />
4、為防止被大量連接撐掛,可在服務提供方限制大接收連接數,以實現服務提供方自我保護
<dubbo:protocol name="dubbo" accepts="1000" />
2. rmi協議
Java標准的遠程調用協議。
連接個數:多連接
連接方式:短連接
傳輸協議:TCP
傳輸方式:同步傳輸
序列化:Java標准二進制序列化
適用范圍:傳入傳出參數數據包大小混合,消費者與提供者個數差不多,可傳文件。
適用場景:常規遠程服務方法調用,與原生RMI服務互操作
RMI協議采用JDK標准的java.rmi.*實現,采用阻塞式短連接和JDK標准序列化方式 。
3. hessian協議
基於Hessian的遠程調用協議。
連接個數:多連接
連接方式:短連接
傳輸協議:HTTP
傳輸方式:同步傳輸
序列化:表單序列化
適用范圍:傳入傳出參數數據包大小混合,提供者比消費者個數多,可用瀏覽器查看,可用表單或URL傳入參數,暫不支持傳文件。
適用場景:需同時給應用程序和瀏覽器JS使用的服務。
1、Hessian協議用於集成Hessian的服務,Hessian底層采用Http通訊,采用Servlet暴露服務,Dubbo缺省內嵌Jetty作為服務器實現。
2、Hessian是Caucho開源的一個RPC框架:http://hessian.caucho.com,其通訊效率高於WebService和Java自帶的序列化。
4. http協議
基於http表單的遠程調用協議。參見:[HTTP協議使用說明]
連接個數:多連接
連接方式:短連接
傳輸協議:HTTP
傳輸方式:同步傳輸
序列化:表單序列化
適用范圍:傳入傳出參數數據包大小混合,提供者比消費者個數多,可用瀏覽器查看,可用表單或URL傳入參數,暫不支持傳文件。
適用場景:需同時給應用程序和瀏覽器JS使用的服務。
5. webservice協議
基於WebService的遠程調用協議。
連接個數:多連接
連接方式:短連接
傳輸協議:HTTP
傳輸方式:同步傳輸
序列化:SOAP文本序列化
適用場景:系統集成,跨語言調用
二 序列化
序列化是將一個對象變成一個二進制流就是序列化, 反序列化是將二進制流轉換成對象。
為什么要序列化?
1. 減小內存空間和網絡傳輸的帶寬
2. 分布式的可擴展性
3. 通用性,接口可共用。
Dubbo序列化支持java、compactedjava、nativejava、fastjson、dubbo、fst、hessian2、kryo,其中默認hessian2。其中java、compactedjava、nativejava屬於原生java的序列化。
dubbo序列化:阿里尚未開發成熟的高效java序列化實現,阿里不建議在生產環境使用它。
hessian2序列化:hessian是一種跨語言的高效二進制序列化方式。但這里實際不是原生的hessian2序列化,而是阿里修改過的,它是dubbo RPC默認啟用的序列化方式。
json序列化:目前有兩種實現,一種是采用的阿里的fastjson庫,另一種是采用dubbo中自己實現的簡單json庫,但其實現都不是特別成熟,而且json這種文本序列化性能一般不如上面兩種二進制序列化。
java序列化:主要是采用JDK自帶的Java序列化實現,性能很不理想。
dubbo序列化主要由Serialization(序列化策略)、DataInput(反序列化,二進制->對象)、DataOutput(序列化,對象->二進制流) 來進行數據的序列化與反序列化。其關系類圖為:

看一下Serialization的接口:
1 /** 2 * Serialization. (SPI, Singleton, ThreadSafe) 3 */ 4 @SPI("hessian2") 5 public interface Serialization { 6 7 /** 8 * get content type id 9 * 10 * @return content type id 11 */ 12 byte getContentTypeId(); 13 14 /** 15 * get content type 16 * 17 * @return content type 18 */ 19 String getContentType(); 20 21 /** 22 * create serializer 23 * 24 * @param url 25 * @param output 26 * @return serializer 27 * @throws IOException 28 */ 29 @Adaptive 30 ObjectOutput serialize(URL url, OutputStream output) throws IOException; 31 32 /** 33 * create deserializer 34 * 35 * @param url 36 * @param input 37 * @return deserializer 38 * @throws IOException 39 */ 40 @Adaptive 41 ObjectInput deserialize(URL url, InputStream input) throws IOException; 42 43 }
從上面類圖可以看出各個Serialization實現類調用了各自的output和input,我們看一下默認的hessian2的實現。
1 public class Hessian2Serialization implements Serialization { 2 3 public static final byte ID = 2; 4 5 public byte getContentTypeId() { 6 return ID; 7 } 8 9 public String getContentType() { 10 return "x-application/hessian2"; 11 } 12 13 public ObjectOutput serialize(URL url, OutputStream out) throws IOException { 14 return new Hessian2ObjectOutput(out); 15 } 16 17 public ObjectInput deserialize(URL url, InputStream is) throws IOException { 18 return new Hessian2ObjectInput(is); 19 } 20 21 }
output實現類:
1 public class Hessian2ObjectOutput implements ObjectOutput { 2 private final Hessian2Output mH2o; 3 4 public Hessian2ObjectOutput(OutputStream os) { 5 mH2o = new Hessian2Output(os); 6 mH2o.setSerializerFactory(Hessian2SerializerFactory.SERIALIZER_FACTORY); 7 } 8 9 public void writeBool(boolean v) throws IOException { 10 mH2o.writeBoolean(v); 11 } 12 13 public void writeByte(byte v) throws IOException { 14 mH2o.writeInt(v); 15 } 16 17 public void writeShort(short v) throws IOException { 18 mH2o.writeInt(v); 19 } 20 21 public void writeInt(int v) throws IOException { 22 mH2o.writeInt(v); 23 } 24 25 public void writeLong(long v) throws IOException { 26 mH2o.writeLong(v); 27 } 28 29 public void writeFloat(float v) throws IOException { 30 mH2o.writeDouble(v); 31 } 32 33 public void writeDouble(double v) throws IOException { 34 mH2o.writeDouble(v); 35 } 36 37 public void writeBytes(byte[] b) throws IOException { 38 mH2o.writeBytes(b); 39 } 40 41 public void writeBytes(byte[] b, int off, int len) throws IOException { 42 mH2o.writeBytes(b, off, len); 43 } 44 45 public void writeUTF(String v) throws IOException { 46 mH2o.writeString(v); 47 } 48 49 public void writeObject(Object obj) throws IOException { 50 mH2o.writeObject(obj); 51 } 52 53 public void flushBuffer() throws IOException { 54 mH2o.flushBuffer(); 55 } 56 }
input實現類:
1 public class Hessian2ObjectInput implements ObjectInput { 2 private final Hessian2Input mH2i; 3 4 public Hessian2ObjectInput(InputStream is) { 5 mH2i = new Hessian2Input(is); 6 mH2i.setSerializerFactory(Hessian2SerializerFactory.SERIALIZER_FACTORY); 7 } 8 9 public boolean readBool() throws IOException { 10 return mH2i.readBoolean(); 11 } 12 13 public byte readByte() throws IOException { 14 return (byte) mH2i.readInt(); 15 } 16 17 public short readShort() throws IOException { 18 return (short) mH2i.readInt(); 19 } 20 21 public int readInt() throws IOException { 22 return mH2i.readInt(); 23 } 24 25 public long readLong() throws IOException { 26 return mH2i.readLong(); 27 } 28 29 public float readFloat() throws IOException { 30 return (float) mH2i.readDouble(); 31 } 32 33 public double readDouble() throws IOException { 34 return mH2i.readDouble(); 35 } 36 37 public byte[] readBytes() throws IOException { 38 return mH2i.readBytes(); 39 } 40 41 public String readUTF() throws IOException { 42 return mH2i.readString(); 43 } 44 45 public Object readObject() throws IOException { 46 return mH2i.readObject(); 47 } 48 49 @SuppressWarnings("unchecked") 50 public <T> T readObject(Class<T> cls) throws IOException, 51 ClassNotFoundException { 52 return (T) mH2i.readObject(cls); 53 } 54 55 public <T> T readObject(Class<T> cls, Type type) throws IOException, ClassNotFoundException { 56 return readObject(cls); 57 } 58 59 }
其他的實現類也是類似。
如果想要改變序列化方式,可以更改配置,xml配置可以使用<dubbo:protocol serialization="fastjson" />,springboot配置可以使用dubbo.protocol.serialization=fastjson。
hessian 是一個比較老的序列化實現了,而且它是跨語言的,所以不是單獨針對java進行優化的。而dubbo RPC實際上完全是一種Java to Java的遠程調用,其實沒有必要采用跨語言的序列化方式(當然肯定也不排斥跨語言的序列化)。
現在有一些新的序列化:
專門針對Java語言的:Kryo,FST等等
跨語言的:Protostuff,ProtoBuf,Thrift,Avro,MsgPack等等
這些序列化方式的性能多數都顯著優於 hessian2 (甚至包括尚未成熟的dubbo序列化)。所以我們可以
為 dubbo 引入 Kryo 和 FST 這兩種高效 Java 來優化 dubbo 的序列化。
