dubbo 协议组成

1.所有的RPC协议基本组成

2.dubbo中所支持的PRC协议使用

名称

实现描述

连接描述

适用场景

dubbo

传输服务: mina, netty(默认), grizzy

序列化: hessian2(默认), java, fastjson

自定义报文

单个长连接

NIO异步传输

1、常规RPC调用

2、传输数据量小

3、提供者少于消费者

rmi

传输:java rmi 服务

序列化:java原生二进制序列化

多个短连接

BIO同步传输

1、常规RPC调用

2、与原RMI客户端集成

3、可传少量文件

4、不支持防火墙穿透

hessian

传输服务:servlet容器

序列化:hessian二进制序列化

基于Http 协议传输,

依懒servlet容器配置

1、提供者多于消费者

2、可传大字段和文件

3、跨语言调用

http

传输服务:servlet容器

序列化:java原生二进制序列化

依懒servlet容器配置

1、数据包大小混合

thrift

与thrift RPC 实现集成,并在其基础上修改了报文头

长连接、NIO异步传输

 

3.dubbo协议的使用和配置

3.1 参数配置说明 

   name: 协议名称 dubbo|rmi|hessian|http|   (默认为dubbo协议)

   host:本机IP可不填,则系统自动获取 

   port:端口、填-1表示系统自动选择 (如果dubbo协议 -1代表从20880 开始一次递增)

   server:运行服务  mina|netty|grizzy|servlet|jetty (默认为 netty)

   serialization:序列化方式 hessian2|java|compactedjava|fastjson(默认为 hessian2)

 

4.dubbo支持的序列化

 

特点

fastjson

文本型:体积较大,性能慢、跨语言、可读性高

fst

二进制型:体积小、兼容 JDK 原生的序列化。要求 JDK 1.7 支持。

hessian2

二进制型:跨语言、容错性高、体积小

java

二进制型:在JAVA原生的基础上 可以写入Null

compactedjava

二进制型:与java 类似,内容做了压缩

nativejava

二进制型:原生的JAVA 序列化

kryo

二进制型:体积比hessian2 还要小,但容错性 没有hessian2 好

注意的是:支持dubbo的协议并不代表一定指出对应的序列化,比喻http就只能支持然生的java 需要映入依赖

  <dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>org.springframework.integration.httpinvoker</artifactId>
    <version>2.0.0.M4</version>
</dependency>

<!-- 配置协议 同时配置会报错   -1默认8080   -->
      <dubbo:protocol name="http" port="-1" />

      <!-- 默认值 -->
   <dubbo:provider protocol="http" port="-1"/>

5.演示容错率(java序列化和hessian2对比)

    dubbo序列化放在  利用spi 实现,(spi另外一篇文章中介绍)

 

 5.1对比序列化怎么体现容错率

import java.util.Date;
import java.util.List;

/**
* ......................................
* .没有才怎么怀才不遇,没有志怎么壮志难酬 .
* . ....................................
*
*@Author: lq
*@Date:  2018/12/4 10:14
*@Description:    比较几种序列化
*/
public class UserSerializable implements Serializable{

    private static final long serialVersionUID = -7103556216921677848L;
    private String name;
    private Date dateTime;
    private Integer age;
    private Integer age1;
    private Sex sex;
    List<String> list;

    public UserSerializable(String name, Date dateTime, Integer age, Sex sex, List<String> list) {
        this.name = name;
        this.dateTime = dateTime;
        this.age = age;
        this.sex = sex;
        this.list = list;
    }

    public UserSerializable(){}

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getDateTime() {
        return dateTime;
    }

    public void setDateTime(Date dateTime) {
        this.dateTime = dateTime;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Sex getSex() {
        return sex;
    }

    public void setSex(Sex sex) {
        this.sex = sex;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    @Override
    public String toString() {
        return "UserSerializable{" +
                "name='" + name + '\'' +
                ", dateTime=" + dateTime +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", list=" + list +
                '}';
    }


    public enum Sex {
        man,woman,ladyBody,girl;
    }
}

5.2测试

package com.fashion.common;

import com.alibaba.com.caucho.hessian.io.Hessian2Input;
import com.alibaba.com.caucho.hessian.io.Hessian2Output;
import org.junit.Assert;
import org.junit.Test;

import java.io.*;
import java.util.Date;
import java.util.LinkedList;

import static org.junit.Assert.*;

public class UserSerializableTest {


    //  java 序列化
    @Test
    public void javaSerializable() {
        String s = System.getProperty("user.dir") + "/target/user_java";
        try {
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(s));

            UserSerializable user = new UserSerializable("张三",new Date(),18, UserSerializable.Sex.man,new LinkedList<>());

            System.out.println("java  serializable success");
            outputStream.writeObject(user);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // java 反序列化
    @Test
    public void javaDeserializable() {
        String s = System.getProperty("user.dir") + "/target/user_java";

        try {
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(s));

            UserSerializable user = (UserSerializable) inputStream.readObject();

            System.out.println(user);
            Assert.assertNotNull(user);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    // hessian2 序列化
    @Test
    public void hessian2Serializable() {
        String s = System.getProperty("user.dir") + "/target/user_hessian";

        try {
            Hessian2Output ho = new Hessian2Output(new FileOutputStream(s));

            UserSerializable user = new UserSerializable("张三",new Date(),18, UserSerializable.Sex.man,new LinkedList<>());

            ho.writeObject(user);
            ho.flush();
            System.out.println("hessian2序列化完成");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // hessian2 反序列化
    @Test
    public void hessian2Deserializable() {
        String s = System.getProperty("user.dir") + "/target/user_hessian";

        try {
            Hessian2Input in = new Hessian2Input(new FileInputStream(s));

            UserSerializable user = (UserSerializable) in.readObject();

            System.out.println(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

5.3 通过结果得出 hessian2容错率比java要高,如果bean中增删属性,没有添加

private static final long serialVersionUID = -7103556216921677848L; 那么序列化会报错如果这个id不同就会报错

 但是我在岩石中并未发现这个问题。

不过通过对比,java的序列化的内容比hessian2要大的多,所以效率上 hessian2要高

5.4 序列化的兼容性 hessian2

数据通讯

情况

结果

A->B

类A多一种 属性(或者说类B少一种 属性)

不抛异常,A多的那 个属性的值,B没有, 其他正常

A->B

枚举A多一种 枚举(或者说B少一种 枚举),A使用多 出来的枚举进行传输

抛异常

A->B

枚举A多一种 枚举(或者说B少一种 枚举),A不使用 多出来的枚举进行传输

不抛异常,B正常接 收数据

A->B

A和B的属性 名相同,但类型不相同

抛异常

A->B

serialId 不相同

正常传输

   接口增加方法,对客户端无影响,如果该方法不是客户端需要的,客户端不需要重新部署。输入参数和结果集中增加属性,对客户端无影响,如果客户端并不需要新属性,不用重新部署。

    输入参数和结果集属性名变化,对客户端序列化无影响,但是如果客户端不重新部署,不管输入还是输出,属性名变化的属性值是获取不到的。

总结:服务器端和客户端对领域对象并不需要完全一致,而是按照最大匹配原则。

6.RPC协议编码解码(拆包和黏包)

       RPC的协议的传输是基于 TCP/IP 做为基础使用Socket 或Netty、mina等网络编程组件实现。但有个问题是TCP是面向字节流的无边边界协议,其只管负责数据传输并不会区分每次请求所对应的消息,这样就会出现TCP协义传输当中的拆包与粘包问题

         6.1    我们知道tcp是以流动的方式传输数据,传输的最小单位为一个报文段(segment)。tcp Header中有个Options标识位,常见的标识为mss(Maximum Segment Size)指的是,连接层每次传输的数据有个最大限制MTU(Maximum Transmission Unit),一般是1500比特,超过这个量要分成多个报文段,mss则是这个最大限制减去TCP的header,光是要传输的数据的大小,一般为1460比特。换算成字节,也就是180多字节。    

       6.2   tcp为提高性能,发送端会将需要发送的数据发送到缓冲区,等待缓冲区满了之后,再将缓冲中的数据发送到接收方。同理,接收方也有缓冲区这样的机制,来接收数据。这时就会出现以下情况:

  1. 应用程序写入的数据大于MSS大小,这将会发生拆包
  2. 应用程序写入数据小于MSS大小,这将会发生粘包
  3. 接收方法不及时读取套接字缓冲区数据,这将发生粘包

  6.3 拆包和黏包的解决办法(netty中已有实现)本次将dubbo的实现

       1.可以设置定长消息,服务端每次读取到消息先判断是不是这个长度,那么直接截取,如果不是等待下一波,得到定长再开始读取。

      2.使用带消息头的协议、消息头存储消息开始标识及消息长度信息,服务端获取消息头的时候解析出消息长度,然后向后读取该长度的内容。

     3.http 协议的heade 中的 Content-Length标识消息体的大小

     

6.4   dubbo协议

6.5   固定的定长请求头 16个字节 16 * 8 个字节

源码 

 会获取到16个字节开始判断

6.6 协议解释:

  1. magic:类似java字节码文件里的魔数,用来判断是不是dubbo协议的数据包。魔数是常量0xdabb,用于判断报文的开始。
  2. flag:标志位, 一共8个地址位。低四位用来表示消息体数据用的序列化工具的类型(默认hessian),高四位中,第一位为1表示是request请求,第二位为1表示双向传输(即有返回response),第三位为1表示是心跳ping事件。
  3. status:状态位, 设置请求响应状态,dubbo定义了一些响应的类型。具体类型见 com.alibaba.dubbo.remoting.exchange.Response
  4. invoke id:消息id, long 类型。每一个请求的唯一识别id(由于采用异步通讯的方式,用来把请求request和返回的response对应上)
  5. body length:消息体 body 长度, int 类型,即记录Body Content有多少个字节。

6.7源码

  > com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec#encode

   >com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec#encodeRequest   读取16个字节

 请求头读取后开始进行序列化 

>com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec#encodeRequestData 

>数据修完后开始进行发送 那么到达了服务端   

 -》com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec#decode(com.alibaba.dubbo.remoting.Channel, com.alibaba.dubbo.remoting.buffer.ChannelBuffer)

      >com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec#decode(com.alibaba.dubbo.remoting.Channel, com.alibaba.dubbo.remoting.buffer.ChannelBuffer, int, byte[])

      >com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec#decodeBody

 >com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation#decode(com.alibaba.dubbo.remoting.Channel, java.io.InputStream)

猜你喜欢

转载自blog.csdn.net/qq_36260963/article/details/84785877
今日推荐