Spring Cloud Alibaba 综合集成架构演示案例

前言

Spring Cloud 是一套较为全面的微服务框架集,集成了如服务注册发现、配置中心、消息总线、负载均衡、断路器、API 网关等功能实现。而在网上经常会发现 Spring Cloud 与阿里巴巴的 Dubbo 进行选择对比,这样做其实不是很妥当,前者是一套较为完整的微服务架构方案,而 Dubbo 只是服务治理与 RPC 实现方案。Dubbo 在国内有着非常大的用户群体,但是其周边设施与组件相对来说并不那么完善。很多开发者用户又很希望享受 Spring Cloud 的生态,因此也会有一些 Spring Cloud 与 Dubbo 一起使用的案例与方法出现,但是一直以来大部分 Spring Cloud 整合 Dubbo 的使用方案都不完善,直到 Spring Cloud Alibaba 的出现,才得以解决这样的问题。

问题延伸

由于 Feign 是基于 HTTP Restful 的调用,在高并发下的性能不够理想,那么 RPC 方案能否切换为 Dubbo?Spring Cloud 与阿里系的若干组件能否完美集成呢?

整体系统架构

系统架构图

spring-cloud-alibaba-framework

  • API 网关:系统统一入口,屏蔽架构内部结构,统一安全拦截,采用 Zuul 实现
  • Application-1:应用 1,模拟应用,提供 HTTP 接口服务给 API 网关调用(Feign)
  • Service-1:微服务 1,模拟微服务,提供 Dubbo 接口服务给 Application-1 调用
  • Service-2:微服务 2,模拟微服务,提供 Dubbo 接口服务给 Application-1 调用

架构分层

  • 接入层:API 网关
  • 应用层:Application-1
  • 微服务层:Service-1、Service-2

调用流程

所有访问系统的请求都要经过 API 网关,网关转发 HTTP 请求至 Application-1,然后 Application-1 使用 Dubbo 调用 Service-1 完成自身业务,最后 Sevice-1 使用 Dubbo 调用 Service-2 完成自身业务。至此,完成所有组件贯穿。

Application 与 Sevice 的区别

  • 形成 Service 支撑 Application 的整体架构,增加多变的 Application 甚至不需要变动 Service
  • Service 提供了基础服务功能,而 Application 组装基础服务功能,提供给用户直接可用的业务,适合快速迭代开发
  • Service 服务粒度小、功能基础,不易发生改变,而 Application 提供上游业务功能,紧贴业务需求,容易发生改变

Spring Cloud Alibaba 集成架构演示案例

1.0、技术选型

Spring Boot、Spring Cloud Zuul、Spring Cloud OpenFeign、Nacos、Dubbo

1.1、版本说明

  • Zuul 1.3.1
  • Dubbo 2.7.8
  • Nacos Server 1.4.0
  • Spring Boot 2.1.18.RELEASE
  • Spring Cloud Greenwich.SR6
  • Spring Cloud Alibaba Dubbo 2.2.3.RELEASE
  • Spring Cloud Alibaba Nacos Config 2.1.3.RELEASE
  • Spring Cloud Alibaba Nacos Discovery 2.1.3.RELEASE

本案例中使用的各开源组件的版本如上,其中 Spring Cloud Alibaba Nacos Config 并没有真正发挥配置中心的作用,因为本文为了方便演示,并没有将 bootstrap.yml 配置文件里的部分配置信息发布到 Nacos Server(配置中心 + 注册中心),尤其是 api-gateway 工程里的路由映射配置,点击下载完整的案例代码。

1.2、工程结构

采用 Maven 工程结构(如下),为了方便演示,各组件的开发顺序为: service-2 -> service-1 -> application-1 -> api-gateway

1
2
3
4
5
6
7
8
9
alibaba-micro-service-study       整体父工程
├── api-gateway API 网关,端口:56010
├── application-1 应用 1,端口:56020
├── service-1 服务 1 父工程
│   ├── service-1-api 服务 1 API
│   ├── service-1-business 服务 1 业务实现,端口:56030
└── service-2 服务 2 父工程
├── service-2-api 服务 2 API
└── services-2-business 服务 2 业务实现,端口:56040

1.3、创建 Maven 父工程

创建 Maven 父工程,配置好工程需要的父级依赖,目的是为了更方便管理与简化配置,具体配置如下:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<groupId>com.alibaba.micro.study</groupId>
<artifactId>alibaba-micro-service-study</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<modules>
<module>api-gateway</module>
<module>application-1</module>
<module>service-1</module>
<module>service-2</module>
</modules>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.18.RELEASE</version>
</parent>

<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Greenwich.SR6</spring-cloud.version>
<spring-cloud-dubbo.version>2.2.3.RELEASE</spring-cloud-dubbo.version>
<spring-cloud-nacos.version>2.1.3.RELEASE</spring-cloud-nacos.version>
</properties>

<!-- 管理依赖 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
<version>${spring-cloud-dubbo.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${spring-cloud-nacos.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring-cloud-nacos.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

<!-- 利用传递依赖,公共部分 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

1.4、创建 Service 2 工程

Service 2 工程 的 Maven 配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<artifactId>service-2</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<modules>
<module>service-2-api</module>
<module>services-2-business</module>
</modules>

<parent>
<artifactId>alibaba-micro-service-study</artifactId>
<groupId>com.alibaba.micro.study</groupId>
<version>1.0-SNAPSHOT</version>
</parent>

1.4.1、创建 Service 2 API 工程

Service 2 API 工程非常简单,只负责声明服务接口,没有具体的实现,Maven 配置如下:

1
2
3
4
5
6
7
8
9
<artifactId>service-2-api</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
<groupId>com.alibaba.micro.study</groupId>
<artifactId>service-2</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

声明服务接口

1
2
3
4
5
6
public interface ProviderService {

public String add(Integer a, Integer b);

public String sub(Integer a, Integer b);
}

1.4.2、创建 Service 2 Business 工程

引入 service-2-api 依赖,由于需用使用 Dubbo 供 service-1 模块进行远程调用,因此需要引入 spring-cloud-starter-dubbo 依赖

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
<artifactId>services-2-business</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
<groupId>com.alibaba.micro.study</groupId>
<artifactId>service-2</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<dependencies>
<dependency>
<groupId>com.alibaba.micro.study</groupId>
<artifactId>service-2-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>

创建主启动类,添加 @EnableDiscoveryClient 注解,启用服务发现,将服务注册到 Nacos Server

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableDiscoveryClient
public class Service2Application {

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

创建具体的服务接口实现类,添加 @DubboService 注解标记此类的方法暴露为 Dubbo 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 使用 @DubboService 注解标记此类的方法暴露为Dubbo接口
*/
@DubboService
public class ProviderServiceImpl implements ProviderService {

private Logger LOG = LoggerFactory.getLogger(ProviderServiceImpl.class);

@Override
public String add(Integer a, Integer b) {
LOG.info("service 2 business invoke");
return String.valueOf(a + b);
}

@Override
public String sub(Integer a, Integer b) {
LOG.info("service 2 business invoke");
return String.valueOf(a - b);
}
}

添加 bootstrap.yml 配置文件,加入 Dubbo 相关的配置内容

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
server:
port: ${port:56040}
servlet:
context‐path: /service2

spring:
application:
name: service2
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: 4bfcbae8-8c37-417d-89e4-d5134e23eb18 # 开发环境
cluster-name: DEFAULT
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
namespace: 4bfcbae8-8c37-417d-89e4-d5134e23eb18 # 开发环境
group: NACOS_MICRO_SERVICE_GROUP # xxx业务组

dubbo:
scan:
base-packages: com.alibaba.micro.study
protocol:
name: dubbo
port: 20891
registry:
address: nacos://127.0.0.1:8848 # 注册中心地址
application:
qos-enable: false # Dubbo运维服务是否开启
consumer:
check: false # 启动时就否检查依赖的服务

1.5、创建 Service 1 工程

Service 1 工程 的 Maven 配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<artifactId>service-1</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>

<modules>
<module>service-1-api</module>
<module>service-1-business</module>
</modules>

<parent>
<artifactId>alibaba-micro-service-study</artifactId>
<groupId>com.alibaba.micro.study</groupId>
<version>1.0-SNAPSHOT</version>
</parent>

1.5.1、创建 Service 1 API 工程

Service 1 API 工程非常简单,只负责声明服务接口,没有具体的实现,Maven 配置如下:

1
2
3
4
5
6
7
8
9
<artifactId>service-1-api</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
<groupId>com.alibaba.micro.study</groupId>
<artifactId>service-1</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

声明服务接口

1
2
3
4
public interface ConsumerService {

public String add(Integer a, Integer b);
}

1.5.2、创建 Service 1 Business 工程

引入 service-1-apiservice-2-api 依赖,由于需用使用 Dubbo 调用 service-2-business 的服务实现,因此需要引入 spring-cloud-starter-dubbo 依赖

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
33
34
<artifactId>service-1-business</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
<groupId>com.alibaba.micro.study</groupId>
<artifactId>service-1</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>

<dependencies>
<dependency>
<groupId>com.alibaba.micro.study</groupId>
<artifactId>service-1-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.micro.study</groupId>
<artifactId>service-2-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>

创建主启动类,添加 @EnableDiscoveryClient 注解,启用服务发现,将服务注册到 Nacos Server

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableDiscoveryClient
public class Service1Application {

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

创建具体的服务接口实现类,添加 @DubboService 注解标记此类的方法暴露为 Dubbo 接口,同时使用 @DubboReference 注解生成接口代理对象,然后通过代理对象进行远程调用 service-2 的服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 使用 @DubboService 注解标记此类的方法暴露为Dubbo接口
*/
@DubboService
public class ConsumerServiceImpl implements ConsumerService {

/**
* 生成接口代理对象,通过代理对象进行远程调用
*/
@DubboReference
private ProviderService providerService;

private Logger LOG = LoggerFactory.getLogger(ConsumerServiceImpl.class);

@Override
public String add(Integer a, Integer b) {
LOG.info("service 1 business invoke");
return providerService.add(a, b);
}
}

添加 bootstrap.yml 配置文件,加入 Dubbo 相关的配置内容

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
server:
port: ${port:56030}
servlet:
context‐path: /service1

spring:
application:
name: service1
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: 4bfcbae8-8c37-417d-89e4-d5134e23eb18 # 开发环境
cluster-name: DEFAULT
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
namespace: 4bfcbae8-8c37-417d-89e4-d5134e23eb18 # 开发环境
group: NACOS_MICRO_SERVICE_GROUP # xxx业务组

dubbo:
scan:
base-packages: com.alibaba.micro.study
protocol:
name: dubbo
port: 20881
registry:
address: nacos://127.0.0.1:8848 # 注册中心地址
application:
qos-enable: false # Dubbo运维服务是否开启
consumer:
check: false # 启动时就否检查依赖的服务

1.6、创建 Application 1 工程

引入 service-1-apiservice-2-api 依赖,由于需要使用 Dubbo 进行远程调用,因此还需要引入 spring-cloud-starter-dubbo 依赖

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
33
34
<artifactId>application-1</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
<artifactId>alibaba-micro-service-study</artifactId>
<groupId>com.alibaba.micro.study</groupId>
<version>1.0-SNAPSHOT</version>
</parent>

<dependencies>
<dependency>
<groupId>com.alibaba.micro.study</groupId>
<artifactId>service-1-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.micro.study</groupId>
<artifactId>service-2-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>

创建主启动类,添加 @EnableDiscoveryClient 注解,启用服务发现,将服务注册到 Nacos Server

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableDiscoveryClient
public class ApplicationBootstrap {

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

创建 Controller 测试类,暴露供第三方调用的 HTTP API,同时使用 @DubboReference 注解生成接口代理对象,然后通过代理对象进行远程调用 service-1service-2 的服务

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
@RestController
public class ApplicationController {

/**
* 生成接口代理对象,通过代理对象进行远程调用
*/
@DubboReference
private ConsumerService consumerService;

/**
* 生成接口代理对象,通过代理对象进行远程调用
*/
@DubboReference
private ProviderService providerService;

private Logger LOG = LoggerFactory.getLogger(ApplicationController.class);

@GetMapping("/add")
public String add(Integer a, Integer b) {
LOG.info("application invoke");
return consumerService.add(a, b);
}

@GetMapping("/sub")
public String sub(Integer a, Integer b) {
LOG.info("application invoke");
return providerService.sub(a, b);
}
}

添加 bootstrap.yml 配置文件,特别注意,这里并没有将 appplication-1 的任何 Dubbo 服务注册到 Nacos Server,只是单纯的作为 Dubbo 服务的消费者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server:
port: ${port:56020}
servlet:
context‐path: /application1

spring:
application:
name: application1
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: 4bfcbae8-8c37-417d-89e4-d5134e23eb18 # 开发环境
cluster-name: DEFAULT
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
namespace: 4bfcbae8-8c37-417d-89e4-d5134e23eb18 # 开发环境
group: NACOS_MICRO_SERVICE_GROUP # xxx业务组

dubbo:
consumer:
check: false # 启动时就否检查依赖的服务

1.7、创建 API 网关工程

引入 Maven 依赖,由于使用了 Zuul 作为网关服务,因此需要引入 spring-cloud-starter-netflix-zuul 依赖,同时这里指定 Zuul 通过 Feign 将第三方的 HTTP 请求转发给 application-1 服务,还需要引入 spring-cloud-starter-openfeign

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
<artifactId>api-gateway</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<parent>
<artifactId>alibaba-micro-service-study</artifactId>
<groupId>com.alibaba.micro.study</groupId>
<version>1.0-SNAPSHOT</version>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>

创建主启动类,添加 @EnableDiscoveryClient@EnableZuulProxy

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class GatewayApplication {

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

添加 bootstrap.yml 配置文件,由于路由的映射规则会经常发生改变,在生产环境中建议将下列 Zuul 相关的配置发布到 Nacos Server(配置中心 + 注册中心)中。为了演示方便,这里直接将 Zuul 的路由配置信息写在 bootstrap.yml 里。

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
server:
port: ${port:56010}
servlet:
context‐path: /api-gateway

spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: 4bfcbae8-8c37-417d-89e4-d5134e23eb18 # 开发环境
cluster-name: DEFAULT
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
namespace: 4bfcbae8-8c37-417d-89e4-d5134e23eb18 # 开发环境
group: NACOS_MICRO_SERVICE_GROUP # xxx业务组

zuul:
routes:
application1:
stripPrefix: false
path: /application1/**

或者将 Zuul 的路由规则配置发布到 Nacos Server,而不是直接写在 bootstrap.yml 配置文件中,如下图所示:

spring-cloud-alibaba-nacos-integration-add-configs

1.8、测试应用代码

  • 1)分别启动 service-2service-1application-1api-gateway 应用
  • 2)浏览器访问 http://127.0.0.1:56020/application1/sub?a=6&b=2,若响应结果正确返回,则说明 service-2application-1 服务运行正常
  • 3)浏览器访问 http://127.0.0.1:56020/application1/add?a=3&b=4,若响应结果正确返回,则说明 service-2service-1application-1 服务运行正常
  • 4)浏览器访问 http://127.0.0.1:56010/api-gateway/application1/add?a=3&b=4,若响应结果正确返回,则说明 service-2service-1application-1api-gateway 服务运行正常
  • Nacos Server 的服务列表如下:

spring-cloud-alibaba-nacos-integration

  • 若希望测试各服务多实例的负载均衡调用情况,可以通过 -Dport=xxxxx VM 参数指定不同的端口来启动多个服务实例即可,这里不再累述