如何优化Spring Boot应用以处理每秒百万请求

如何优化Spring Boot应用以处理每秒百万请求

在当今高流量的互联网环境中,应用程序需要处理的请求量正在迅速增长。作为Java开发者,我们经常面临如何扩展Spring Boot应用以处理大量并发请求的挑战。本文将探讨如何将Spring Boot应用从处理每秒5万请求优化到每秒100万请求的实用策略,并提供详细的代码示例和实施步骤。

面临的挑战

想象一下这样的场景:你的团队被告知应用需要在三个月内实现20倍的流量增长,从每秒5万请求提升到每秒100万请求,而且硬件预算有限。这听起来几乎是不可能完成的任务。然而,通过深入分析性能瓶颈和应用一系列优化技术,我们可以达成这一目标。

在开始优化之前,我们需要了解系统的现状:

  • 基于Spring Boot 2.x的传统MVC架构
  • 使用内嵌的Tomcat服务器
  • 采用JPA进行数据库访问
  • 典型的N层架构(Controller > Service > Repository)
  • 高峰期出现频繁的GC暂停和超时错误

关键性能指标

在开始优化过程之前,了解我们的目标至关重要。成功优化后的应用应该达到以下性能指标:

  • 最大吞吐量:每秒120万请求
  • 平均响应时间:85毫秒(优化前为350毫秒)
  • 95%响应时间:120毫秒(优化前为850毫秒)
  • 峰值CPU使用率:60-70%(优化前为85-95%)
  • 内存使用:可用堆的50%(优化前为75%)
  • 数据库查询:通过缓存减少70%

优化策略

1. 采用响应式编程模型(Spring WebFlux)

最有影响力的变化是采用Spring WebFlux进行响应式编程。这不仅仅是简单的替换,而是需要重新思考应用程序的结构。首先,更新项目依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 将spring-boot-starter-web替换为spring-boot-starter-webflux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.7.5</version>
</dependency>

<!-- 使用Netty作为服务器 -->
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty</artifactId>
<version>1.0.24</version>
</dependency>

<!-- 引入Reactor测试支持 -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>

然后,改变控制器实现方式:

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
// 传统Spring MVC控制器
@RestController
@RequestMapping("/api/products")
public class ProductController {

@Autowired
private ProductService productService;

@GetMapping("/{id}")
public ResponseEntity<Product> getProduct(@PathVariable Long id) {
Product product = productService.getProductById(id);
return ResponseEntity.ok(product);
}

@GetMapping
public ResponseEntity<List<Product>> getAllProducts() {
List<Product> products = productService.getAllProducts();
return ResponseEntity.ok(products);
}
}

// 响应式WebFlux控制器
@RestController
@RequestMapping("/api/products")
public class ReactiveProductController {

@Autowired
private ReactiveProductService productService;

@GetMapping("/{id}")
public Mono<ResponseEntity<Product>> getProduct(@PathVariable Long id) {
return productService.getProductById(id)
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}

@GetMapping
public Mono<ResponseEntity<List<Product>>> getAllProducts() {
return productService.getAllProducts()
.collectList()
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}
}

服务层也需要相应改变:

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
// 传统的服务实现
@Service
public class ProductService {
@Autowired
private ProductRepository repository;

public Product getProductById(Long id) {
return repository.findById(id)
.orElseThrow(() -> new ProductNotFoundException(id));
}

public List<Product> getAllProducts() {
return repository.findAll();
}
}

// 响应式服务实现
@Service
public class ReactiveProductService {
@Autowired
private ReactiveProductRepository repository;

public Mono<Product> getProductById(Long id) {
return repository.findById(id)
.switchIfEmpty(Mono.error(new ProductNotFoundException(id)));
}

public Flux<Product> getAllProducts() {
return repository.findAll();
}
}

2. 优化数据库访问

数据库通常是系统的主要瓶颈。以下是一些优化数据库访问的详细策略:

2.1 采用响应式数据库驱动

将传统的JDBC驱动替换为响应式数据库驱动:

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 添加R2DBC依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>

<!-- MySQL的R2DBC驱动 -->
<dependency>
<groupId>dev.miku</groupId>
<artifactId>r2dbc-mysql</artifactId>
<version>0.8.2.RELEASE</version>
</dependency>

配置R2DBC连接:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration
public class R2dbcConfig {

@Bean
public ConnectionFactory connectionFactory() {
return MySqlConnectionFactory.from(
MySqlConnectionConfiguration.builder()
.host("localhost")
.port(3306)
.username("root")
.password("password")
.database("reactive_demo")
.build()
);
}

@Bean
public R2dbcEntityTemplate r2dbcEntityTemplate(ConnectionFactory connectionFactory) {
return new R2dbcEntityTemplate(connectionFactory);
}
}

创建响应式Repository:

1
2
3
4
5
6
7
8
9
public interface ReactiveProductRepository extends ReactiveCrudRepository<Product, Long> {

Flux<Product> findByCategory(String category);

Mono<Product> findByName(String name);

@Query("SELECT * FROM products WHERE price > :minPrice")
Flux<Product> findExpensiveProducts(BigDecimal minPrice);
}

2.2 实施多级缓存策略

引入Redis缓存:

1
2
3
4
5
<!-- Redis响应式支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

配置Redis缓存:

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
@Configuration
@EnableRedisRepositories
public class RedisConfig {

@Bean
public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory() {
return new LettuceConnectionFactory("localhost", 6379);
}

@Bean
public ReactiveRedisTemplate<String, Product> reactiveRedisTemplate(
ReactiveRedisConnectionFactory factory) {

StringRedisSerializer keySerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer<Product> valueSerializer =
new Jackson2JsonRedisSerializer<>(Product.class);

RedisSerializationContext.RedisSerializationContextBuilder<String, Product> builder =
RedisSerializationContext.newSerializationContext(keySerializer);

RedisSerializationContext<String, Product> context =
builder.value(valueSerializer).build();

return new ReactiveRedisTemplate<>(factory, context);
}
}

在服务层实现缓存:

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
@Service
public class CachedProductService {

private static final String CACHE_KEY_PREFIX = "product:";
private static final Duration CACHE_TTL = Duration.ofMinutes(10);

@Autowired
private ReactiveProductRepository repository;

@Autowired
private ReactiveRedisTemplate<String, Product> redisTemplate;

public Mono<Product> getProductById(Long id) {
String cacheKey = CACHE_KEY_PREFIX + id;

// 先尝试从缓存获取
return redisTemplate.opsForValue().get(cacheKey)
.switchIfEmpty(
// 缓存未命中,从数据库加载
repository.findById(id)
.flatMap(product -> {
// 将结果存入缓存
return redisTemplate.opsForValue()
.set(cacheKey, product, CACHE_TTL)
.thenReturn(product);
})
);
}

public Flux<Product> getAllProducts() {
// 对于列表类型的数据,可以使用不同的缓存策略
return repository.findAll();
}

// 清除缓存的方法,在更新产品时调用
public Mono<Void> invalidateCache(Long id) {
String cacheKey = CACHE_KEY_PREFIX + id;
return redisTemplate.delete(cacheKey);
}
}

2.3 优化SQL查询

使用索引优化:

1
2
3
4
-- 为常用查询字段添加索引
CREATE INDEX idx_product_category ON products(category);
CREATE INDEX idx_product_price ON products(price);
CREATE INDEX idx_product_name ON products(name);

优化查询方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 避免使用count(*)
public Mono<Boolean> existsByCategory(String category) {
return repository.findFirstByCategory(category)
.map(product -> true)
.defaultIfEmpty(false);
}

// 使用分页减少数据传输量
public Flux<Product> getProductsByCategory(String category, int page, int size) {
return repository.findByCategory(category,
PageRequest.of(page, size, Sort.by("name").ascending()));
}

// 使用投影只返回需要的字段
@Query("SELECT id, name, price FROM products WHERE category = :category")
Flux<ProductSummary> findProductSummariesByCategory(String category);

2.4 实现读写分离

配置主从数据源:

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
@Configuration
public class DatabaseConfig {

@Bean
@Primary
@Qualifier("masterConnectionFactory")
public ConnectionFactory masterConnectionFactory() {
return MySqlConnectionFactory.from(
MySqlConnectionConfiguration.builder()
.host("master-db.example.com")
.port(3306)
.username("master_user")
.password("master_pass")
.database("products")
.build()
);
}

@Bean
@Qualifier("slaveConnectionFactory")
public ConnectionFactory slaveConnectionFactory() {
return MySqlConnectionFactory.from(
MySqlConnectionConfiguration.builder()
.host("slave-db.example.com")
.port(3306)
.username("read_user")
.password("read_pass")
.database("products")
.build()
);
}

@Bean
public TransactionManager transactionManager(
@Qualifier("masterConnectionFactory") ConnectionFactory connectionFactory) {
return new R2dbcTransactionManager(connectionFactory);
}
}

创建读写分离的Repository:

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
@Repository
public class ReadWriteProductRepository {

private final R2dbcEntityTemplate masterTemplate;
private final R2dbcEntityTemplate slaveTemplate;

public ReadWriteProductRepository(
@Qualifier("masterConnectionFactory") ConnectionFactory masterFactory,
@Qualifier("slaveConnectionFactory") ConnectionFactory slaveFactory) {
this.masterTemplate = new R2dbcEntityTemplate(masterFactory);
this.slaveTemplate = new R2dbcEntityTemplate(slaveFactory);
}

// 写操作使用主库
@Transactional
public Mono<Product> save(Product product) {
return masterTemplate.insert(Product.class)
.into("products")
.using(product);
}

// 读操作使用从库
public Mono<Product> findById(Long id) {
return slaveTemplate.select(Product.class)
.from("products")
.matching(Query.query(Criteria.where("id").is(id)))
.one();
}

public Flux<Product> findAll() {
return slaveTemplate.select(Product.class)
.from("products")
.all();
}
}

3. 配置调优

性能提升往往来自于正确的配置调整。以下是详细的配置优化步骤:

3.1 调整Netty服务器参数

application.yml中配置Netty参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
webflux:
base-path: /api

netty:
connection:
timeout: 5000 # 连接超时时间(毫秒)
worker:
count: 16 # worker线程数量, 建议设置为CPU核心数的2倍
boss:
count: 2 # boss线程数量
buffer:
size: 32768 # 缓冲区大小(字节)

在Java代码中自定义Netty配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Configuration
public class NettyConfig {

@Bean
public NettyReactiveWebServerFactory nettyReactiveWebServerFactory() {
NettyReactiveWebServerFactory factory = new NettyReactiveWebServerFactory();

factory.addServerCustomizers(server -> {
HttpServer httpServer = (HttpServer) server;

// 设置更大的接收缓冲区大小
httpServer.tcpConfiguration(tcpServer ->
tcpServer.option(ChannelOption.SO_RCVBUF, 128 * 1024)
.option(ChannelOption.SO_SNDBUF, 128 * 1024)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.TCP_NODELAY, true));

return httpServer;
});

return factory;
}
}

3.2 优化JVM堆和垃圾收集器

application.properties中添加JVM参数,或者直接在启动脚本中配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 堆内存设置
-Xms4g
-Xmx4g

# 年轻代大小,建议为堆的1/3到1/2
-Xmn2g

# GC相关设置,使用G1收集器
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:ParallelGCThreads=8
-XX:ConcGCThreads=2
-XX:InitiatingHeapOccupancyPercent=70

# 禁用类元数据的GC,防止Full GC
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m

# 优化内存分配
-XX:+AlwaysPreTouch
-XX:+DisableExplicitGC

# 开启GC日志记录
-Xlog:gc*:file=logs/gc-%t.log:time,uptime,level,tags:filecount=5,filesize=100m

3.3 连接池优化

配置R2DBC连接池:

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
@Configuration
public class ConnectionPoolConfig {

@Bean
public ConnectionFactory connectionFactory() {
ConnectionPoolConfiguration configuration = ConnectionPoolConfiguration.builder()
.connectionFactory(createConnectionFactory())
.maxIdleTime(Duration.ofMinutes(30))
.maxLifeTime(Duration.ofHours(2))
.maxAcquireTime(Duration.ofSeconds(3))
.initialSize(10)
.maxSize(50) // 根据负载调整
.minIdle(10)
.build();

return new ConnectionPool(configuration);
}

private ConnectionFactory createConnectionFactory() {
return MySqlConnectionFactory.from(
MySqlConnectionConfiguration.builder()
.host("localhost")
.port(3306)
.username("root")
.password("password")
.database("reactive_demo")
.connectTimeout(Duration.ofSeconds(3))
.build()
);
}
}

配置Redis连接池:

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
@Configuration
public class RedisConnectionConfig {

@Bean
public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory() {
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.commandTimeout(Duration.ofMillis(100)) // 命令超时
.shutdownTimeout(Duration.ZERO) // 关闭超时
.clientOptions(ClientOptions.builder()
.socketOptions(SocketOptions.builder()
.connectTimeout(Duration.ofMillis(100)) // 连接超时
.keepAlive(true)
.build())
.build())
.clientResources(DefaultClientResources.builder()
.ioThreadPoolSize(4) // IO线程池大小
.computationThreadPoolSize(4) // 计算线程池大小
.build())
.build();

RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration();
serverConfig.setHostName("localhost");
serverConfig.setPort(6379);

return new LettuceConnectionFactory(serverConfig, clientConfig);
}
}

3.4 HTTP客户端调优

配置WebClient高性能连接池:

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
@Configuration
public class WebClientConfig {

@Bean
public WebClient webClient() {
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.TCP_NODELAY, true)
.doOnConnected(conn ->
conn.addHandlerLast(new ReadTimeoutHandler(10))
.addHandlerLast(new WriteTimeoutHandler(10)))
.responseTimeout(Duration.ofSeconds(5))
.compress(true)
.wiretap(true) // 开发环境调试使用,生产环境应关闭
.keepAlive(true)
.poolResources(ConnectionProvider.builder("custom")
.maxConnections(500)
.pendingAcquireTimeout(Duration.ofMillis(5000))
.pendingAcquireMaxCount(1000)
.maxIdleTime(Duration.ofMillis(8000))
.build());

return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.codecs(configurer ->
configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024))
.build();
}
}

4. 混合架构方法

不是所有的端点都需要改为响应式的。下面是混合架构的详细实现:

4.1 定义混合架构路由

使用Spring的路由器函数:

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
@Configuration
public class RouterConfig {

// 高流量端点使用响应式处理
@Bean
public RouterFunction<ServerResponse> highTrafficRoutes(
ReactiveProductHandler productHandler) {

return RouterFunctions
.route(GET("/api/products").and(accept(APPLICATION_JSON)),
productHandler::getAllProducts)
.andRoute(GET("/api/products/{id}").and(accept(APPLICATION_JSON)),
productHandler::getProduct)
.andRoute(GET("/api/products/category/{category}").and(accept(APPLICATION_JSON)),
productHandler::getProductsByCategory);
}

// 低流量的管理端点保留传统MVC
@Bean
public WebMvcConfigurer mvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/admin").setViewName("admin");
registry.addViewController("/dashboard").setViewName("dashboard");
}
};
}
}

4.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
30
31
32
33
34
35
36
37
38
39
40
41
42
@Component
public class ReactiveProductHandler {

private final ReactiveProductService productService;

public ReactiveProductHandler(ReactiveProductService productService) {
this.productService = productService;
}

public Mono<ServerResponse> getProduct(ServerRequest request) {
Long productId = Long.valueOf(request.pathVariable("id"));

return productService.getProductById(productId)
.flatMap(product ->
ServerResponse.ok()
.contentType(APPLICATION_JSON)
.bodyValue(product))
.switchIfEmpty(ServerResponse.notFound().build());
}

public Mono<ServerResponse> getAllProducts(ServerRequest request) {
return productService.getAllProducts()
.collectList()
.flatMap(products ->
ServerResponse.ok()
.contentType(APPLICATION_JSON)
.bodyValue(products))
.switchIfEmpty(ServerResponse.notFound().build());
}

public Mono<ServerResponse> getProductsByCategory(ServerRequest request) {
String category = request.pathVariable("category");

return productService.getProductsByCategory(category)
.collectList()
.flatMap(products ->
ServerResponse.ok()
.contentType(APPLICATION_JSON)
.bodyValue(products))
.switchIfEmpty(ServerResponse.notFound().build());
}
}

4.3 实现逐步迁移策略

创建适配器以支持新旧代码互操作:

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
@Component
public class ProductServiceAdapter {

private final ReactiveProductService reactiveService;
private final LegacyProductService legacyService;

@Autowired
public ProductServiceAdapter(
ReactiveProductService reactiveService,
LegacyProductService legacyService) {
this.reactiveService = reactiveService;
this.legacyService = legacyService;
}

// 将响应式服务适配到传统服务
public Product getProductSync(Long id) {
return reactiveService.getProductById(id).block();
}

// 将传统服务适配到响应式服务
public Mono<Product> getProductReactive(Long id) {
return Mono.fromCallable(() -> legacyService.getProductById(id))
.subscribeOn(Schedulers.boundedElastic());
}

// 批量操作适配
public List<Product> getAllProductsSync() {
return reactiveService.getAllProducts().collectList().block();
}

public Flux<Product> getAllProductsReactive() {
return Flux.defer(() -> Flux.fromIterable(legacyService.getAllProducts()))
.subscribeOn(Schedulers.boundedElastic());
}
}

5. 水平扩展与Kubernetes

在优化应用程序之后,可以通过Kubernetes进行智能水平扩展。以下是详细的配置和实现:

5.1 创建Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
FROM openjdk:17-jdk-slim

WORKDIR /app

COPY target/reactive-product-service-0.0.1-SNAPSHOT.jar app.jar

# 设置JVM参数
ENV JAVA_OPTS="-XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UseStringDeduplication -Xms1g -Xmx1g -XX:+AlwaysPreTouch"

EXPOSE 8080

# 健康检查
HEALTHCHECK --interval=5s --timeout=3s --retries=3 CMD curl -f http://localhost:8080/actuator/health || exit 1

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

5.2 创建Kubernetes部署配置

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
apiVersion: apps/v1
kind: Deployment
metadata:
name: product-service
namespace: ecommerce
spec:
replicas: 3
selector:
matchLabels:
app: product-service
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
template:
metadata:
labels:
app: product-service
spec:
containers:
- name: product-service
image: my-registry/product-service:latest
imagePullPolicy: Always
ports:
- containerPort: 8080
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1000m"
memory: "2Gi"
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60
periodSeconds: 20
timeoutSeconds: 5
failureThreshold: 3
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: SERVER_PORT
value: "8080"
- name: JAVA_OPTS
value: "-XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UseStringDeduplication -Xms1g -Xmx1g -XX:+AlwaysPreTouch"

5.3 创建服务和入口配置

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
apiVersion: v1
kind: Service
metadata:
name: product-service
namespace: ecommerce
spec:
selector:
app: product-service
ports:
- port: 80
targetPort: 8080
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: product-service-ingress
namespace: ecommerce
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
spec:
rules:
- host: api.example.com
http:
paths:
- path: /api/products
pathType: Prefix
backend:
service:
name: product-service
port:
number: 80

5.4 配置自动扩缩容

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
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: product-service-hpa
namespace: ecommerce
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: product-service
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Percent
value: 100
periodSeconds: 15
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 20
periodSeconds: 60

5.5 实现优雅降级机制

在应用程序中添加断路器模式:

1
2
3
4
5
6
7
8
9
10
11
<!-- 添加Resilience4j依赖 -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-reactor</artifactId>
<version>1.7.1</version>
</dependency>

配置断路器:

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class ResilienceConfig {

@Bean
public CircuitBreakerRegistry circuitBreakerRegistry() {
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.slowCallRateThreshold(50)
.slowCallDurationThreshold(Duration.ofSeconds(1))
.permittedNumberOfCallsInHalfOpenState(10)
.minimumNumberOfCalls(20)
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.

参考

https://medium.com/javarevisited/how-i-optimized-a-spring-boot-application-to-handle-1m-requests-second-0cbb2f2823ed