通关微服务-dubbo
Dubbo是一个开源的高性能Java RPC框架,用于快速开发高性能的服务。
分布式系统中相关概念
大型互联网项目架构特点
- 用户多
- 流量大,并发高
- 海量数据
- 易受攻击
- 功能繁琐
- 变更快
大型互联网项目架构目标
- 高性能:提供快速的访问体验
- 高可用:网站服务一直可以正常访问
- 可伸缩:通过硬件增加/减少,提高/降低处理能力
- 高可扩展:系统间耦合低,方便通过新增/移除方式,增加/减少功能/模块
- 安全性:提供网站安全访问和数据加密,安全存储等策略
- 敏捷性:随需应变,快速响应
集群和分布式
- 集群:很多"人"一起,干一样的事
- 一个业务模块,部署在多台服务器上。
- 分布式:很多"人"一起,干不一样的事。这些不一样的事,合起来是一件大事
- 一个大的业务系统,拆分为小的业务模块,分别部署在不同机器上。
(eg.饭店,厨师,洗菜、切菜、炒菜)
架构演进
单体架构
优点:
- 简单:开发部署都很方便,小型项目首选
缺点:
- 项目启动慢
- 可靠性差
- 可伸缩性差
- 扩展性和可维护性差
- 性能低
垂直架构
在某个领域细分就是垂直的意思。 垂直架构是指单体架构中的多个模块拆分为多个独立的项目。行程多个独立的单体架构。

垂直架构存在的问题:
- 重复功能多
分布式架构
分布式架构是指在垂直架构的基础上,将公共业务模块抽取出来,作为独立的服务,供其他调用者消费,以实现服务的共享和重用。
RPC:Remote Procedure Call远程过程调用。有非常多的协议和技术都来实现了RPC过程。比如:HTTP REST风格,Java RMI规范等等。

分布式架构存在的问题:
- 服务的提供方一旦产生变更,所有消费方都需要变更
SOA架构
![[Pasted image 20240317145745.png]]
SOA:Service-Oriented Architecture,面向服务的架构。是一个组件模型,它将应用程序的不同功能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和契约联系起来。
ESB:Enterparise Service Bus企业服务总线,服务中介。主要提供了一个服务于服务之间的交互。ESB包含的功能包含:负载均衡,流量控制,加密处理,服务的监控,异常处理,监控告急等等。
微服务架构

微服务架构是在SOA上做的升华,微服务架构强调的一个重点是"业务需要彻底的组件化和服务化",原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。
微服务架构 = 80%SOA服务架构思想 + 100%的组件化思想 + 80%的领域建模思想。
特点:
- 服务实现组件化:开发者可以自由选择开发技术,也不需要协调其他团队
- 服务之间交互一般采用REST API
- 去中心化:每个微服务有自己私有的数据库持久化业务数据
- 自动化部署:把应用拆分成为一个一个独立的单个服务,方便自动化部署、测试、运维
总结

Dubbo概述
Dubbo概念
- Dubbo是阿里开源的一个高性能、轻量级的Java RPC框架,现已收录到apache了。
- 致力于提供高性能和透明化的RPC,已经SOA服务治理方案。
- dubbo官网
Dubbo架构
编号标注了使用流程,很清晰 0.start:服务的提供者需要运行在容器里,比如tomcat里。 1.register:服务启动后,会注册到注册中心,相当于把服务的IP、端口等信息放到注册中心上。 2.subscribe:消费者想要调用服务,就需要订阅,向注册中心要,把服务提供者的IP、端口地址给我。 3.notify:消费者向注册中心要,注册中心提供信息。 4.invoke:这个时候消费者就拿到了目标服务的信息,就可以调用了,就是RPC的过程。这个过程不用管,这是dubbo内部自动实现。 5.monitor:管理者,是做服务监控的,比如某个服务调用了多少次。
Dubbo快速入门
Zookeeper安装
其实zookeeper就是dubbo推荐使用的注册中心。
Dubbo快速入门

启动注册中心
对于一个微服务化的应用来说,注册中心是不可或缺的一个组件。只有通过注册中心,消费端才可以成功发现服务端的地址信息,进而进行调用。通过官方教程获取一个基于 Apache Zookeeper 注册中心的简易启动器。
Windows:
git clone --depth=1 --branch master git@github.com:apache/dubbo-samples.git
cd dubbo-samples
./mvnw.cmd clean compile exec:java -pl tools/embedded-zookeeper
Linux / MacOS:
git clone --depth=1 --branch master git@github.com:apache/dubbo-samples.git
cd dubbo-samples
./mvnw clean compile exec:java -pl tools/embedded-zookeeper
Docker:
docker run --name some-zookeeper -p 2181:2181 --restart always -d zookeeper初始化项目

如上图所示,可以建立一个基础的项目。
搭建了基础项目之后,我们还需要创建 dubbo-spring-boot-demo-interface 、dubbo-spring-boot-demo-provider 和 dubbo-spring-boot-demo-consumer 三个子模块。
创建了三个子模块之后,需要创建一下几个文件夹:
- 在
dubbo-spring-boot-demo-consumer/src/main/java下创建org.apache.dubbo.springboot.demo.consumerpackage - 在
dubbo-spring-boot-demo-interface/src/main/java下创建org.apache.dubbo.springboot.demopackage - 在
dubbo-spring-boot-demo-provider/src/main/java下创建org.apache.dubbo.springboot.demo.providerpackage

添加Maven依赖
在初始化完项目以后,我们需要先添加 Dubbo 相关的 maven 依赖。
对于多模块项目,首先需要在父项目的 pom.xml 里面配置依赖信息。

编辑 ./pom.xml 这个文件,添加下列配置。
<properties>
<dubbo.version>3.2.0-beta.4</dubbo.version>
<spring-boot.version>2.7.8</spring-boot.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>然后在 dubbo-spring-boot-consumer 和 dubbo-spring-boot-provider 两个模块 pom.xml 中进行具体依赖的配置。

编辑 ./dubbo-spring-boot-consumer/pom.xml 和 ./dubbo-spring-boot-provider/pom.xml 这两文件,都添加下列配置。
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-samples-spring-boot-interface</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<type>pom</type>
<exclusions>
<exclusion>
<artifactId>slf4j-reload4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- spring boot starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>在这份配置中,定义了 dubbo 和 zookeeper(以及对应的连接器 curator)的依赖。
添加了上述的配置以后,可以通过 IDEA 的 Maven - Reload All Maven Projects 刷新依赖
定义服务接口
服务接口 Dubbo 中沟通消费端和服务端的桥梁。

在 dubbo-spring-boot-demo-interface 模块的 org.apache.dubbo.samples.api 下建立 DemoService 接口,定义如下:
package org.apache.dubbo.springboot.demo;
public interface DemoService {
String sayHello(String name);
}在 DemoService 中,定义了 sayHello 这个方法。后续服务端发布的服务,消费端订阅的服务都是围绕着 DemoService 接口展开的。
定义服务端实现
定义了服务接口之后,可以在服务端这一侧定义对应的实现,这部分的实现相对于消费端来说是远端的实现,本地没有相关的信息。

在dubbo-spring-boot-demo-provider 模块的 org.apache.dubbo.samples.provider 下建立 DemoServiceImpl 类,定义如下:
package org.apache.dubbo.springboot.demo.provider;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.springboot.demo.DemoService;
@DubboService
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
return "Hello " + name;
}
}在 DemoServiceImpl 中,实现了 DemoService 接口,对于 sayHello 方法返回 Hello name。
注:在DemoServiceImpl 类中添加了 @DubboService 注解,通过这个配置可以基于 Spring Boot 去发布 Dubbo 服务。
配置服务端配置文件

在 dubbo-spring-boot-demo-provider 模块的 resources 资源文件夹下建立 application.yml 文件,定义如下:
dubbo:
application:
name: dubbo-springboot-demo-provider
protocol:
name: dubbo
port: -1
registry:
address: zookeeper://${zookeeper.address:127.0.0.1}:2181在这个配置文件中,定义了 Dubbo 的应用名、Dubbo 协议信息、Dubbo 使用的注册中心地址。
配置消费端配置文件

在 dubbo-spring-boot-demo-consumer 模块的 resources 资源文件夹下建立 application.yml 文件,定义如下:
dubbo:
application:
name: dubbo-springboot-demo-consumer
protocol:
name: dubbo
port: -1
registry:
address: zookeeper://${zookeeper.address:127.0.0.1}:2181在这个配置文件中,定义了 Dubbo 的应用名、Dubbo 协议信息、Dubbo 使用的注册中心地址。
配置服务端启动类

在 dubbo-spring-boot-demo-provider 模块的 org.apache.dubbo.springboot.demo.provider 下建立 Application 类,定义如下:
package org.apache.dubbo.springboot.demo.provider;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}在这个启动类中,配置了一个 ProviderApplication 去读取我们前面第 6 步中定义的 application.yml 配置文件并启动应用。
配置消费端启动类

在 dubbo-spring-boot-demo-consumer 模块的 org.apache.dubbo.springboot.demo.consumer 下建立 Application 类,定义如下:
package org.apache.dubbo.springboot.demo.consumer;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}在这个启动类中,配置了一个 ConsumerApplication 去读取我们前面第 7 步中定义的 application.yml 配置文件并启动应用。
配置消费端请求任务

在 dubbo-spring-boot-demo-consumer 模块的 org.apache.dubbo.springboot.demo.consumer 下建立 Task 类,定义如下:
package org.apache.dubbo.springboot.demo.consumer;
import java.util.Date;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.springboot.demo.DemoService;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Task implements CommandLineRunner {
@DubboReference
private DemoService demoService;
@Override
public void run(String... args) throws Exception {
String result = demoService.sayHello("world");
System.out.println("Receive result ======> " + result);
new Thread(()-> {
while (true) {
try {
Thread.sleep(1000);
System.out.println(new Date() + " Receive result ======> " + demoService.sayHello("world"));
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}).start();
}
}在 Task 类中,通过@DubboReference 从 Dubbo 获取了一个 RPC 订阅,这个 demoService 可以像本地调用一样直接调用。在 run方法中创建了一个线程进行调用。
启动应用

首先是启动 org.apache.dubbo.samples.provider.Application ,等待一会出现如下图所示的日志(Current Spring Boot Application is await)即代表服务提供者启动完毕,标志着该服务提供者可以对外提供服务了。
[Dubbo] Current Spring Boot Application is await...然后是启动org.apache.dubbo.samples.client.Application ,等待一会出现如下图所示的日志(Hello world )即代表服务消费端启动完毕并调用到服务端成功获取结果。

Receive result ======> Hello worldDubbo高级特性
序列化

- dubbo内部已经将序列化和反序列化的过程内部封装了
- 只需要在定义pojo类时实现Serializable接口即可
- 一般会定义一个公共的pojo模块,让生产者和消费者都依赖该模块
地址缓存

超时

- 服务消费者在调用服务提供者的时候发生了阻塞、等待的情形,服务消费者会一直等待下去。
- 在峰值时刻,大量请求同时到达,会造成线程的大量堆积,会造成服务雪崩。
- dubbo利用超时机制来解决,设置一个超时时间,在时间段内无法完成访问,则自动断开。
- 使用timeout属性配置超时时间,默认值是1000,单位毫秒。
注:推荐配置在服务的提供方。
重试

- 如果出现了网络抖动,则这一次请求就会失败。
- dubbo提供重试机制来避免类似问题。
- 通过retries属性来设置重试次数,默认是2次。
多版本


dubbo中使用version属性来设置和调用同一个接口的不同版本。
负载均衡

负载均衡策略:
- Random:按权重随机。
- RoundRobin:按权重轮询。
- LeastActive:最少活跃调用,相同活跃是随机。
- ConsistentHash:一致性Hash,相同参数额请求总是发到同一个提供者。
集群容错

集群容错模式:
- Failover Cluster:失败重试。默认值。当出现失败,重试其他服务器,默认重试2次,使用retries配置,一般用于。
- Failfast Cluster:快速失败,只发起一次调用,失败立即报错。通常用于。
- Failsafe Cluster:失败安全,出现异常时,直接忽略。返回一个空结果。
- Failback Cluster:失败自动回复,后台记录失败请求时,定时重发。
- Forking Cluster:并行调用多个服务器,只要一个成功即返回。比较耗费性能。
- Broadcast Cluster:广播调用所有提供者,逐个调用,任意一台报错则报错。同步要求性比较高的情形。
服务降级

在B上,同时存在广告服务,日志服务,支付服务。很明显支付服务较为重要,当B机器cpu达到90%时,除了定位异常外,最先考虑的应该是服务降级,要确保支付服务正常,不重要的广告服务、日志服务可以先停掉。这就是服务降级。
dubbo服务降级方式:
mock=force:return null表示消费方对该服务的方法调用都直接返回null,不发起远程调用。用来屏蔽不重要服务可不用时对调用方的影响。mock=fail:return null表示消费方对该服务的方法调用在失败后,再返回null,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。