Spring Boot 单体应用升级 Spring Cloud 微服务最佳实践 - Spring Cloud Alibaba官网
欢迎报名8月2日上海首个AI原生应用架构开源沙龙!点此了解

Spring Boot 单体应用升级 Spring Cloud 微服务最佳实践

刘军

Spring Boot 单体应用升级 Spring Cloud 微服务

通过以下示例,我们完整的演示了一个 Spring Boot 架构的单体应用集群,如何平滑的升级为一个 Spring Cloud 微服务集群,本文章包含源码、讲解、原理说明。

  1. 原始 Spring Boot 应用架构

在示例中,我们有如下基于 Spring Boot 开发的应用架构:

spring boot

我们这里列出来的只是一种示例架构。基于 Spring Boot 构建的应用架构变化多样,比如可能如下一些常用的架构,但不论哪种架构,升级 Spring Cloud 的大致改造方式都是类似的(都可以转为基于 Nacos 注册中心的地址发现与负载均衡)。

  • 基于 DNS 自定义域名,服务间的通过域名调用实现负载均衡
  • 基于 SLB 的中心化流量转发,调用直接转发到 SLB,由 SLB 实现在服务间实现流量转发
  • 基于 Kubernetes Service 微服务体系,依赖 Kubernetes ClusterIP 等实现负载均衡与调用转发
  1. 升级后的 Spring Cloud Alibaba 应用架构

我们将以上示例全部改造为 Spring Cloud 应用,改造后的架构如下:

spring cloud

新架构基于 Spring Cloud Service Discovery 机制实现地址自动发现与负载均衡,使用 Nacos 作为注册中心。

示例 Spring Boot 应用

示例包含 spring-boot-A(service-a) 和 spring-boot-B(service-b)两个应用(微服务),应用之间依赖 dns 域名完成互相调用。

spring boot

因此,要完整的运行示例,我们首先需要在本地 /etc/hosts 配置域名映射:

127.0.0.1 service-b.example.com
127.0.0.1 service-a.example.com

依次启动 spring-boot-B(service-b)、spring-boot-A(service-a) 应用,使用以下命令验证应用正常工作:

Terminal window
$ curl http://service-a.example.com:18000/test
Get result from service B.

示例 Spring Cloud 应用

spring cloud

接下来,我们分步骤将 Spring Boot A 应用和 Spring Boot B 应用改造为 Spring Cloud 应用。

改造应用B

首先将 Spring Boot B 应用进行改造,接入 Spring Cloud Nacos Registry

第一步:添加依赖

<properties>
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
<spring-cloud.version>2022.0.0</spring-cloud.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-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>

第二步:完善配置文件

spring:
application:
name: service-b #项目名称必填,在注册中心唯一;最好和之前域名保持一致,第四步会讲到原因
cloud:
nacos:
discovery: #启用 spring cloud nacos discovery
server-addr: 127.0.0.1:8848

第三步:启动类注解

@SpringBootApplication
@EnableDiscoveryClient
public class SpringBootBApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootBApplication.class, args);
}
}

改造应用A

第一步:添加依赖

<properties>
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
<spring-cloud.version>2022.0.0</spring-cloud.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-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Nacos 服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 服务发现:OpenFeign服务调用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 负载均衡器 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>

第二步:配置文件

spring:
application:
name: service-a #项目名称必填,在注册中心唯一;最好和之前域名保持一致,第四步会讲到原因
cloud:
nacos:
discovery: #启用 spring cloud nacos discovery
server-addr: 127.0.0.1:8848

第三步:启动类注解

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class SpringBootAApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootAApplication.class, args);
}
}

第四步:调整调用方式

改造后的应用我们要使用 Nacos 注册中心来做地址发现,并使用 Loadbalancer 来实现负载均衡。

@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
@RestController
public class AController {
@Autowired
private RestTemplate restTemplate;
private static final String SERVICE_PROVIDER_ADDRESS = "http://service-b";
@GetMapping("/test")
public String callServiceB() {
return restTemplate.getForObject(SERVICE_PROVIDER_ADDRESS +"/test-service-b", String.class);
}
}

增加 Gateway 网关

依赖

<properties>
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
<spring-cloud.version>2022.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Nacos 服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 服务发现:OpenFeign服务调用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 整合nacos需要添加负载均衡依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
<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-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

启动类注解

@SpringBootApplication
@EnableDiscoveryClient
public class SpringGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SpringGatewayApplication.class, args);
}
}

路由配置

spring:
cloud:
gateway:
routes:
- id: service-a
uri: lb://service-a
predicates:
- Path=/service-a/test/**
filters:
- StripPrefix=1
- id: service-b
uri: lb://service-b
predicates:
- Path=/service-b/test-service-b/**
filters:
- StripPrefix=1

启动与验证

在本地 /etc/hosts 配置如下 hostname 映射

Terminal window
127.0.0.1 service.example.com

依次启动 A、B、Gateway 三个应用。

访问:http://service.example.com/service-a/test-servicer-b

可以看到,请求正常被转发到 service-a、service-b,并实现多实例地址自动发现、负载均衡。