Spring Cloud Alibaba 整合 Dubbo 2 综合案例

大纲

前言

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、版本说明

组件版本说明
Zuul1.3.1
Nacos Server1.4.0
Spring Boot2.1.18.RELEASE
Spring CloudGreenwich.SR6
Spring Cloud Alibaba Dubbo2.2.3.RELEASE依赖 Dubbo 2.7.8
Spring Cloud Alibaba Nacos Config2.1.3.RELEASE
Spring Cloud Alibaba Nacos Discovery2.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 参数指定不同的端口来启动多个服务实例即可,这里不再累述