认识Spring Cloud Alibaba

Spring Cloud初识 讲了Spring Cloud,本篇博客则对Spring Cloud Alibaba进行介绍。

由来

为什么有了Spring Cloud,还要再整个新轮子Spring Cloud Alibaba呢?一切源于Netflix项目的停止更新,进入维护状态。大家都知道,Spring Cloud是在Netflix开源的一系列微服务核心技术栈上进行封装整合得到的,因此Netflix停止更新对于SpringCloud的打击是不言而喻的。

那么Netflix为什么停止更新旗下开源框架呢?简单来讲,就是这些项目的更新需要消耗大量的时间精力,然而没有直接的利益收入。当技术热情的潮水慢慢退去,剩下的就是贫瘠的现实,最终走向终结。Spring Cloud Alibaba就在这时候出现了。有人认为其目的是为了推广阿里云,但不管怎么说,Spring Cloud Alibaba是一套优质的微服务解决方案。

简介

Spring Cloud Alibaba同样包含一堆组件,如Nacos、Sentinel、Seata等。Spring Cloud Alibaba的主要特性如下:

  • 流量控制和服务降级:通过Sentinel完成流量控制、服务熔断和系统自适应保护。
  • 服务注册和发现:使用Nacos完成服务注册和发现,并且在Nacos中集成了Ribbon以达到客户端负载均衡效果。
  • 分布式配置:使用Nacos作为数据仓库。
  • 事件驱动:通过RocketMQ搭建高性能且可扩展的事件驱动的微服务。
  • 消息总线:使用RocketMQ连接分布式系统节点。
  • 分布式事务:使用Seata提供高性能且方便使用的分布式事务
  • Dubbo Rpc:使用Dubbo Rpc扩展了Spring Cloud中服务调用的通信协议。

接下来将介绍各组件,包括其功能特点和简单使用。

Nacos

Nacos不仅支持服务注册和发现,还提供了配置功能。同时支持三种部署模式:

  • 单机模式:用于测试和单机试用。
  • 集群模式:用于生产环境,确保高可用性。
  • 多集群模式:用于多数据中心场景。

另外Nacos支持持久化存储,其内置数据源为Derby,外置数据源支持Mysql。以下仅展示Nacos的基本功能,首先均需要通过startup.sh/cmd启动nacos。

Nacos服务注册和发现

在服务注册和发现上,Nacos的使用与Eureka类似,但是不需要像Eureka单独跑一个应用作为注册中心。Nacos服务注册和发现详细介绍了其使用过程。

Nacos服务注册

启动类代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@SpringBootApplication
@EnableDiscoveryClient
public class NacosProvider9001 {

@Value("${server.port}")
private String serverPort;

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

@RestController
public class EchoController {

@GetMapping(value = "/echo/{str}")
public String echo(@PathVariable String str) {
return String.format("Hello Nacos discovery from %s : %s", serverPort, str);
}
}
}

对应application.yml内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server:
port: 9002

spring:
application:
name: nacos-provider-echo
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848

management:
endpoints:
web:
exposure:
include: '*'

按照同样的配置和代码(注意端口设置,我个人设置的是9001和9002)启动两个服务。

Nacos服务发现

启动类代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConsumer10001 {

@RestController
public class NacosController{

@Autowired
private RestTemplate restTemplate;

@Value("${spring.application.name}")
private String appName;

@GetMapping("/echo/app-name")
public String echoAppName() {
String path = String.format("http://%s/echo/%s", "nacos-provider-echo", appName);
System.out.println("request path:" + path);
return restTemplate.getForObject(path, String.class);
}

}

@Bean
@LoadBalanced // 使用负载均衡功能
public RestTemplate restTemplate() {
return new RestTemplate();
}

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

注意,由于Nacos集成了Ribbon,所以可以使用其负载均衡机制。服务发现者的application.yml内容为:

1
2
3
4
5
6
7
8
9
10
server:
port: 10001

spring:
application:
name: nacos-consumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848

最终效果

Nacos的服务列表中有两个服务,分别来自服务提供者和服务消费者。


Nacos服务列表

对服务消费者使用http://localhost:10001/echo/app-name进行测试,其效果如下(同时体现了负载均衡效果):


Nacos配置中心

Nacos还能够作为配置中心,并支持动态配置。

动态配置

在Nacos中使用图形界面创建配置:

需要注意的是Data Id需要按照一定规则设置:即${spring.application.name}-${profile}. ${file-extension:properties}。因此图中的Data Id为nacos-config-dev.yaml。

新建应用,启动类代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootApplication
public class NacosConfig {

public static void main(String[] args) throws Exception {
ConfigurableApplicationContext applicationContext = SpringApplication.run(NacosConfig.class, args);
while(true) {
// 不断获取配置信息,并打印
String configInfo = applicationContext.getEnvironment().getProperty("config.info");
String currentEnv = applicationContext.getEnvironment().getProperty("current.env");
System.err.println("config.info = " + configInfo + ", current.env = " + currentEnv);
TimeUnit.SECONDS.sleep(1);
}
}
}

其yml配置为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server:
port: 5566

spring:
profiles:
active: test # 选择不同的环境
application:
name: nacos-config
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yaml

运行应用,并在Nacos上动态地将config.info修改为version = 2, 可以看到应用的打印信息发生了变化:

分类配置

Nacos引入了Namespace、Group、Data Id来进行更强大的分类控制功能。

命名空间(Namespace)用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。如未指定Namespace,则默认使用public。

Group是Nacos 中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串(如 Buy 或 Trade )对配置集进行分组,从而区分 Data ID 相同的配置集。当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。配置分组的常见场景:不同的应用或组件使用了相同的配置类型,如 database_url 配置和 MQ_topic 配置

Data Id是Nacos 中的某个配置集的 ID。配置集 ID 是组织划分配置的维度之一。Data ID 通常用于组织划分系统的配置集。一个系统或者应用可以包含多个配置集,每个配置集都可以被一个有意义的名称标识。Data ID 通常采用类 Java 包(如 com.taobao.tc.refund.log.level)的命名规则保证全局唯一性。此命名规则非强制。

三者组合使用能够达到细粒度配置的效果,从范围角度看:Namespace > Group > Data Id。通过在配置中选择不同的group和namespace即可选择不同的配置集。

1
2
3
4
5
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
group: xx
namespace: xx

对于Nacos更详细的文档可见Nacos官网

Sentinel

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定性。

Sentinel分为两部分:

  • 核心库(客户端):不依赖其他库或框架,可以嵌入到应用中。
  • 控制台(Dashboard):基于SpringBoot开发,可直接运行,提供图形化界面。

Sentinel基本展示

通过添加Sentinel依赖,并且在yml文件中加入:

1
2
3
4
5
6
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080
port: 8719

即可在Sentinel控制台看到微服务流量访问情况。

Sentinel配置

Sentinel允许为不同资源配置不同规则:

  • 流控:针对流量访问进行控制,允许从QPS或线程数维度进行流量控制,提供多种流控模式(直接、关联、链路)和流控效果(直接失败、Warm Up和排队等待)。
  • 降级:熔断降级,允许多种降级策略(RT、异常比例和异常数)。相比Hystrix,Sentinel没有半开状态。
  • 热点:可对热点资源的访问进行限制。

Sentinel能够对应用异常和控制台配置违规进行相应兜底处理。除此之外,Sentinel还支持系统规则等功能。通过配置,能够灵活地使用Sentinel对微服务进行稳定性方面的设置。对于Sentinel更详细的文档可见Sentinel官网

Seata

Seata是用于分布式事务(协调多数据源的数据一致问题)的框架。Seata基于两阶段提交协议,采用一个Transaction Id和三个组件模型来协调分布式事务处理过。

  • Transaction Id:全局唯一的事务Id
  • 三个组件:
    • 事务协调者(Transaction Coordinator,TC):维护全局和分支事务的状态,驱动全局事务提交或回滚。
    • 事务管理器(Transaction Manager,TM):定义全局事务的范围:开始全局事务、提交或回滚全局事务。
    • 资源管理器(Resource Manager,RM):管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

Seata支持多种事务模式(AT,TCC,Saga,XA)。这里简单描述下AT模式下Seata的处理过程,假设服务A、B(服务A同时是该事务的发起者)要执行一个分布式事务,过程如下:

  1. 服务A的TM向TC申请开启一个全局事务,全局事务创建成功后会生成一个全局唯一的XID。
  2. 服务A执行自身业务逻辑,并生成undo日志(用于回滚),同时RM向TC注册分支事务(该分支事务被纳入XID对应全局事务的管辖)。执行成功则提交本地事务。
  3. 当服务A执行到远程调用(调用指向B)时,由服务B执行业务逻辑,生成undo日志,并由服务B的RM向TC注册分支事务,将其纳入全局事务的管辖。执行成功则提交本地事务。
  4. 远程调用成功,由服务A的TM向TC提交全局事务的提交决议。
  5. TC提交该全局事务,并清除相关资源(会话、各服务undo日志等),完成提交。

对于Seata的其他情况可参考Seata官网

参考资料