Dubbo

1.背景知识

 

  • 单一应用架构

简单的 ORM 单机的 ssm

  • 垂直架构

将应用的功能独立拆分, 使用nginx 负载均衡代理多台tomcat

  • 分布式架构

分布式架构,就是将应用按照功能模块进行拆分,模块块之间通过http,tcp 请求完成远程的访问和业务调用

RPC: 远程过程调用 ,底层通过 tcp (nio 非阻塞io 实现(netty) )

具体框架dubbo

 

soa: 面向服务的框架

springcloud : springcloud -netflix ,springcloud -alibaba

dubbo 相当于springcloud 中的feign

2.Dubbo

高性能Java RPC框架

Apache Dubbo 是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:

面向接口的远程方法调用,----》feign

智能容错和负载均衡,----》sentinel,ribbon

以及服务自动注册和发现(使用zk,redis)。----->nacos

dubbo 架构体系

节点角色说明

节点角色说明
Provider暴露服务的服务提供方
Consumer调用远程服务的服务消费方
Registry服务注册与发现的注册中心
Monitor统计服务的调用次数和调用时间的监控中心
Container服务运行容器

调用关系说明

  1. 服务容器负责启动,加载,运行服务提供者。

  2. 服务提供者在启动时,向注册中心注册自己提供的服务。

  3. 服务消费者在启动时,向注册中心订阅自己所需的服务。

  4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。

  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

  6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

连通性

注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中

心交互,注册中心不转发请求,压力较小监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心

服务器,并以报表展示

服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销

服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用

时间到监控中心,此时间包含网络开销

健壮性

监控中心宕掉不影响使用,只是丢失部分采样数据

数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务

注册中心对等集群,任意一台宕掉后,将自动切换到另一台

注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯

服务提供者无状态,任意一台宕掉后,不影响使用

服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复

升级性

当服务集群规模进一步扩大,带动IT治理结构进一步升级,需要实现动态部署,进行流动计算,现有分

布式服务架构不会带来阻力。

文档 | Apache Dubbo

3.dubbo 例子

1.provider 服务提供者 提供查询 学生的服务

2.consumer 服务消费者 需要远程调用 查询学生的服务

1.创建一个父子工程

 <!--  父工程-->
    <packaging>pom</packaging>
​
​
​
    <!-- Spring Boot 启动父依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
    </parent>

2.创建common子工程

不用导入依赖。创建实体类Student

import java.io.Serializable;

/**
 * 序列化
 *       1.将对象保存在磁盘
 *       2.便于对象 网络传输
 */

public class Student implements Serializable {

    private Integer id;

    private String name;

    private Integer age;

    private String school;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public String getSchool() {
        return school;
    }

    public void setSchool(String school) {
        this.school = school;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", school='" + school + '\'' +
                '}';
    }
}


创建service

public interface StudentService {

    public Student findStudentById(Integer id);

}
 

接着一定要install打包

 

3.创建provider ,并引入依赖(注意导入common工程,因为需要用到实体类和服务)

<dependencies> <!-- Spring Boot Web 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Spring Boot Test 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Spring Boot Dubbo 依赖 -->
        <dependency>
            <groupId>com.alibaba.spring.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>com.qw</groupId>
            <artifactId>dubbo-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- zookeeper依赖 -->
        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.10</version>
        </dependency>


    </dependencies>

创建配置文件application.properties

server.port=8088


#dubbo 服务名称
spring.dubbo.application.name=provider
# zookeeper 注册中心
spring.dubbo.registry.address=zookeeper://8.130.166.101:2182

#zookeeper 通信端口
spring.dubbo.protocol.name=dubbo

spring.dubbo.protocol.port=20881

启动类

@SpringBootApplication
//开启Dubbo
@EnableDubboConfiguration
public class ProviderApplication {

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

}

提供数据服务(注意Service包,这里导的是dubbo的)

import com.alibaba.dubbo.config.annotation.Service;
import com.qf.entity.Student;
import com.qf.service.StudentService;
import org.springframework.stereotype.Component;

@Service
@Component
public class StudentServiceImpl implements StudentService {
    @Override
    public Student findStudentById(Integer id) {
        //dao层查询
        Student student = new Student();
        student.setId(100);
        student.setName("藏三");
        student.setAge(21);
        student.setSchool("和平路小学");

        return student;

    }
}

4.创建消费者comsumer

消费者的依赖 和配置 同provider ,没有任何区别,除了服务名

依赖和provider相同

<dependencies> <!-- Spring Boot Web 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Spring Boot Test 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Spring Boot Dubbo 依赖 -->
        <dependency>
            <groupId>com.alibaba.spring.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>com.qw</groupId>
            <artifactId>dubbo-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- zookeeper依赖 -->
        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.10</version>
        </dependency>


    </dependencies>

配置文件

server.port=8089


#dubbo 服务名称
spring.dubbo.application.name=consumer
# zookeeper 注册中心
spring.dubbo.registry.address=zookeeper://8.130.166.101:2182

#zookeeper 通信端口
spring.dubbo.protocol.name=dubbo

spring.dubbo.protocol.port=20881

启动类

@EnableDubboConfiguration// 激活dubbo 相关配置
@SpringBootApplication
public class ConsumerApplication {


    public static void main(String[] args) {

        SpringApplication.run(ConsumerApplication.class,args);

    }

}

远程调用provider 提供数据服务(注意reference的包)

import com.alibaba.dubbo.config.annotation.Reference;
import com.qf.entity.Student;
import com.qf.service.StudentService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class StudentController {


    @Reference()// @Reference是dubbo 中的注解,就是帮助我们伪装了 接口实现类,搬我们发起远程调用
    private StudentService studnetService;


    @RequestMapping("/findStudentById")
    public Student findStudentById(int id) {


        // 远程调用 provider  中的  StudentServiceImpl.findStudentById();

        // 发起远程rpc 调用
        Student student = studnetService.findStudentById(id);
        System.out.println("student = " + student);

        return student;

    }
}

备注:一定要先启动provider 在启动consumer

4.dubbo 高级配置

consumer 配置

#对每个提供者的最大连接数,rmi 、 http 、hessian等 短连接 协议表示限制连接数,Dubbo等 长连接 协表示建立的长连接个数 默认0
spring.dubbo.customer.connections=10
#每服务消费者每服务每方法最大并发调用数 默认是0
spring.dubbo.customer.actives=20

provider配置

#重试次数
spring.dubbo.service.retries=2

#全局配置 负载均衡配置
spring.dubbo.service.loadbalance=random



#业务处理 线程池 大小 默认200
spring.dubbo.service.threads=5

#服务提供方最大可接受连接数 0表示不限制
spring.dubbo.service.acceptes=5

#服务提供者每服务每方法最大可并行执行请求数  0表示不限制
spring.dubbo.service.executes=5

check

用在@Reference的参数,启动时检查提供者是否存在,true报错,false忽略,默认为true,如果生产者不存在,消费者启动会报错

  • true:默认值,会去检查provider 是否有可用的

  • false:启动时不去检查 provider是否可用

 

 /**
    * check = false  启动时不去检查是否有可用 provider
    *
    */
   @Reference(check = false)// @Reference是dubbo 中的注解,就是帮助我们伪装了 接口实现类,搬我们发起远程调用
   private StudentService studentService;


服务分组

当一个接口有多种实现时,可用使用group分组。

根据消费者启动时依赖的服务分组不同,会调用不同的服务

相当于分布式接口的 多态

服务提供者

/**
 * // 注意service 是 com.alibaba.dubbo.config.annotation
 *
 * group = "g1" 当前服务提供者 属于g1分组 凡是想调用给服务的消费者必须属于 g1分组
 *
 */
@Service(group = "g1")// 标记当前接口 可以被外部 通过rpc 远程访问
@Component // 加入到容器
public class StudentServiceImpl implements StudentService {
。。。。。。
}

/**
 * // 注意service 是 com.alibaba.dubbo.config.annotation
 *
 * group = "g2" 当前服务提供者 属于g1分组 凡是想调用给服务的消费者必须属于 g2分组
 *
 */
@Service(group = "g2")// 标记当前接口 可以被外部 通过rpc 远程访问
@Component // 加入到容器
public class StudentServiceImpl2 implements StudentService {
。。。
}

消费者

   /**
     * check = false  启动时不去检查是否有可用 provider
     * group = "g2"  表明消费者属于g2 也只会调用g2 分组中的服务
     *
     */
    @Reference(check = false,group = "g2")// @Reference是dubbo 中的注解,就是帮助我们伪装了 接口实现类,搬我们发起远程调用
    private StudentService studentService;

 

多版本

当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。

public Student findStudentById(int id) ----> public ResponseData<Student> findStudentById(int id)

provider

/**
 * // 注意service 是 com.alibaba.dubbo.config.annotation
 *
 * group = "g1" 当前服务提供者 属于g1分组 凡是想调用给服务的消费者必须属于 g1分组
 * version = "v1"  version 一般用于接口的升级 ,解决兼容性问题
 *          消费者调用 provider 是根据 group version 作为联合条件选择provider
 * 
 * 
 */
@Service(group = "g1",version = "v1")// 标记当前接口 可以被外部 通过rpc 远程访问
@Component // 加入到容器
public class StudentServiceImpl implements StudentService {
​
。。。。
}

消费者

    /**
     * check = false  启动时不去检查是否有可用 provider
     * group = "g1"  表明消费者属于g1 也只会调用g1 分组中的服务
     *
     */
    @Reference(check = false,group = "g1",version = "v1")// @Reference是dubbo 中的注解,就是帮助我们伪装了 接口实现类,搬我们发起远程调用
    private StudentService studentService;

延迟暴露

如果你的服务需要预热时间,比如初始化缓存,等待相关资源就位等,可以使用 delay 进行延迟暴露。我们在 Dubbo 2.6.5 版本中对服务延迟暴露逻辑进行了细微的调整,将需要延迟暴露(delay > 0)服务的倒计时动作推迟到了 Spring 初始化完成后进行。你在使用 Dubbo 的过程中,并不会感知到此变化,因此请放心使用。

延迟暴露 就是provider 原来启动就注册到注册中,消费者立即发现。现在由于某些原因,provider依赖的资源没有立即到位(初始化缓存),不能立即提供服务,此时就是需要延迟 一段时间,在注册到注册中心,消费者才可以调用

问题:

provider 需要依赖hbase ,hbase连接需要10多秒时间才能就位,
​
如果provider立即暴露,就会造成消费者报错,拿不到数据,此时需要延迟暴露

/**
 * // 注意service 是 com.alibaba.dubbo.config.annotation
 *
 * group = "g1" 当前服务提供者 属于g1分组 凡是想调用给服务的消费者必须属于 g1分组
 * version = "v1"  version 一般用于接口的升级 ,解决兼容性问题
 *          消费者调用 provider 是根据 group version 作为联合条件选择provider
 *          
 * ,delay=5000  延迟暴露 5s  再被消费者发现使用         
 *
 *
 */
@Service(group = "g1",version = "v1",delay=5000)// 标记当前接口 可以被外部 通过rpc 远程访问
@Component // 加入到容器
public class StudentServiceImpl implements StudentService {
。。。。。。
}

集群容错

Failover Cluster

失败自动切换,当出现失败,重试其它服务器 [1]。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。

@Service(retries = 2)
​
@Reference(retries = 2)
<dubbo:service retries="2" />

Failfast Cluster

快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。

Failsafe Cluster

失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。

Failback Cluster

失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

Forking Cluster

并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。

Broadcast Cluster

广播调用所有提供者,逐个调用,任意一台报错则报错 [2]。通常用于通知所有提供者更新缓存或日志等本地资源信息。

服务端配置

 * retries = 2,cluster="failover" 配置重试策略,重试 2
 * cluster="failfast" 快速失败
 *
 *
 */
@Service(group = "g1",version = "v1",delay=5000,cluster="failfast")// 标记当前接口 可以被外部 通过rpc 远程访问
@Component // 加入到容器
public class StudentServiceImpl implements StudentService {
​
。。。。
}

消费者配置

    /**
     * check = false  启动时不去检查是否有可用 provider
     * group = "g1"  表明消费者属于g1 也只会调用g1 分组中的服务
     *
     * ,cluster = "failfast"  配置快速失败策略
     *
     */
    @Reference(check = false,group = "g1",version = "v1",cluster = "failfast")// @Reference是dubbo 中的注解,就是帮助我们伪装了 接口实现类,搬我们发起远程调用
    private StudentService studentService;

负载均衡

在集群负载均衡时,Dubbo 提供了多种均衡策略。

随机 random。还可以配置轮询roundrobin、最不活跃优先 [4] leastactive 和一致性哈希 consistenthash

负载均衡的关键字就是:每种的粗略的第一个单词小写

  • random

  • roundrobin

  • leastactive

  • consistenthash

Random LoadBalance

  • 随机,按权重设置随机概率。

  • 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。

RoundRobin LoadBalance

  • 轮询,按公约后的权重设置轮询比率。

  • 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。

LeastActive LoadBalance

  • 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。

  • 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

ConsistentHash LoadBalance

  • 一致性 Hash,相同参数的请求总是发到同一提供者。

  • 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。

如何配置

1.全局配置

#全局配置 服务端负载均衡配置
spring.dubbo.service.loadbalance=random

2.通过注解配置单个接口

消费者端配置

/**
     * check = false  启动时不去检查是否有可用 provider
     * group = "g1"  表明消费者属于g1 也只会调用g1 分组中的服务
     *
     * ,cluster = "failfast"  配置快速失败策略
     *  loadbalance="random" 配置远程调用  随机策略
     */
    @Reference(check = false,group = "g1",version = "v1",cluster = "failfast",loadbalance="random")// @Reference是dubbo 中的注解,就是帮助我们伪装了 接口实现类,搬我们发起远程调用
    private StudentService studentService;

生产者配置(生产者 消费者 配其一 )

@Service(group = "g1",version = "v1",delay=5000,loadbalance = "random")

容错

容错,当服务提供者挂了,消费者依然可以返回兜底数据

dubbo 是通过mock 配置容错

1.在消费者 创建 容错实现类

public class StudentServiceMockImpl implements StudentService {
    @Override
    public Student findStudentById(Integer id) {
        Student student = new Student();
        student.setId(id);
        student.setName("xxxx-From consumer---容错策略");
        student.setAge(-1);
        student.setSchool("兜底数据");
        System.out.println("student = " + student);
        return student ;
    }
}

2.使用

  
@RestController
public class StudentController {

    /**
         * check = false  启动时不去检查是否有可用 provider
         * group = "g1"  表明消费者属于g1 也只会调用g1 分组中的服务
         *
         * ,cluster = "failfast"  配置快速失败策略
         *  loadbalance="random" 配置远程调用  随机策略
         *
         *  mock = "com.qfedu.service.impl.StudentServiceMockImpl" 配置客户端容错措施
         *
         */
    @Reference(check = false,mock = "com.qf.wrong.StudentServiceMockImpl")// @Reference是dubbo 中的注解,就是帮助我们伪装了 接口实现类,搬我们发起远程调用
    private StudentService studnetService;


    @RequestMapping("/findStudentById")
    public Student findStudentById(int id) {


        // 远程调用 provider  中的  StudentServiceImpl.findStudentById();

        // 发起远程rpc 调用
        Student student = studnetService.findStudentById(id);
        System.out.println("student = " + student);

        return student;

    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇智波波奶茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值