【Dubbo 2.7.3】原理与实战


一、什么是Dubbo?

    早期Dubbo是基于Java的高性能、轻量级的RPC框架, 是SOA方面的技术. SOA架构, Service Oriented Architecture 面向服务结构. SOA = RPC + 服务治理
    现在是一款易用的、高性能的Web和RPC框架. 同时为构建企业级微服务提供服务发现,流量治理、可观测、认证鉴权等能力.全面面向微服务, Dubbo已经在阿里巴巴内部微服务集群全面落地. 依托Dubbo3, 阿里提出了自己的微服务解决方案DNS, D-Dubbo N-Nacos S-Sentinel

二、架构发展历程

1.架构演变

    大致分为6个阶段: 单体架构 -〉水平扩展 -〉垂直扩展 -〉RPC -〉soa -〉微服务
时至今日, SOA架构和微服务结构基本可以等同视之了, 下面将对不同架构作简要概述

1.1) 单体项目

    就是把项目中所涉及的模块( 门户系统、后台系统 )等统一打成一个jar包或者war包, 部署在一个tomcat实例里
    出现问题:
    1, 热点问题, 即某个子系统访问量大, 可能会导致其他子系统的访问出现问题
    2, 模块与模块之间的耦合度高, 相互影响( 修改某一个子系统的代码, 产生了问题,影响其他的子系统 )
    3, 维护部署成本高 ( 某次发布, 只更新了后台管理的模块, 但是因为是单体架构, 所以会发布整个系统 )
    4, 技术栈受限, 必须使用相同的编程语言开发不用的子系统

1.2) 水平扩展

    集群构建, 就是部署多个单体架构, 形成一个集群
        1, 提高了系统的稳定性和并发能力
        2, 一台节点出现了问题, 不会影响整个系统
    但是根本上没有解决单体架构的问题

1.3) 垂直结构

    把一个单体应用, 根据业务的边界进行了划分, 每个业务系统都独立部署在自己的tomcat中, 多个业务共享数据库等存储资源
    如把门户系统和后台系统进行拆分, 解决了部分单体架构的问题
解决了:
    1, 子系统之间的热点问题解决了, 但是模块的热点问题没有解决
    2,子系统之间耦合度降低, 独立更新和发版本
    3, 技术栈也不要求统一语言

1.4) RPC

    RPC : Remote Procedure Call 远程过程调用
    由垂直结构演变而来, 解决的是子系统间模块功能的调用
    如下门户系统中需要调用后台中系统中的订单服务,这种跨进程间该如何调用呢? 答案RPC在这里插入图片描述
    RPC的好处:
        复用其他系统的功能
        问题:
        1, 不能像调用本地方法那样进行调用
        如在Web门户系统内部增加一个订单模块,那么可以像调用本地方法那样调用OrderService.getOrderByOrderNo(), 这样的话就是重复造轮子, 那么现在需要调用另外一台服务器当中的订单模块, 这是跨虚拟机的, 就是跨进程的, 跨进程之间的调用是没有办法像调用本地方法那样的, 跨进程之间的调用必须通过网络来调用. ( 不管是一台机器还是多台机器 )即从web门户将请求参数通过网络传递给后台管理的订单模块, 订单模块处理完成再通过网络将处理结果返回给web门户
        2, 走网络调用,那么 1,通信方式 2, 协议 3, 序列化

1.5) SOA架构 Service Oriented Architecture 面向服务架构

    SOA是RPC架构的演化. 代表框架 Dubbo
    RPC存在的问题:
    如上用户自服务模块调用订单模块
    1, 如果后台系统的订单模块出现了问题怎么办?
    2, 订单模块访问量特别大怎么办?
    解决方式: 对订单模块进行水平扩展, 集群. 但是按照目前RPC的结构,无法完成针对订单模块的扩展,如果扩展只能子系统内全扩展
    SOA解决:
    把订单模块从后台系统中抽取出来, 成为一个服务
    即: 把订单模块的代码独立成一个新的module, 打成一个jar包, 部署在一个tomcat中. 可以分别对门户和后台系统共同提供服务
    避免单节点故障, 可以水平扩展, 部署多个订单服务形成一个集群
    如下已经将功能模块抽取出来并形成一个集群, 那么谁来管理这个集群呢? 所以服务治理应运而生

服务治理
    注册中心: 提供服务注册功能, 对服务集群进行管理, 监控集群每一个服务是否健康等
    负载均衡: 将调用订单服务的请求按照一定的算法调度给集群中的机器
    容错: 当一次请求走到了2号机器进行处理请求,此时2号机器出问题无法提供服务, 就会有容错机制切换到其他机器进行处理请求
    配置中心: 对集群中的服务的配置进行统一配置管理, 改一处集群服务均生效
    限流: 对集群的请求流量进行流量限制, 保证集群的稳定
所以: SOA = RPC + 服务治理 如果有异构系统, 再加个ESB

1.6) 企业服务总线 ESB Enterpraise-Service-Bus ( SOA架构的变种 )

    如下, 订单模块抽取服务后, 门户系统和后台系统都可以对其发起调用, 当然也有可能存在其他语言开发的系统(Go/PHP/Python)等开发的系统需要对其发起调用. 所以就会存在python调java的代码, 这里java是不认识python的,所以对于这种异构系统之间如何完成RPC的调用呢?
在这里插入图片描述
    解决: RPC是跨进程建的调用, 我们需要将数据在网络传输过程中, 把请求数据和返回数据转换成一种大家都认可的中间语言类型. 中间语言类型: GRpc Probuf 将数据类型都转换为 Probuf.Thrift IDL (java版) IDL也是支持不同语言的, 这种中间层即为ESB-企业服务总线, 可以把不同语言的数据类型转换成一种都认识的中间状态( 1, Probuf 2, IDL), 对于相同语言的, 如java也要同通过ESB. 即所有语言不论相同还是不同都要经过ESB
RPC的调用, 如果是同步的调用, 可以通过ESB进行处理, 异步的调用可以使用MQ进行处理.

1.7) 微服务架构

    微服务架构是SOA结构的升级, 在微服务架构系统中, 没有子系统了, 全部都拆解为服务化功能.
    微服务架构代表框架: Springcloud体系 ( Alibaba), 以Bubbo为代表的DNS体系

2.Dubbo环境搭建

基于Springboot2 + Dubbo2.7.3 提前安装好zookeeper注册中心
springboot中使用dubbo也是一样, 需要建立接口层、服务提供者、服务消费者

接口层

public interface SiteService {

    String getName(String name);
}

服务提供者
pom.xml

<!--接口层依赖-->
<dependency>
    <groupId>org.yuchi</groupId>
    <artifactId>myboot-dubbo-interface</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<!--dubbo依赖-->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.7.3</version>
</dependency>
<!--zookeeper依赖-->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-registry-zookeeper</artifactId>
    <version>2.7.3</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>

实现接口层实现类
@Service是apache.dubbo的, 2.7.4以后为@DubboService
功能:将以下实现类发布成RPC服务,暴漏SiteService接口,并将实现类SiteServiceImpl注入iOC容器中

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

@Service
public class SiteServiceImpl implements SiteService {

    @Override
    public String getName(String name) {
        return "name: " + name;
    }
}

application.yml

server:
  port: 9001

dubbo:
  application:
    name: myboot-dubbo-provider    // 服务名称
  registry:
    address: zookeeper://localhost:2181  // zookeeper注册中心地址
  protocol:
    name: dubbo    // dubbo协议
    port: 20882    // dubbo端口

启动类开启dubbo @EnableDubbo

@SpringBootApplication
@EnableDubbo
public class MybootDubboProviderApplication {

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

开启成功后,在注册中心可以发现存在了dubbo节点,且已经注册了com.yuchi.service.SiteService服务
在这里插入图片描述
服务消费者
pom依赖

<!--接口层依赖-->
<dependency>
    <groupId>org.yuchi</groupId>
    <artifactId>myboot-dubbo-interface</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<!--dubbo依赖-->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.7.3</version>
</dependency>
<!--zookeeper依赖-->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-registry-zookeeper</artifactId>
    <version>2.7.3</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>

application.yml

server:
  port: 9002

dubbo:
  application:
    name: myboot-dubbo-consumer
  registry:
    address: zookeeper://localhost:2181 注册中心地址

消费提供者接口

@RestController
@RequestMapping("site")
public class SiteController {
    // @Reference完成两件事:1,服务启动时向注册中心订阅siteService服务地址列表
    // 2, 生成siteService服务代理对象
    @Reference    // dubbo的Reference  2.7.4以后为@DubboReference
    private SiteService siteService;

    @GetMapping("getName")
    public String getName(String name){
        return siteService.getName("aaa");
    }
}

启动类开启dubbo

@SpringBootApplication
@EnableDubbo
public class MybootDubboConsumerApplication {

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

3.服务治理

3.1) 服务超时

    服务提供者和服务消费者都可以配置服务超时时间(在dubbo框架中默认是1s)
    服务提供者超时:执行暴漏的服务的超时时间。如果超时,则会打印超时日志(warn),但服务会正常执行完
    服务消费者超时:从发起服务调用到收到服务响应的整个过程的时间。如果超时,则进行重试,重试失败则抛出异常。

场景一: 服务消费者或服务提供者只有一方配置了超时时间,则默认是对双方都生效
如下是对服务提供者配置了超时时间:6秒,默认消费者也是6秒, 服务执行5秒,所以正常调用
消费者配置

@Service(version = "sync", timeout = 6000)
public class AsyncSiteServiceImpl implements SiteService {

    @Override
    public String getName(String name) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "sync" + name;
    }
}

场景二: 服务消费者或服务提供者都配置了超时时间
    如下消费者配置3秒,上提供者配置6秒,虽然提供者没有超时,但消费者超时了, 提供者超时时间配置4s,程序运行5s

@Service(version = "sync", timeout = 4000)
public class AsyncSiteServiceImpl implements SiteService {

    @Override
    public String getName(String name) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("aaa");
        return "sync" + name;
    }
}

消费者配置3s

@RestController
@RequestMapping("site")
public class SiteController {

    @Reference(version = "sync", timeout = 3000)
    private SiteService siteService;

    @GetMapping("/getName")
    public String getName(String name){
        return siteService.getName("aaa");
    }
}

结果:
1,提供者打印了warn日志,程序执行了3次 (消费者重试了三次),没有报错
    a)也就是说提供者即使是服务超时了,程序也是会执行完的
    b)提供者配置的超时时间是预期执行时间,即使超过了,程序仍然可以继续运行,会打印warn日志
2, 消费者根据自己配置超时时间判断,超时重试2次后仍然超时,则报错
dubbo请求超时后会,默认会重试2次,一共执行三次

3.2) 集群容错

集群容错: 指的是在集群调用失败时, Dubbo提供的容错方案
    1、默认使用的是: failover 失败自动切换, 重试其他服务器 ( 不是重试自己 ). 通常用于读操作( 读是幂等的 ), 但重试会带来更长延迟. 可通过 retries=“2” 来设置重试次数
    failover: 当对集群调用失败时, 会进行重试, 默认重试2次,一共三次调用. 会出现幂等性问题
虽然出现幂等性问题,但是依然推荐使用这种容错机制, 需要在业务层面解决幂等问题
    方案1: 把数据的业务id作为数据库的联合主键, 此时业务id不能重复
    方案2( 推荐 ): 使用分布式锁来解决重复消费问题.
    2、failfast: 快速失败. 只发起一次调用, 失败立即报错. 通常用于非幂等性的写操作, 比如新增数据等功能
    3、failsafe: 失败安全, 出现异常, 直接忽略. 常用语日志记录等操作
    4、failback: 失败自动恢复, 后台记录失败请求记录, 定时重发. 常用于消息通知操作
    5、forking: 并行调用多个服务器, 只要一个成功即返回. 通常用于实时性要求较高的读操作. 但需要浪费更多服务资源. 可通过forks=”2“来设 置最大并行数

3.3) 服务降级

    比如一个电商系统, 核心业务是登陆、搜索、加入购物车、下单、支付等, 非核心业务有评论、个性化推荐等. 那么在双11流量洪峰过来时, 应该把服务器的资源留给核心业务, 非核心业务就可以通过降级处理.
作用域: 消费者

@RestController
@RequestMapping("site")
public class SiteController {

    @Reference(version = "sync", timeout = 3000, mock = "force:return timeoutaaa")
    private SiteService siteService;

    @GetMapping("/getName")
    public String getName(String name){
        return siteService.getName("aaa");
    }
}

降级方式:
1、mock = “fail:return timeoutaaa”), 是消费者对提供者发起远程调用失败后的处理结果, 返回"timeoutaaa", 不抛异常, 用来容忍不重要服务不稳定时对调用方的影响
2、mock = “force:return timeoutaaa”, 是消费者只要调用该方法,不发起远程调用直接强制返回: timeoutaaa, 用来屏蔽不重要服务不可用时对调用方的影响

  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值