服务治理—Dubbo

Dubbo底层协议

  1. Dubbo:以下
  2. RMI:底层采用阻塞式(同步传输)的短连接+JDK中标准的二进制序列化
    • 适用场景:参数数据包大小不一,服务提供者和服务消费者的个数相近
  3. Hessian 用于集成Hessian的服务,基于HTTP短连接,采用Servlet向外暴露服务
    • 适用场景: 参数数据包较大,服务提供者的数量远多于消费者数量
  4. HTTP:最简单的基于HTTP表单的协议
    • 适用场景 可以使用浏览器直接查看,适用于需要同时给后台应用程序以及浏览器提供服务的场景(很多其他协议无法通过浏览器直接发起调用)
  5. WebService:基于大名鼎鼎的Apache CXF,使用基于SOAP的序列化技术
    • 适用场景 比如公司内部的系统是由多种不同语言构成的,那么这个场景就比较适合采用WebService协议,WebService通常使用在系统集成和跨语言调用场景中
  6. REST:基于标准的Java REST API(JAX-RS 2.0)实现的REST风格调用,Dubbo花了大力气在这部分的实现上面,它的使用风格和原生Spring MVC类似
  7. Thrift:略
  8. Memcached:略
  9. Redis:略

Dubbo协议

项目 说明
连接个数 单连接
连接方式 长连接
传输协议 TCP
底层通信框架 Netty异步NIO
序列化方式 Hessian2或Dubbo序列化

Dubbo协议的适用场景

  • 适用场景:传入传出参数数据包较小(建议小于100K),但是并发量高的场景。简单来说就是短平快,QPS/TPS高但是数据量小的情况

  • 不适场景:尽量不要用Dubbo协议传输大数据包(比如大文件、视频、超大字符串等),这类场景建议使用上面介绍过的其他协议栈

Dubbo协议的调用流程

在这里插入图片描述

Transporter

大家注意看图片底部最中间的Transporter,这个是底层网络传输组件,目前Dubbo支持Mina和Netty。大家可能对Netty比较熟悉,但并没有接触过Mina。在Dubbo的2.0版本以后已经从Mina全面替换为Netty,基于Netty的传输层在稳定性和性能上都要更好一些。

Header & Body

接下来我们看图中的绿色部分,就是Header和Body,这部分是Dubbo定义的私有RPC协议中的数据格式部分,它是一个变长协议,由定长的Header和不定长的Body组成。

  • Header
    图片描述

上面是Header的结构,图片中的数字代表Header中Bit位置,下面的文字表示这段字节所携带的信息,Header总长度为16字节。其中Magic High=0xda,Magic Low=0xbb,标识了这是个Dubbo协议。

后面紧跟着16-23比特位带有两个信息:当前请求的类型以及序列化的方式。其中高四位表示当前请求的Request flag(有三种类型:REQUEST, TWOWAY和EVENT),低四位表示序列化的方式,Dubbo有四种序列化方式,分别对应Dubbo,FastJson,Hessian2和Java原生序列化。

接下来24-31位是响应报文才有的信息,作为Request报文并不包含这部分信息。其中定义了详情请求的状态,比如OK,BAD_RESPONSE,SERVER_TIMEOUT。注意这些Status Code可不是HTTP Status,而是Dubbo自定义的状态。

最后两个部分分别对应Request ID(唯一请求ID)和经过序列化后的Body内容的长度。

  • Body

这部分是Dubbo协议中不定长的部分,在传输之前会经过序列化处理,对于一个请求包来说,主要包含三部分的信息:

  1. 协议版本:Dubbo当前的版本
  2. 寻址信息:目标服务的名称,服务版本,方法名,方法签名类型
  3. 数据:方法参数值,附件形式的数据等

Client,Server和ThreadPool

Client对应调用发起方,Server对应服务提供方。

  • Client:在发起调用之前会将整个消息进行序列化,组装成上面的Header+Body的变长协议格式
  • Server:根据Header里标识的序列化方式,对Body中的内容进行反序列化
  • ThreadPool:响应服务调用请求的线程池,可以选择配置Fixed或Cached Thread Pool

Dispatcher线程派发模型

Dispatcher用来创建具有线程派发能力的ChannelHandler,将来访Request派发到线程池或当前IO线程。我们可以给Dispatcher配置5种派发策略

策略 说明
all 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件等
direct 所有消息都不派发到线程池,全部在 IO 线程上直接执行
message 只有请求和响应消息派发到线程池,其它消息均在 IO 线程上执行
execution 只有请求消息派发到线程池,不含响应。其它消息均在 IO 线程上执行
connection 在 IO 线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池

协议的约束

既然Dubbo协议应用了序列化和反序列化技术,那么对我们数据传输有几个约束条件:

  1. Serializable:参数和返回值必须实现Serializable接口,否则在调用的时候会抛出无法序列化/反序列化的异常
  2. 使用JDK原生类:比如Map、Date、List、Number等一系列接口,我们不能在返回值和参数中使用自己创建的实现类,只能使用JDK原生的接口实现类。

服务的同步和异步调用

Dubbo支持同步和异步两种调用方式,整个链路流程非常复杂,我们只抓重点看下两个调用模式的不同

  • 异步调用:异步调用还可细分为“有返回值”的异步调用和“无返回值”的异步调用。所谓“无返回值”异步调用是指服务消费方只管调用,但不关心调用结果,此时 Dubbo 会直接返回一个空的 RpcResult。若要使用异步特性,需要服务消费方手动进行配置。
  • 同步调用:这是默认情况下Dubbo所采用的调用方式,调用方等待服务提供者返回处理结果

示例代码

配置

pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.7.3</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <version>2.7.3</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.3</version>
        </dependency>

        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>dubbo-api</artifactId>
            <version>${project.version}</version>
        </dependency>

    </dependencies>

application.xml

dubbo:
  application:
    name: dubbo-provider/dubbo-consumer
  registry:
    # zookeeperip
    address: zookeeper://127.0.0.1:2181
    # 启动时检查是否存在,注册中心不存在就报错
    protocol: zookeeper
    check: false
  #Dubbo Admin
  metadata-report:
    address: zookeeper://127.0.0.1:2181
  #协议配置,
  protocol:
    # name必填,指定协议类型
    #dubbo协议缺省端口为20880,rmi为1099,http和hessian为80
    name: dubbo
  monitor:
    #用于配置连接监控中心相关信息,可选
    protocol: register
server:
  port: 63000/63001

Api

public interface IDubboService {
    
    

    Product publish(Product prod);

}
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;


@Data
public class Product implements Serializable {
    
    

    private String name;
    private BigDecimal price;

}

Provider

import org.apache.dubbo.config.annotation.Service;

@Service
@Slf4j
public class DubboService implements IDubboService {
    
    

    @Override
    public Product queryByID(Product prod) {
    
    
        log.info("Publishing prod {}", prod.getName());
        return prod;
    }
}
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubbo
public class DubboProviderApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(DubboProviderApplication.class, args);
    }
}

Consumer

import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Controller {
    
    

    @Reference(loadbalance = "roundrobin")
    private IDubboService dubboService;

    @PostMapping("/publish")
    public Product publish(@RequestParam String name) {
    
    
        Product product = new Product();
        product.setName(name);
        return dubboService.publish(product);
    }

}
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableDubbo
public class DubboConsumerApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(DubboConsumerApplication.class, args);
    }
}

Dubbo Admin(略)

applicattion.properties

# centers in dubbo2.7
admin.registry.address=zookeeper://127.0.0.1:2181
admin.config-center=zookeeper://127.0.0.1:2181
admin.metadata-report.address=zookeeper://127.0.0.1:2181

#group
admin.registry.group=dubbo
admin.config-center.group=dubbo
admin.metadata-report.group=dubbo

admin.apollo.token=e18e5cd903fd0c97a116c873b448544b9d086de1
admin.apollo.appId=test
admin.apollo.env=dev
admin.apollo.cluster=default
admin.apollo.namespace=dubbo

Dubbo负载均衡

负载均衡策略 底层算法
RandomLoadBalance 基于权重算法的负载均衡策略
LeastActiveLoadBalance 基于最少活跃调用数算法
ConsistentHashLoadBalance 基于Hash一致性
RoundRobinLoadBalance 基于加权轮询算法

猜你喜欢

转载自blog.csdn.net/tolmanlau/article/details/105742044