一、分布式基础

1.1 软件架构四大演变

演变顺序:单体 → 垂直 → 分布式 → 微服务

解释:架构进化本质:为了解决流量变大、代码变多、维护困难。

1)单体架构

所有模块一个工程,一个jar包,全部本地调用;

优点:简单;缺点:耦合严重、一处崩全崩。

通俗解释:所有代码写在一个项目里,商城、会员、订单全部在一起,打包就是一个文件。服务器挂一下,网站直接瘫痪。

2)垂直架构

按业务线拆分多个单体项目;解决宕机连锁问题,但是代码大量重复、无法复用。

通俗解释:把大项目拆成多个独立小项目,比如商城系统、后台管理系统分开部署;但是每个项目都要写一遍短信、文件上传工具,代码重复。

3)分布式架构

抽公共服务,分为web消费层、service提供层;第一次出现远程调用RPC,原生调用极其麻烦。

通俗解释:把重复代码单独抽出来做成服务,专门给别人调用;两个不同项目不在同一台电脑,必须网络传输,也就是远程调用。

4)微服务架构

粒度极致细化,一个功能一个服务;服务数量爆炸,必须使用服务治理框架(Dubbo/SpringCloud)。

通俗解释:拆分到极致,用户服务、订单服务、支付服务全部独立;服务太多人工管不过来,必须用框架治理。

1.2 RPC远程调用核心原理

1.2.1 什么是RPC

RPC:远程过程调用,跨JVM、跨服务器调用方法,像本地调用一样简单。

通俗解释:你代码看起来是调用本地方法,实际上调用的是另一台电脑上的代码,屏蔽网络底层。

1.2.2 RPC五大核心要素

  1. 客户端(消费者):需要调用别人服务的一方。

  2. 服务端(提供者):提供接口、被别人调用的一方。

  3. 序列化:Java对象不能直接网络传输,必须转成二进制字节。

  4. 网络传输:采用TCP长连接,稳定、可靠、适合业务调用。

  5. 动态代理:自动生成代理类,不用手写Socket、不用写网络代码。

1.2.3 RPC完整调用流程

消费者调用代理方法 → 序列化参数 → 网络传输 → 服务端反序列化 → 反射执行业务 → 返回结果序列化 → 消费者反序列化拿到数据。

通俗逐句解释:①你调用方法;②把对象变成字节;③通过网络发给服务端;④服务端把字节变回对象;⑤通过反射执行代码;⑥结果再次转字节传回;⑦客户端转回对象展示。

1.3 Dubbo 是什么

  • 高性能 RPC 框架,阿里开源,用于分布式服务调用
  • 核心:远程调用像本地调用一样简单
  • 解决:服务拆分、跨服务通信、服务治理

1.4 Dubbo整体架构五大角色

  1. Provider提供者:暴露服务,注册到注册中心。解释:写业务代码、对外提供接口的项目。

  2. Consumer消费者:订阅服务,调用远程接口。解释:需要使用别人接口、不写业务逻辑的项目。

  3. Registry注册中心:存放服务地址(ZK/Nacos)。解释:中介、通讯录,保存所有服务在哪台服务器。

  4. Monitor监控中心:统计调用次数、耗时、异常。解释:日志统计工具,看哪个接口慢、报错多。

  5. Container容器:Spring容器启动服务。解释:Dubbo依托Spring运行,本身不是独立服务器。

二、Dubbo环境搭建&快速入门

2.1 注册中心ZooKeeper

2.1.1 为什么选用ZK

节点临时特性、心跳检测、适合Dubbo服务注册;服务下线自动删除节点。

通俗解释:服务挂了,ZK自动检测、自动删掉失效地址,不会让消费者调用死掉的服务。

2.1.2 ZK存储结构

根节点/dubbo → 服务名节点 → 提供者节点、消费者节点、配置节点。

通俗解释:树形结构,类似文件夹,清晰保存每一个服务信息、IP、端口。

2.2 Dubbo管理控制台dubbo-admin

  • 作用:可视化查看服务、禁用服务、权重调整、路由配置。解释:网页后台,不用敲命令,肉眼查看服务状态。

  • 部署方式:war包部署tomcat、jar直接启动。解释:新版jar一键启动,老版本war包放tomcat。

  • 页面功能:服务治理、应用管理、流量管控。解释:上线灰度、临时禁用坏接口全部在这里操作。

2.3 第一个Dubbo入门案例

2.3.1 工程拆分规范

  1. 公共接口工程(api):存放所有服务接口、实体类。解释:专门放模板,提供者实现、消费者引用。

  2. 提供者工程(provider):实现接口、暴露服务。解释:干活的服务。

  3. 消费者工程(consumer):引用接口、调用远程服务。解释:请求方,不实现代码

2.3.2 【代码实操】依赖引入(pom.xml)

<!-- Dubbo核心依赖 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.6.2</version>
</dependency>
<!-- Zookeeper连接依赖 -->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.10</version>
</dependency>
<dependency>
    <groupId>com.github.sgroschupf</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.1</version>
</dependency>

2.3.3 【代码实操】公共API接口类

/**
 * 公共服务接口(api工程)
 * 提供者实现、消费者引用
 */
public interface UserService {
    String sayHello(String name);
}

2.3.4 【代码实操】提供者实现类+XML配置

业务实现类:

public class UserServiceImpl implements UserService {
    @Override
    public String sayHello(String name) {
        return "Dubbo调用成功:"+name;
    }
}

提供者dubbo-provider.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://dubbo.apache.org/schema/dubbo
       http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- 1.当前服务应用名称 -->
    <dubbo:application name="dubbo-provider"/>
    <!-- 2.注册中心地址(ZK默认端口2181) -->
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <!-- 3.通信协议及端口 默认20880 -->
    <dubbo:protocol name="dubbo" port="20880"/>
    <!-- 4.暴露服务接口 -->
    <dubbo:service interface="com.lwl.service.UserService" ref="userService"/>
    <!-- 5.注入实现类 -->
    <bean id="userService" class="com.lwl.service.impl.UserServiceImpl"/>
</beans>

2.3.5 【代码实操】消费者XML配置+调用测试

消费者dubbo-consumer.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://dubbo.apache.org/schema/dubbo
       http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- 应用名称 -->
    <dubbo:application name="dubbo-consumer"/>
    <!-- 注册中心 -->
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
    <!-- 引用远程服务 生成代理对象 -->
    <dubbo:reference id="userService" interface="com.lwl.service.UserService"/>
</beans>

测试启动类:

public class ConsumerTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("dubbo-consumer.xml");
        UserService userService = context.getBean(UserService.class);
        // 像本地方法一样调用远程服务
        String result = userService.sayHello("Dubbo快速入门学习");
        System.out.println(result);
    }
}

三、Dubbo高级配置大全

3.1 SpringBoot整合Dubbo三种方式

  1. XML配置方式:老式写法,企业遗留项目多。解释:早期Spring写法,现在新项目不用。

  2. 注解方式(主流):@DubboService、@DubboReference。解释:最简单、现在企业通用。

  3. JavaConfig配置类:纯代码无xml。解释:大型框架底层使用。

3.1.1 【代码实操】SpringBoot注解版

① 提供者 application.yml 配置

# 服务名称
dubbo:
  application:
    name: dubbo-boot-provider
  # 注册中心
  registry:
    address: zookeeper://127.0.0.1:2181
  # 通信协议
  protocol:
    name: dubbo
    port: 20880
  # 扫描注解包
  scan:
    base-packages: com.lwl.service
# 关闭日志冗余打印
logging:
  level:
    root: info

② 消费者 application.yml 配置

dubbo:
  application:
    name: dubbo-boot-consumer
  registry:
    address: zookeeper://127.0.0.1:2181
  # 开发环境关闭启动检查
  consumer:
    check: false
server:
  port: 8080

③ 提供者启动类+服务实现:

// 提供者实现类 暴露服务
@DubboService // 新版注解 暴露服务
public class UserServiceImpl implements UserService {
    @Override
    public String sayHello(String name) {
        return "SpringBoot+Dubbo注解调用成功";
    }
}
// 启动类
@EnableDubbo
@SpringBootApplication
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class,args);
    }
}

④ 消费者引用:

@RestController
public class UserController {
    // 引用远程服务
    @DubboReference
    private UserService userService;

    @GetMapping("/hello")
    public String hello(){
        return userService.sayHello("注解方式");
    }
}

3.2 dubbo.properties全局配置

  • 统一配置公共属性:超时、重试、注册中心。解释:把重复配置抽出来,不用每个类写一遍。

  • 属性加载优先级(从高到低):①启动命令(-D) ②xml/注解配置 ③dubbo.properties。

3.2.1 【代码实操】dubbo.properties全局配置文件

# 应用名称
dubbo.application.name=dubbo-demo
# 注册中心
dubbo.registry.address=zookeeper://127.0.0.1:2181
# 全局超时
dubbo.provider.timeout=3000
# 全局重试次数
dubbo.provider.retries=1

3.3 启动检查check

  • 默认true:消费者启动必须检测服务是否存在,不存在启动报错。解释:生产环境必须保证服务齐全。

  • 开发环境常用:check=false,避免服务未启动导致报错。解释:开发时服务没写完,不用卡死启动。

3.3.1 【代码实操】关闭启动检查

① XML写法

<dubbo:reference id="userService" 
                 interface="com.lwl.service.UserService" 
                 check="false"/>

② .properties 写法

# 消费者关闭启动检查(全局默认)
dubbo.consumer.check=false

3.4 超时配置timeout

  • 默认1000ms,超时直接抛出异常。解释:一秒没返回直接判定失败。

  • 消费者超时优先级高于提供者。解释:调用方说了算,防止服务端卡死。

  • 业务耗时较长必须手动加大超时时间。解释:报表、导出、大数据查询必须调大时间。

3.4.1 【代码实操】超时配置(XML + properties 双版本)

① XML写法(消费者局部配置、优先级最高)

<!-- 消费者设置超时5秒 -->
<dubbo:reference id="userService" timeout="5000">

② dubbo.properties 写法

# 消费者全局超时时间 单位ms
dubbo.consumer.timeout=5000
# 提供者全局超时(消费者没配置才生效)
dubbo.provider.timeout=3000

优先级

1)、精确优先(方法级优先,接口级次之,全局配置再次之)

2)、消费者设置优先(如果级别一样,则消费方优先,提供方次之)

3.5 重试次数retries

  • 默认重试2次,加上第一次一共3次请求。解释:失败自动换服务器再试。

  • 幂等接口可以重试:查询、删除。解释:查多少次、删多少次结果都一样,不会出错。

  • 非幂等禁止重试:下单、扣款(防止重复下单)。解释:扣款重试会扣多遍钱,绝对禁止。

3.5.1 【代码实操】关闭重试(XML + properties 双版本)

① XML写法(单个接口禁用重试)

<dubbo:reference id="payService" retries="0"/>

② dubbo.properties 全局写法(统一全局重试次数)

# 全局重试次数 0=关闭重试
dubbo.consumer.retries=2
# 提供者重试配置
dubbo.provider.retries=2

3.6 多版本version

  • 用于灰度发布、平滑升级。解释:新版本怕bug,先少量人测试。

  • 老版本服务不升级,新版本并行运行,逐步切流。解释:新旧服务共存,不会一次性全量更新。

  • 消费者指定版本调用,互不干扰。解释:A用户调用旧版、B用户调用新版。

3.6.1 【代码实操】版本控制(XML + properties 双版本)

① XML写法

提供者:

<dubbo:service interface="com.lwl.service.UserService" 
               ref="userService" version="1.0.0"/>

消费者指定版本:

<dubbo:reference interface="com.lwl.service.UserService"  id="userService" version="1.0.0"/>

② .properties 配置

# 提供者全局版本号
dubbo.provider.version=1.0.0
# 消费者引用默认版本
dubbo.consumer.version=1.0.0

灰度发布优先XML单独指定版本,properties用来统一默认版本。

3.7 本地存根stub

  • 客户端前置增强,在调用远程方法之前执行。解释:还没发网络请求,先在本地执行代码。

  • 用途:参数校验、本地缓存、日志打印、权限判断。解释:简单判断不用浪费网络请求。

  • 不侵入业务代码,增强扩展性。解释:不用改业务代码就能加功能。

3.7.1 【代码实操】本地存根编写

① 本地存根 Java代码

public class UserServiceStub implements UserService {
    // 注入远程代理对象
    private UserService userService;
    public UserServiceStub(UserService userService){
        this.userService = userService;
    }
    @Override
    public String sayHello(String name) {
        // 本地前置校验
        if(name == null){
            return "参数不能为空";
        }
        // 正常远程调用
        return userService.sayHello(name);
    }
}

XML配置(消费者配置,绑定存根类)

<!-- stub:绑定本地存根类,客户端优先执行存根代码 -->
<dubbo:reference id="userService" 
                 interface="com.lwl.service.UserService"
                 stub="com.lwl.stub.UserServiceStub"/>

总结:

1、存根必须放在消费者端,提供者不需要;

2、必须写有参构造,Dubbo自动注入远程代理;

3、执行顺序:本地存根 → 远程调用

3.8 SpringBoot与dubbo整合的三种方式

  • 1)导入dubbo-starter,在application.properties配置属性,使用@Service【暴露服务】使用@Reference【引用服务】

  • 2)保留dubbo xml配置文件; 导入dubbo-starter,使用@ImportResource导入dubbo的配置文件即可 

  • 3)使用注解API的方式: 将每一个组件手动创建到容器中,让dubbo来扫描其他的组件  //@EnableDubbo //开启基于注解的dubbo功能

package com.lwl.config;

import com.lwl.service.UserService;
import com.lwl.service.impl.UserServiceImpl;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * P19 API编码配置方式
 */
@Configuration
public class MyDubboConfig {

    /**
     * 1.应用配置
     */
    @Bean
    public ApplicationConfig applicationConfig(){
        ApplicationConfig applicationConfig = new ApplicationConfig();
        applicationConfig.setName("dubbo-api-provider");
        return applicationConfig;
    }

    /**
     * 2.注册中心配置
     */
    @Bean
    public RegistryConfig registryConfig(){
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("zookeeper://127.0.0.1:2181");
        return registryConfig;
    }

    /**
     * 3.手动暴露服务
     */
    @Bean
    public ServiceConfig<UserService> serviceConfig(UserService userService){
        ServiceConfig<UserService> serviceConfig = new ServiceConfig<>();
        // 设置接口
        serviceConfig.setInterface(UserService.class);
        // 设置实现类
        serviceConfig.setRef(userService);
        // 设置版本
        serviceConfig.setVersion("1.0.0");

        // 方法级配置
        List<MethodConfig> methods = new ArrayList<MethodConfig>();
        MethodConfig method = new MethodConfig();
        method.setName("sayHello");
        method.setTimeout(10000);
        method.setRetries(0);
        methods.add(method);
        serviceConfig.setMethods(methods);
        return serviceConfig;
    }
}

四、Dubbo高可用机制

4.1 ZK宕机故障转移

  • 注册中心宕机:消费者本地缓存服务列表,不影响正常调用。通俗解释:消费者把服务地址存在本地内存,ZK崩了照样干活。

  • ZK恢复后自动重新同步服务。解释:重启ZK后自动更新最新服务列表。

  • 直连模式:开发调试直接指定提供者IP,绕过注册中心。解释:开发阶段不用启动ZK,直接写死IP调试。

4.1.1 【代码实操】直连模式

<!-- 绕过ZK 直连指定服务地址 -->
<dubbo:reference id="userService" 
                 interface="com.lwl.service.UserService" 
                 url="dubbo://127.0.0.1:20880"/>
@Reference(url="127.0.0.1:20880")
UserService userService;

4.2 四大负载均衡策略

4.2.1 Random随机(默认)

随机挑选节点,简单高效,生产默认。解释:压力分配均匀、不会集中压在一台服务器。

4.2.2 RoundRobin轮询

依次轮流调用,适合性能一致服务器。解释:1号、2号、3号轮流请求,服务器配置一样时使用。

4.2.3 LeastActive最少活跃

优先调用耗时低、并发少的节点,自动避开慢节点。解释:哪台服务器轻松就去哪台,自动规避卡顿机器。

4.2.4 ConsistentHash一致性哈希

相同参数永远访问同一节点,会话保持、缓存命中高。解释:同一个用户永远访问同一台服务,缓存不会失效。

4.2.5 【代码实操】修改负载均衡策略

<!-- 改为轮询负载均衡 -->
<dubbo:reference id="userService"
 interface="com.lwl.service.UserService"  loadbalance="roundrobin"/>
@Reference(loadbalance="roundrobin")
UserService userService;

4.3 服务降级Mock

  • 失败降级:调用失败触发本地mock返回兜底数据。解释:服务崩了,返回写死的默认数据,页面不报错。

  • 强制降级:直接禁用远程服务,全部走本地mock。解释:高峰期直接关掉非核心服务,保住主业务。

  • 场景:流量洪峰、服务宕机、非核心业务降级保主流程。通俗解释:双十一限流,把公告、积分关掉,保住下单付款。

4.4 集群容错模式

4.4.1 Failover失败重试(默认)

失败自动换节点重试,适合幂等查询接口。解释:查数据失败换一台重试,最常用、默认模式。

4.4.2 Failfast快速失败

只调用一次,失败直接报错,适合下单扣款非幂等接口。解释:支付不能重试,失败直接抛异常。

4.4.3 Failsafe安全失败

失败不报错、返回空,用于日志、统计无关紧要接口。解释:错了也不影响主业务,不用报错。

4.4.4 Failback失败通知

失败后台异步重试,用于消息推送、通知类业务。解释:发短信失败,后台慢慢重试,不用用户等待。

4.4.5 Forking并行调用

同时调用多个节点,取最快返回,高可用、耗资源。解释:同时请求3台服务器,谁快用谁,适合金融高可靠业务。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐