跳到主要内容

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

· 阅读需 6 分钟

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) 应用,使用以下命令验证应用正常工作:

$ 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 映射

127.0.0.1  service.example.com

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

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

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