伸缩性的架构设计——服务器集群的伸缩性
DNS负载均衡
DNS服务器将访问的域名转发到对应的网关,网关层做反向代理。
利用消息组件对业务场景进行解耦
适合用消息组件解耦的场景
- 长任务(时间长,逻辑复杂,可异步)
- 非实时(轻任务、时效性要求不高、可异步化)
- 发布订阅(多下游相同语义)
长任务
- 商户数据变更——触发风控的自动评分(耗时的长任务)
- 商品中心——商品编辑&发布(复杂的业务场景)
- 报表系统——生成报表并发送
非实时
- 用户验证——手机验证码
- 通知消息——验证邮箱、用户通知
- 支付业务——付款回调接口
发布订阅
性能规划——性能指标和应用层优化策略
性能测试指标
- RT响应时间
- QPS每秒访问(吞吐量)
- 并发数(并发能力)
复杂业务性能优化
构建性能基线
稳定性测试
经过一段时间使用后,发现一些潜在的Bug
缓存使用模式
既然使用缓存,那就肯定会存在缓存数据和DB数据不一致的问题,只是不一致时间长短的问题
全量缓存场景
时效性缓存场景
读
写
业务设计原则——有限状态机的流转
基于Spring Statemachine的轻量级状态机
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.learn</groupId>
<artifactId>statemachine</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
biz
package com.learn.architect.biz;
/**
* @author YSK
* @since 2023/5/15 14:14
*/
public enum OrderEvent {
PLACE_ORDER,//下单
PAID,//付款完成
DELIVERED//开始运输
}
package com.learn.architect.biz;
/**
* @author YSK
* @since 2023/5/15 14:13
*/
public enum OrderState {
CREATED,
PENDING_PAYMENT,
PENDING_DELIVERY,
ORDER_COMPLETE
}
config
package com.learn.architect.config;
import com.learn.architect.biz.OrderEvent;
import com.learn.architect.biz.OrderState;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import java.util.EnumSet;
/**
* @author YSK
* @since 2023/5/15 14:31
*/
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderState, OrderEvent> {
@Override
public void configure(StateMachineStateConfigurer<OrderState,OrderEvent> states) throws Exception{
//声明状态和其他状态
states.withStates()
.initial(OrderState.CREATED)
.states(EnumSet.allOf(OrderState.class));
}
/**
* 配置状态机如何做流转
*/
@Override
public void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions) throws Exception {
transitions
.withExternal()
.source(OrderState.CREATED)
.target(OrderState.PENDING_PAYMENT)
.event(OrderEvent.PLACE_ORDER)
.and().withExternal()
.source(OrderState.PENDING_PAYMENT)
.target(OrderState.PENDING_DELIVERY)
.event(OrderEvent.PAID)
.and().withExternal()
.source(OrderState.PENDING_DELIVERY)
.target(OrderState.ORDER_COMPLETE)
.event(OrderEvent.DELIVERED);
}
}
listener
package com.learn.architect.listener;
import com.learn.architect.biz.OrderState;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;
/**
* @author YSK
* @since 2023/5/15 14:17
*/
@WithStateMachine
@Slf4j
@Data
public class OrderListener {
private String orderState = OrderState.CREATED.name();
@OnTransition(target = "PENDING_PAYMENT")
public void pendingPayment(Message message) {
log.info("订单创建,等待付款,states={},header={}",
OrderState.PENDING_PAYMENT.name(),
message.getHeaders().get("orderId"));
//todo 业务流程
setOrderState(OrderState.PENDING_PAYMENT.name());
}
@OnTransition(target = "PENDING_DELIVERY")
public void pendingDelivery(Message message) {
log.info("订单已付款,等待发货,states={}",
OrderState.PENDING_DELIVERY.name(),
message.getHeaders().get("orderId"));
//todo 业务流程
setOrderState(OrderState.PENDING_DELIVERY.name());
}
@OnTransition(target = "ORDER_COMPLETE")
public void complete(Message message) {
log.info("订单完成,states={}",
OrderState.ORDER_COMPLETE.name(),
message.getHeaders().get("orderId"));
//todo 业务流程
setOrderState(OrderState.ORDER_COMPLETE.name());
}
}
run
package com.learn.architect;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
/**
* @author YSK
* @since 2023/5/15 14:54
*/
@SpringBootApplication
public class ApplicationStater {
@Bean
public MyRunner myRunner() {
return new MyRunner();
}
public static void main(String[] args) {
SpringApplication.run(ApplicationStater.class,args);
}
}
package com.learn.architect;
import com.learn.architect.biz.OrderEvent;
import com.learn.architect.biz.OrderState;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.Ordered;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import javax.annotation.Resource;
/**
* @author YSK
* @since 2023/5/15 14:46
*/
public class MyRunner implements CommandLineRunner {
@Resource
StateMachine<OrderState,OrderEvent> stateMachine;
@Override
public void run(String... args) throws Exception {
stateMachine.start();
Message<OrderEvent> message = MessageBuilder
.withPayload(OrderEvent.PLACE_ORDER)
.setHeader("orderId", "998")
.build();
stateMachine.sendEvent(message);
stateMachine.sendEvent(OrderEvent.PAID);
stateMachine.sendEvent(OrderEvent.DELIVERED);
}
}
test
package com.learn.architect;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @author YSK
* @since 2023/5/15 14:57
*/
@SpringBootTest
public class StateMachineTest {
@Test
public void run() {
}
}
面试题
业务性能调优
- 首先考虑异步化(MQ、异步编排、池化等)
- 分布式、本地缓存
- 数据异构(非一致性场景快速返回、查找)
缓存的一致性问题
- 读写场景的一致性
- Setnx,watch+multi乐观锁
复杂业务中状态的流转(订单系统中的设计)
- 规则引擎
- 轻量级状态机实现Spring Statemachine