1、微服务架构
微服务架构是一种设计复杂应用程序的方法,它提倡将单一应用程序开发为一组小型、独立的服务,每个服务运行在其自己的进程中,并通过轻量级通信(通常是HTTP协议)进行交互。每个服务都是围绕业务功能构建的,能够独立部署、扩展和维护。
上述说常用的服务间通信的协议是HTTP协议,但是目前很多公司里也会使用Dubbo作为RPC调用框架。
HTTP协议是存在于ISO模型中的应用层,应用层实现具体的应用功能,为应用程序提供服务。
Dubbo则是基于TCP协议的RPC架构,而TCP协议处于ISO模型的传输层,传输层只是建立、管理和维护端到端的连接。
所以在请求的过程中,TCP协议会比HTTP协议封装更少的内容,速度也比HTTP会快一些。
微服务核心特点:
- 模块化:微服务架构强调高内聚低耦合,每个服务专注于执行单一业务功能,这使得系统更易于理解和维护。
- 独立部署:每个微服务都可以独立于其他服务进行构建、测试、部署和扩展,减少了部署时的相互影响。
- 技术栈灵活:不同的微服务可以根据具体需求选择最适合的技术栈,不必受限于整个系统的统一技术选择。
- 弹性伸缩:微服务架构允许按需扩展特定的服务实例,以应对高负载情况,而无需扩展整个应用程序。
- 故障隔离:如果某个微服务出现故障,不会立即影响整个系统,因为其他服务仍然可以正常运行。
- 持续交付:由于微服务的独立性和自动化测试,团队可以更频繁地进行代码提交和部署,加速产品迭代速度。
- 数据管理:每个微服务通常拥有自己的数据库,遵循“数据所有权”原则,避免了全局数据模式的复杂性。
1.1、Dubbo在微服务架构中的角色
Dubbo在微服务架构中不仅提供了高效的服务间通信机制,还通过其全面的服务治理能力,极大地降低了构建、管理和维护复杂微服务架构的难度
- 服务治理:Dubbo是一个高性能、轻量级的开源服务框架,它提供了全面的服务治理解决方案,包括服务注册、服务发现、负载均衡、容错、限流、动态配置等功能,帮助构建稳定可靠的微服务架构。
- RPC通信:Dubbo采用高性能的RPC(Remote Procedure Call)通信机制,允许服务消费者远程调用服务提供者的接口,就像调用本地方法一样简单,极大地简化了分布式系统间的交互。
- 服务注册与发现:Dubbo支持多种注册中心,如Zookeeper、Nacos、Consul等,用于服务的自动注册与发现,使得服务提供者和消费者之间能够动态地找到彼此,而无需硬编码服务地址。
- 智能路由:Dubbo提供了丰富的路由策略,如随机、轮询、最少活跃数、一致性哈希等,可以根据不同的场景和需求,智能地选择服务实例进行调用,提高服务调用效率和系统的整体性能。
- 容错与重试:Dubbo内置了多种容错策略,如失败重试、失败回调、降级、熔断等,能够在服务不可用或网络异常时,提供优雅的降级方案,保证系统的稳定性和用户体验。
- 监控与统计:Dubbo集成了强大的监控统计功能,可以实时监控服务调用的延迟、吞吐量、异常率等关键指标,帮助开发者快速定位和解决问题。
- 灵活的配置管理:Dubbo支持动态配置更新,可以在不重启服务的情况下调整服务参数,提高了运维的灵活性和效率。
1.2、Zookeeper和Nacos作为服务注册与发现中心的作用。
(1)Zookeeper
Zookeeper主要用于协调分布式应用,提供了一套完整的分布式协调服务解决方案。它能够为分布式应用提供命名服务、配置管理、集群管理、分布式锁、队列管理等服务。
在微服务架构中,Zookeeper可以作为一个服务注册中心,各个微服务启动后会向Zookeeper注册自己的信息(如IP地址、端口号等),而其他需要调用这些服务的应用则可以从Zookeeper上查询到服务的信息,实现服务的自动发现。
Zookeeper的核心是其数据模型和Watcher机制,通过维护一个层次化的命名空间来存储数据,并且提供了实时的数据变更通知机制。但是,Zookeeper的性能并不适合高并发场景,且其API相对复杂,需要一定的学习成本。
(2)Nacos
Nacos是阿里巴巴开源的一款易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它结合了服务发现与配置管理的功能,旨在简化微服务和DevOps的运维工作。
Nacos提供了一个强大的服务注册与发现功能,微服务可以在启动时向Nacos注册,同时Nacos也支持健康检查,确保只返回可用的服务实例。服务消费者可以通过Nacos发现并调用服务。
Nacos具有高可用性、高性能和易用性,支持动态配置更新,无需重启服务即可生效。此外,Nacos还提供了丰富的可视化界面,方便用户管理和监控服务。
2、环境准备
1、IDEA编码工具。
2、Maven依赖管理工具
2.1、安装和配置Zookeeper和Nacos。
2.1.1、Zookeeper
(1)下载
Zookeeper是Apache旗下的产品,下载地址:Apache Zookeeper
(2)解压并启动
将Zookeeper压缩包下载好后进行解压。
解压好后还不能直接启动,还需要修改配置文件,一个很简单的修改操作。将conf目录下的zoo_sample.cfg文件改为zoo.cfg文件
即可。
修改前:
修改后:
进入bin目录,在windows系统中直接双击zkServer.cmd文件启动Zookeeper,而在Linux系统下则需要运行zkServer.sh文件。
2.1.2、Nacos
(1)下载
Nacos 是阿里的产品,现在是 SpringCloud 中的一个组件。它的功能要比 Eureka、Zookeeper 更加丰富,在国内比较受欢迎。
下载地址:Nacos GitHub下载地址
(2)下载后解压
(3)修改数据库配置
进入conf目录下,打开application.properties文件,需要在这里面修改数据库的配置。
在修改数据库配置之前需要走一个必不可少的流程,就是将conf目录下的mysql-schema.sql在指定的数据库中执行一次,让数据库中有所需要的表。
数据库的配置信息从33行开始。
#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
### Deprecated configuration property, it is recommended to use `spring.sql.init.platform` replaced.
spring.datasource.platform=mysql
spring.sql.init.platform=mysql
db.num=1
db.url=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user=root
db.password=123456
(4)启动Nacos
在bin目录下执行指定的命令:
1、单机模式:startup.cmd -m standalone
2、集群模式:startup.cmd -m cluster
,需要开启集群模式。
看到上述情况则表示Nacos启动成功,可以访问:http://localhost:8848/nacos
,用户名和密码如果没有设置的话都是:nacos/nacos。
2.2、依赖版本选择
- SpringBoot依赖版本:2.7.17
- JDK版本:JDK 17
- Dobbu依赖版本:2.7.17(nacos)、3.2.12(zookeeper)
- Nacos依赖版本:2021.0.5.0
2.2.1、Zookeeper版本下父pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.tt</groupId>
<artifactId>dubbo-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<packaging>pom</packaging>
<artifactId>zookeeper-registry</artifactId>
<modules>
<module>provider</module>
<module>interface-api</module>
<module>Consumer</module>
</modules>
<properties>
<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>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>3.2.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-zookeeper</artifactId>
<version>3.2.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2.2.2、Nacos模式下父pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.tt</groupId>
<artifactId>dubbo-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<packaging>pom</packaging>
<artifactId>nacos-registry</artifactId>
<modules>
<module>nacos-interface-api</module>
<module>nacos-provider</module>
<module>nacos-consumer</module>
</modules>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>2.7.17</spring.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2021.0.5.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
3、Dubbo与SpringBoot集成
将阿里巴巴开源的高性能、轻量级的微服务框架Dubbo,与流行的Spring Boot框架结合使用,以构建分布式服务应用。这种集成可以充分利用Dubbo的RPC(远程过程调用)功能和Spring Boot的快速开发特性,实现服务的自动注册、发现、负载均衡、容错、降级等功能。
3.1、整体项目结构
3.2、公共模块
公共模块主要是提供接口和公共的对象。
3.2.1、Nacos模式下的公共模块
// 提供公共的接口,具体业务由实现类完成。
public interface UserService {
public String userLogin(String username, String password);
}
3.2.2、Zookeeper模式下的公共模块
public interface UserService {
String userLogin(String username, String password);
}
3.2、构建服务提供者
服务提供者,先导入公共模块的依赖同时实现相应的接口,并将实现类的信息上传到注册中心。
3.2.1、Nacos模式服务提供者
(1)导入API公共模块依赖
在服务提供者的pom文件中导入API模块的依赖
<dependency>
<groupId>com.tt</groupId>
<artifactId>nacos-interface-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
(2)实现接口,完成业务逻辑
@DubboService
public class UserServiceImpl implements UserService {
@Override
public String userLogin(String username, String password) {
// 省略具体的业务逻辑
return username + "登录成功";
}
}
@DubboService是Dubbo Spring Boot Starter提供的一个注解,用于标记一个接口的实现类为Dubbo的服务提供者。
(3)开启Dubbo服务并设置配置中心
@SpringBootApplication
@EnableDubbo // 在启动类上开启Dubbo服务
public class NacosProviderApplication {
public static void main(String[] args) {
SpringApplication.run(NacosProviderApplication.class, args);
}
}
server:
port: 8081
spring:
application:
name: nacos-provider
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
namespace: 2cb71f92-6787-40bd-b9df-ccc2be92e0ec
file-extension: yaml
discovery:
server-addr: 127.0.0.1:8848
namespace: 2cb71f92-6787-40bd-b9df-ccc2be92e0ec
dubbo:
registry:
address: nacos://${spring.cloud.nacos.config.server-addr}
protocol:
port: 20880 # dubbo服务端口
name: dubbo # 协议
# monitor:
# protocol: registry
scan:
base-packages: com.tt.service
3.2.2、Zookeeper模式服务提供者
Zookeeper模式下的服务提供者除了配置文件与Nacos模式的服务提供者不同,其他基本一致。
spring:
application:
name: tt-provider
dubbo:
protocol:
name: dubbo
port: -1
# serialization: kryo
registry:
address: zookeeper://127.0.0.1:2181
application:
qos-port: 2222
provider:
timeout: 5000
scan:
base-packages: com.tt.service
3.3、构建服务消费者
在Dubbo模式下,服务消费者调用服务提供者的路径是一个涉及多个组件和步骤的过程。下面是一个典型的调用流程概述:
- 服务注册
服务提供者启动时,它会向注册中心(如Zookeeper)注册其提供的服务,包括服务的接口名、版本、所在主机、端口等信息。 - 服务发现
当服务消费者启动时,它会向注册中心订阅所需服务的接口信息。
注册中心返回服务提供者列表给消费者。 - 代理创建
消费者根据服务接口生成动态代理对象(Proxy),这个代理对象对用户透明,看起来就像在本地调用一样。 - 调用路由
当消费者通过代理对象调用服务时,调用会被转发到Invoker。
Invoker使用集群路由策略(如轮询、最少活跃数、一致性哈希等)选择一个服务提供者。 - 远程调用
选定提供者后,调用请求通过网络传输到提供者,通常使用Netty等高性能网络框架。
提供者接收到请求后,执行本地调用并返回结果。 - 结果处理
结果被编码并通过网络返回给消费者。
消费者接收到结果,完成整个调用链路。
3.3.1、Nacos模式下消费者
(1)开启Dubbo服务并配置注册中心
@SpringBootApplication
@EnableDubbo
public class NacosConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(NacosConsumerApplication.class, args);
}
}
server:
port: 8082
spring:
application:
name: nacos-consumer
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
namespace: 2cb71f92-6787-40bd-b9df-ccc2be92e0ec
file-extension: yaml
discovery:
server-addr: 127.0.0.1:8848
namespace: 2cb71f92-6787-40bd-b9df-ccc2be92e0ec
dubbo:
registry:
address: nacos://${spring.cloud.nacos.config.server-addr}
protocol:
port: 20880 # dubbo服务端口
name: dubbo # 协议
# monitor:
# protocol: registry
# scan:
# base-packages: com.tt.service
(2)进行Dubbo调用
@RestController
public class UserController {
@DubboReference // 通过接口进行代理对象的注入,然后进行RPC调用
private UserService userService;
@GetMapping(value = "/login/{username}/{password}")
public String userLogin(@PathVariable("username") String username, @PathVariable("password") String password){
return userService.userLogin(username, password);
}
}
3.3.2、Zookeeper模式下消费者
同Nacos模式下的消费者类似。
4、Dubbo特性
4.1、Dubbo的负载均衡策略
Dubbo是一个高性能、轻量级的开源微服务框架,它支持多种负载均衡策略来优化服务调用的分布,从而提高系统的整体性能和稳定性。以下是Dubbo支持的主要负载均衡策略:
- 随机(Random)
这是Dubbo默认的负载均衡策略,它随机选择一个可用的服务实例进行调用。如果配置了权重,那么权重较大的服务实例被选中的概率也更大。 - 轮询(RoundRobin)
也称为循环调度,按顺序逐一分配请求到不同的服务实例,保证每个实例都能公平地接收请求。当配置了权重时,权重高的实例会有更高的机会被选中。 - 最少活跃数(LeastActive)
选择活跃数最少的服务实例,即正在处理请求最少的那个实例,这样可以避免某些服务实例过载。 - 一致性哈希(ConsistentHash)
基于请求参数的哈希值进行路由,将请求分配给距离当前哈希值最近的下一个服务实例。这种策略可以减少服务实例变化带来的重新分配成本。 - 加权轮询(WeightedRoundRobin)
这是轮询策略的一种扩展,根据服务实例的权重进行轮询,权重大的实例被选中的频率更高。 - 加权随机(WeightedRandom)
类似于随机策略,但是根据服务实例的权重来随机选择,权重越高的实例被选中的概率越大。
在实际应用中,你可以根据服务的具体需求和场景选择合适的负载均衡策略,也可以自定义实现以满足更复杂的需求。这些策略可以通过Dubbo的配置文件或者注解来指定,例如在@Reference注解中使用loadbalance属性来指定负载均衡策略。
@RestController
public class UserController {
@DubboReference(loadbalance = "random") // 声明使用的负载均衡策略
private UserService userService;
@GetMapping(value = "/login/{username}/{password}")
public String userLogin(@PathVariable("username") String username, @PathVariable("password") String password){
return userService.userLogin(username, password);
}
}
负载均衡是分布式系统中一个关键的部分,它有助于提升系统的可用性、伸缩性和性能。
4.2、容错机制和重试策略。
Dubbo提供了多种容错机制和重试策略,用于处理服务调用时可能遇到的故障和异常情况。这些机制可以帮助服务保持高可用性和稳定性,尤其是在分布式系统中。下面是几种主要的容错和重试策略:
- Failover(失败自动切换)
当调用失败时,Dubbo会自动切换到其他可用的服务提供者进行重试。
这种策略通常用于读操作,因为重试可能会带来额外的延迟。
可以通过retries参数来设置重试次数(不包括首次调用)。 - Failfast(快速失败):
只发起一次调用,如果调用失败,则立即抛出异常并终止后续调用。
适用于非幂等性的写操作,例如新增记录。 - Failsafe(失败安全):
出现异常时,直接忽略并返回一个默认结果或空结果。
通常用于写入审计日志等操作,即使失败也不影响系统的主要功能。 - Failback(失败自动恢复):
请求失败后,会自动记录在失败队列中,并由一个定时线程在后台重试。
这种策略适用于那些可以延后处理且不会造成严重后果的操作。 - Forking(并行调用):
同时调用多个服务提供者,只要其中一个成功即返回。
适用于实时性要求较高的读操作,但需要消耗更多的服务资源。 - Broadcast(广播调用):
广播调用所有服务提供者,逐个调用,任意一台报错则报错。
通常用于通知所有提供者更新缓存或日志等本地资源信息。
在实际应用中,可以根据服务的特性和业务需求选择合适的容错策略。例如,对于查询语句,推荐使用默认的Failover策略,而对于增删改操作,建议使用Failfast策略,或者使用Failover策略但将retries设为0,以防止数据重复添加。
这些策略可以通过Dubbo的配置文件或代码中的注解来指定,例如在@DubboReference或@DubboService注解中使用cluster属性来设置容错策略。
@RestController
public class UserController {
// 随机的负载均衡策略,失败自动切换的容错机制,5次的重试机制
@DubboReference(loadbalance = "random", cluster = "failover", retries = 5)
private UserService userService;
@GetMapping(value = "/login/{username}/{password}")
public String userLogin(@PathVariable("username") String username, @PathVariable("password") String password){
return userService.userLogin(username, password);
}
}
如果项目中使用的是XML,可以使用以下写法:
<dubbo:reference id="demoService" interface="com.example.DemoService" cluster="failover" retries="2"/>
4.3、服务降级和熔断
Dubbo的服务降级和服务熔断是微服务架构中用于增强系统稳定性和防止雪崩效应的关键机制。它们通过限制或改变服务调用行为,来保护系统免受异常服务实例的影响。
4.3.1、服务降级
服务降级是指在服务调用遇到问题时,主动降低服务的功能或性能,返回一个默认或简化的响应,而不是等待服务响应或重试。这有助于避免服务调用方因等待无响应的服务而导致的资源耗尽。
在Dubbo中,服务降级可以通过多种方式实现:
- Mock机制:Dubbo允许你配置一个Mock对象,当服务调用失败时,Mock对象会返回一个预定义的响应,而不是抛出异常。这可以通过在@DubboReference注解中设置mock属性,或者在XML配置中使用mock标签来实现。
- 自定义降级逻辑:你也可以编写自定义的降级逻辑,例如,当服务不可用时,返回一个静态的错误页面或默认值。
4.3.1.1、简单的降级
@DubboReference(mock = "return null")
private MyService myService;
当myService调用失败时立马返回null。
4.3.1.2、自定义Mock
(1)实现降级方法
public class MyServiceMock implements MyService {
@Override
public String someMethod(String param) {
return "Mocked response for: " + param;
}
}
(2)配置mock
@DubboReference(mock = "com.example.service.MyServiceMock$someMethod")
private MyService myService;
4.3.2、服务熔断
服务熔断是一种保护机制,当一个服务的调用失败率达到一定程度时,Dubbo会自动“断开”对该服务的调用,直接返回错误或预定义的响应,而不是继续尝试调用可能已失败的服务。这可以防止大量请求涌向已知有问题的服务,避免进一步的系统崩溃。
Dubbo可以通过集成Hystrix来实现服务熔断。Hystrix是Netflix开源的一个容错库,它可以监控服务调用的健康状态,并在必要时触发熔断机制。
要使用Hystrix进行熔断,你需要做以下几点:
- 引入Hystrix依赖:在项目中添加Hystrix的依赖。
- 配置Hystrix:配置Hystrix的规则,比如失败率阈值、熔断时间窗口等。
- 使用@HystrixCommand注解:在服务调用方法上添加@HystrixCommand注解,指定熔断的逻辑和回退方法。
- 定义回退方法:当服务调用被熔断时,Hystrix会调用你定义的回退方法来处理请求。
在Dubbo中,你还可以通过配置circuitBreaker相关属性来控制熔断的行为:
@DubboReference(circuitBreaker = "on", timeout = 3000, retries = 0)
private DemoService demoService;