前言
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 与阿里系的若干组件能否完美集成呢?
整体系统架构
系统架构图
- 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 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
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 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-api
、service-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 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
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 consumer: check: false
|
1.6、创建 Application 1 工程
引入 service-1-api
、service-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-1
、service-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
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
zuul: routes: application1: stripPrefix: false path: /application1/**
|
或者将 Zuul 的路由规则配置发布到 Nacos Server,而不是直接写在 bootstrap.yml
配置文件中,如下图所示:
1.8、测试应用代码
- 1)分别启动
service-2
、service-1
、application-1
、api-gateway
应用 - 2)浏览器访问
http://127.0.0.1:56020/application1/sub?a=6&b=2
,若响应结果正确返回,则说明 service-2
、application-1
服务运行正常 - 3)浏览器访问
http://127.0.0.1:56020/application1/add?a=3&b=4
,若响应结果正确返回,则说明 service-2
、service-1
、application-1
服务运行正常 - 4)浏览器访问
http://127.0.0.1:56010/api-gateway/application1/add?a=3&b=4
,若响应结果正确返回,则说明 service-2
、service-1
、application-1
、api-gateway
服务运行正常 - Nacos Server 的服务列表如下:
- 若希望测试各服务多实例的负载均衡调用情况,可以通过
-Dport=xxxxx
VM 参数指定不同的端口来启动多个服务实例即可,这里不再累述