架构演变
基础知识
性能指标
响应时间:指执行一个请求从开始到最后收到响应数据所花费的总体时间。
并发数:指系统同时能处理的请求数量。
并发连接数:指的是客户端向服务器发起请求,并建立了TCP连接。每秒钟服务器连接的总TCP数量
请求数:也称为QPS(Query Per Second) 指每秒多少请求.
并发用户数:单位时间内有多少用户
吞吐量:指单位时间内系统能处理的请求数量。
QPS:Query Per Second 每秒查询数。
TPS:Transactions Per Second 每秒事务数。
一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。
架构目标
高性能:提供快速的访问体验。
高可用:网站服务一直可以正常访问。
可伸缩:通过硬件增加/减少,提高/降低处理能力。
高可扩展:系统间耦合低,方便通过新增/移除方式,增加/减少新的功能/模块。
安全性:提供网站安全访问和数据加密,安全存储等策略。
敏捷性:随需应变,快速响应。
相关概念
集群:很多“人”一起 ,干一样的事。
- 一个业务模块,部署在多台服务器上。
分布式:很多“人”一起,干不一样的事。这些不一样的事,合起来是一件大事。
- 一个大的业务系统,拆分为小的业务模块,分别部署在不同的机器上。
单体架构
优点:
- 简单:开发部署都很方便,小型项目首选
缺点:
- 项目启动慢、可靠性差、可伸缩性差、扩展性和可维护性差、性能低
垂直架构
将单体架构中的多个模块拆分为多个独立的项目。形成多个独立的单体架构。
垂直架构存在的问题:重复功能太多(例如图中 E 功能)
分布式架构
在垂直架构的基础上,将公共业务模块抽取出来,作为独立的服务,供其他调用者消费,以实现服务的共享和重用。
分布式架构存在的问题:服务提供方地址一旦产生变更,所有消费方都需要变更。
SOA架构
SOA:(Service-Oriented Architecture,面向服务的架构)是一个组件模型,它将应用程序的不同功能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和契约联系起来。
ESB:(Enterparise Servce Bus) 企业服务总线,服务中介。主要是提供了一个服务于服务之间的交互。ESB 包含的功能如:负载均衡,流量控制,加密处理,服务的监控,异常处理,监控告急等等
微服务架构
微服务架构是在 SOA 上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。
微服务架构 = 80%的SOA服务架构思想 + 100%的组件化架构思想 + 80%的领域建模思想
特点:
服务实现组件化:开发者可以自由选择开发技术。也不需要协调其他团队
服务之间交互一般使用REST API
去中心化:每个微服务有自己私有的数据库持久化业务数据
自动化部署:把应用拆分成为一个一个独立的单个服务,方便自动化部署、测试、运维
Dubbo 简介
Apache Dubbo 是一款易用、高性能的 WEB 和 RPC 框架,同时为构建企业级微服务提供服务发现、流量治理、可观测、认证鉴权等能力、工具与最佳实践。
Dubbo 作为一款微服务框架,最重要的是向用户提供跨进程的 RPC 远程调用能力。如上图所示,Dubbo 的服务消费者(Consumer)通过一系列的工作将请求发送给服务提供者(Provider)。
为了实现这样一个目标,Dubbo 引入了注册中心(Registry)组件,通过注册中心,服务消费者可以感知到服务提供者的连接方式,从而将请求发送给正确的服务提供者。
Dubbo 快速入门
1.启动注册中心 ZooKeeper
要启动 ZooKeeper,您需要一个配置文件。这是一个示例,在 conf/zoo 中创建它.cfg:
tickTime=2000
dataDir=/Users/jianjian/JavaSoft/apache-zookeeper-3.8.1-bin/data/
clientPort=2181
启动ZooKeeper:
bin/zkServer.sh start
2.初始化项目
请参考:Dubbo Spring Boot Starter 开发微服务应用-初始化项目
3.添加 Maven 依赖
在初始化完项目以后,我们需要先添加 Dubbo 相关的 maven 依赖。
对于多模块项目,首先需要在父项目的 pom.xml
里面配置依赖信息。
<properties>
<dubbo.version>3.2.0</dubbo.version>
<spring-boot.version>2.7.8</spring-boot.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</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>
<!-- interface -->
<dependency>
<groupId>org.example</groupId>
<artifactId>dubbo-spring-boot-demo-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 刷新依赖
4.定义服务接口
服务接口沟通消费端和服务端的桥梁。
在 dubbo-spring-boot-demo-interface
模块的 org.apache.dubbo.springboot.demo
下建立 DemoService
接口,定义如下:
package org.apache.dubbo.springboot.demo;
public interface DemoService {
String sayHello(String name);
}
在 DemoService
中,定义了 sayHello
这个方法。后续服务端发布的服务,消费端订阅的服务都是围绕着 DemoService
接口展开的。
5. 定义服务端的实现
定义了服务接口之后,可以在服务端这一侧定义对应的实现,这部分的实现相对于消费端来说是远端的实现,本地没有相关的信息。
在dubbo-spring-boot-demo-provider
模块的 org.apache.dubbo.springboot.demo.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 服务。
6.配置服务端 YAML 配置文件
从本步骤开始至第 7 步,将会通过 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://localhost:2181
在这个配置文件中,定义了 Dubbo 的应用名、Dubbo 协议信息、Dubbo 使用的注册中心地址。
7. 配置消费端 YAML 配置文件
同样的,我们需要创建消费端的配置文件。
在 dubbo-spring-boot-demo-consumer
模块的 resources
资源文件夹下建立 application.yml
文件,定义如下:
dubbo:
application:
name: dubbo-springboot-demo-consumer
protocol:
name: dubbo
port: -1
registry:
address: zookeeper://localhost:2181
在这个配置文件中,定义了 Dubbo 的应用名、Dubbo 协议信息、Dubbo 使用的注册中心地址。
8. 基于 Spring 配置服务端启动类
除了配置 Yaml 配置文件之外,我们还需要创建基于 Spring Boot 的启动类。
首先,我们先创建服务端的启动类。
在 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
配置文件并启动应用。
9. 基于 Spring 配置消费端启动类
同样的,我们需要创建消费端的启动类。
在 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
配置文件并启动应用。
10. 配置消费端请求任务
除了配置消费端的启动类,我们在 Spring Boot 模式下还可以基于CommandLineRunner
去创建
在 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
方法中创建了一个线程进行调用。
11. 启动应用
截止第 10 步,代码就已经开发完成了,本小节将启动整个项目并进行验证。
首先是启动 org.apache.dubbo.springboot.demo.provider.Application
,等待一会出现如下图所示的日志(Current Spring Boot Application is await
)即代表服务提供者启动完毕,标志着该服务提供者可以对外提供服务了。
[Dubbo] Current Spring Boot Application is await...
然后是启动org.apache.dubbo.springboot.demo.consumer.Application
,等待一会出现如下图所示的日志(Hello world
)即代表服务消费端启动完毕并调用到服务端成功获取结果。
Receive result ======> Hello world
Dubbo Admin
安装
参考:两种方式安装dubbo-admin、zookeeper、M1 Mac下使用Docker安装zookeeper、dubbo-admin
准备工作
参考:docker安装zookeeper、Docker安装Zookeeper教程(超详细)
- 安装 zookeeper
docker search zookeeper
docker pull zookeeper
docker images # 查看下载的本地镜像
docker inspect zookeeper # 查看zookeeper详细信息
- 创建ZooKeeper 挂载目录(数据挂载目录、配置挂载目录和日志挂载目录)
mkdir -p /mydata/zookeeper/data # 数据挂载目录
mkdir -p /mydata/zookeeper/conf # 配置挂载目录
mkdir -p /mydata/zookeeper/logs # 日志挂载目录
- 启动ZooKeeper容器
docker run -d --name zookeeper --privileged=true -p 2181:2181 -v /mydata/zookeeper/data:/data -v /mydata/zookeeper/conf:/conf -v /mydata/zookeeper/logs:/datalog zookeeper
参数说明
-d # 表示在一直在后台运行容器
--name # 设置创建的容器名称
-p 2181:2181 # 对端口进行映射,将本地2181端口映射到容器内部的2181端口
-v # 将本地目录(文件)挂载到容器指定目录;
--restart always #始终重新启动zookeeper,看需求设置不设置自启动
- 查看容器
docker ps
- 添加ZooKeeper配置文件,在挂载配置文件目录(/mydata/zookeeper/conf)下,新增zoo.cfg 配置文件,配置内容如下:
dataDir=/data # 保存zookeeper中的数据
clientPort=2181 # 客户端连接端口,通常不做修改
dataLogDir=/datalog
tickTime=2000 # 通信心跳时间
initLimit=5 # LF(leader - follower)初始通信时限
syncLimit=2 # LF 同步通信时限
autopurge.snapRetainCount=3
autopurge.purgeInterval=0
maxClientCnxns=60
standaloneEnabled=true
admin.enableServer=true
server.1=localhost:2888:3888;2181
- 启动zk客户端
# 进入zookeeper 容器内部
docker exec -it zookeeper /bin/bash
# 检查容器状态
docker exec -it zookeeper /bin/bash ./bin/zkServer.sh status
# 进入控制台
docker exec -it zookeeper zkCli.sh
docker 安装
- 拉取镜像
docker search dubbo-admin
docker pull apache/dubbo-admin
- 创建容器
docker run -d --name dubbo-admin -p 8081:8080 -e admin.registry.address=zookeeper://ip地址:2181 -e admin.config-center=zookeeper://ip地址:2181 -e admin.metadata-report.address=zookeeper://ip地址:2181 --restart=always apache/dubbo-admin
- 访问dubbo-admin
访问地址: http://ip地址:8081
,用户名/密码:root/root
手动安装
Dubbo 高级特性
序列化
dubbo 内部已经将序列化和反序列化的过程内部封装了
我们只需要在定义pojo类时实现Serializable接口即可
一般会定义一个公共的pojo模块,让生产者和消费者都依赖该模块。
地址缓存
注册中心挂了,服务是否可以正常访问?
可以,因为dubbo服务消费者在第一次调用时,会将服务提供方地址缓存到消费者本地,以后再调用则不会访问注册中心。
当服务提供者地址发生变化时,注册中心会通知服务消费者。
超时与重试
服务消费者在调用服务提供者的时候发生了阻塞、等待的情形,这个时候,服务消费者会一直等待下去。在某个峰值时刻,大量的请求都在同时请求服务消费者,会造成线程的大量堆积,势必会造成雪崩。
dubbo 利用超时机制来解决这个问题,设置一个超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。
使用timeout属性配置超时时间,默认值1000,单位毫秒。
设置了超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。如果出现网络抖动,则这一次请求就会失败。
Dubbo 提供重试机制来避免类似问题的发生。
通过 retries 属性来设置重试次数。默认为 2 次。
@DubboService(timeout = 1000, retries = 2)
多版本
灰度发布:当出现新功能时,会让一部分用户先使用新功能,用户反馈没问题时,再将所有用户迁移到新功能。
- dubbo 中使用 version 属性来设置和调用同一个接口的不同版本
@DubboService(version = "v1.0") // 提供方
@DubboReference(version = "v1.0") // 使用方
负载均衡
Dubbo 提供 4 种负载均衡策略:
Random :按权重随机,默认值。按权重设置随机概率。
RoundRobin :按权重轮询。
LeastActive:最少活跃调用数,相同活跃数的随机。
ConsistentHash:一致性 Hash,相同参数的请求总是发到同一提供者。
@DubboService(weight = 100)
@DubboService(weight = 200)
@DubboReference(loadbalance = "random")
集群容错
Dubbo 提供的集群容错模式:
Failover Cluster:失败重试。默认值。当出现失败,重试其它服务器 ,默认重试2次,使用 retries 配置。一般用于读操作
Failfast Cluster :快速失败,只发起一次调用,失败立即报错。通常用于写操作。
Failsafe Cluster :失败安全,出现异常时,直接忽略。返回一个空结果。
Failback Cluster :失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
Forking Cluster :并行调用多个服务器,只要一个成功即返回。
Broadcast Cluster :广播调用所有提供者,逐个调用,任意一台报错则报错。
@DubboReference(cluster = "failover")
服务降级
服务降级方式
- mock=force:return null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
- mock=fail:return null 表示消费方对该服务的方法调用在失败后,再返回null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
@DubboReference(mock = "force:return null")
📚参考资料
❤️Sponsor
您的支持是我不断前进的动力,如果您感觉本文对您有所帮助的话,可以考虑打赏一下本文,用以维持本博客的运营费用,拒绝白嫖,从你我做起!🥰🥰🥰
支付宝 | 微信 |