第02章 - 系统架构详解
第02章 - 系统架构详解
2.1 微服务架构设计理念
2.1.1 微服务架构概述
微服务架构(Microservices Architecture)是一种将单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务之间通过轻量级机制(通常是HTTP REST API)进行通信。
RuoYi-Cloud采用微服务架构设计,将传统的单体应用拆分为多个独立的服务模块,每个模块负责特定的业务功能,可以独立开发、部署和扩展。
2.1.2 微服务架构的优势
技术多样性
- 不同服务可以使用不同的技术栈
- 可以针对特定场景选择最合适的技术
- 便于引入新技术和框架
独立部署
- 各服务可以独立部署,互不影响
- 更新某个服务不需要重新部署整个系统
- 支持灰度发布和A/B测试
弹性伸缩
- 可以针对高负载服务进行水平扩展
- 资源利用更加精细和高效
- 支持按需扩缩容
故障隔离
- 单个服务故障不会导致整个系统崩溃
- 支持熔断、降级等容错机制
- 提高系统整体可用性
团队协作
- 不同团队可以负责不同服务
- 降低团队之间的耦合
- 提高开发效率
2.1.3 微服务架构的挑战
复杂性增加
- 服务数量多,管理复杂
- 需要处理分布式事务
- 服务间通信开销
运维成本
- 需要完善的监控体系
- 日志收集和追踪
- 配置管理复杂
数据一致性
- 分布式事务处理
- 数据同步问题
- 最终一致性保证
2.2 Spring Cloud Alibaba 技术栈
2.2.1 技术选型
RuoYi-Cloud基于Spring Cloud Alibaba生态构建,主要技术组件包括:
┌─────────────────────────────────────────────────────────────────────────────┐
│ Spring Cloud Alibaba 生态 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Nacos │ │ Sentinel │ │ Seata │ │ Dubbo │ │
│ │ 服务发现/ │ │ 流量控制 │ │ 分布式事务 │ │ RPC框架 │ │
│ │ 配置管理 │ │ 熔断降级 │ │ │ │ (可选) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────────────┤
│ Spring Cloud 核心组件 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Gateway │ │ OpenFeign │ │ LoadBalan │ │ Config │ │
│ │ API网关 │ │ 服务调用 │ │ 负载均衡 │ │ 配置中心 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────────────┤
│ Spring Boot 基础框架 │
└─────────────────────────────────────────────────────────────────────────────┘
2.2.2 Nacos - 注册中心与配置中心
Nacos简介
Nacos(Dynamic Naming and Configuration Service)是阿里巴巴开源的一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
核心功能
-
服务发现与健康检查
- 支持基于DNS和RPC的服务发现
- 提供对服务的实时健康检查
- 阻止向不健康的主机或服务实例发送请求
-
动态配置管理
- 以中心化、外部化和动态化的方式管理配置
- 配置变更时无需重新部署应用
- 支持配置版本历史和回滚
-
动态DNS服务
- 支持权重路由
- 更容易实现中间层负载均衡、灵活的路由策略
在RuoYi-Cloud中的应用
# bootstrap.yml 配置示例
spring:
application:
name: ruoyi-system
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: ${spring.profiles.active}
config:
server-addr: localhost:8848
namespace: ${spring.profiles.active}
file-extension: yml
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
2.2.3 Sentinel - 流量控制与熔断
Sentinel简介
Sentinel是阿里巴巴开源的面向分布式服务架构的轻量级流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助保护服务的稳定性。
核心功能
-
流量控制
- 基于QPS的限流
- 基于并发线程数的限流
- 支持直接限流、关联限流、链路限流
-
熔断降级
- 慢调用比例
- 异常比例
- 异常数
-
热点参数限流
- 对指定参数进行限流
- 支持热点参数例外项
-
系统自适应保护
- 系统负载保护
- CPU使用率保护
- 平均RT保护
配置示例
// 使用@SentinelResource注解
@SentinelResource(value = "getUserById",
blockHandler = "handleBlock",
fallback = "handleFallback")
public User getUserById(Long id) {
return userService.getById(id);
}
// 限流处理
public User handleBlock(Long id, BlockException ex) {
return new User().setName("限流默认用户");
}
// 降级处理
public User handleFallback(Long id, Throwable ex) {
return new User().setName("降级默认用户");
}
2.2.4 Seata - 分布式事务
Seata简介
Seata(Simple Extensible Autonomous Transaction Architecture)是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
事务模式
-
AT模式(自动补偿)
- 基于本地ACID事务的关系型数据库
- 两阶段提交,自动生成回滚日志
- 适用于大多数业务场景
-
TCC模式(手动补偿)
- Try-Confirm-Cancel三阶段
- 业务侵入性强,但更灵活
- 适用于复杂业务场景
-
SAGA模式(长事务)
- 长流程业务事务
- 支持正向服务和补偿服务
- 适用于业务流程长的场景
-
XA模式(强一致性)
- 基于XA协议的两阶段提交
- 强一致性保证
- 性能较低
在RuoYi-Cloud中的使用
// 开启全局事务
@GlobalTransactional(name = "createOrder", rollbackFor = Exception.class)
public void createOrder(OrderDTO orderDTO) {
// 1. 创建订单
orderService.create(orderDTO);
// 2. 扣减库存(远程调用)
stockFeignClient.deduct(orderDTO.getProductId(), orderDTO.getCount());
// 3. 扣减余额(远程调用)
accountFeignClient.deduct(orderDTO.getUserId(), orderDTO.getMoney());
}
2.3 核心模块架构
2.3.1 模块依赖关系
┌─────────────────────────────────┐
│ ruoyi (父工程) │
│ pom.xml │
└─────────────────────────────────┘
│
┌───────────────────────────────┼───────────────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌─────────────────┐ ┌───────────────┐
│ ruoyi-common │ │ ruoyi-api │ │ ruoyi-modules │
│ (通用模块) │ │ (接口模块) │ │ (业务模块) │
└───────────────┘ └─────────────────┘ └───────────────┘
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌─────────────────┐ ┌───────────────┐
│ common-core │ │ api-system │ │ ruoyi-system │
│ common-redis │◄───────────┤ │◄───────────┤ ruoyi-gen │
│ common-security│ └─────────────────┘ │ ruoyi-job │
│ common-log │ │ ruoyi-file │
│ common-swagger│ └───────────────┘
│ ... │
└───────────────┘
┌───────────────────────────────┼───────────────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌─────────────────┐ ┌───────────────┐
│ ruoyi-gateway │ │ ruoyi-auth │ │ ruoyi-visual │
│ (网关服务) │ │ (认证中心) │ │ (可视化) │
└───────────────┘ └─────────────────┘ └───────────────┘
│
▼
┌───────────────┐
│ ruoyi-monitor │
│ (监控中心) │
└───────────────┘
2.3.2 ruoyi-common 通用模块
通用模块包含了系统中所有服务共用的代码和配置:
ruoyi-common-core(核心模块)
- 通用工具类(StringUtils、DateUtils等)
- 通用常量定义
- 基础实体类(BaseEntity、BaseController等)
- 统一响应结果封装(R、AjaxResult)
- 全局异常处理
- 文本操作、分页支持等
ruoyi-common-redis(缓存模块)
- Redis配置类
- Redis工具类
- 缓存操作封装
ruoyi-common-security(安全模块)
- 权限注解定义(@RequiresLogin、@RequiresPermissions、@RequiresRoles)
- 权限校验处理器
- Token解析和验证
- 安全工具类
ruoyi-common-log(日志模块)
- 操作日志注解(@Log)
- 日志切面处理
- 日志事件发布
ruoyi-common-swagger(接口文档模块)
- Springdoc OpenAPI配置
- 接口文档自动生成
ruoyi-common-datascope(数据权限模块)
- 数据权限注解(@DataScope)
- 数据权限切面处理
- 按部门/用户过滤数据
ruoyi-common-datasource(多数据源模块)
- 动态数据源配置
- 数据源切换注解
ruoyi-common-sensitive(数据脱敏模块)
- 敏感数据注解
- 脱敏序列化器
ruoyi-common-seata(分布式事务模块)
- Seata配置
- 全局事务支持
2.3.3 ruoyi-api 接口模块
接口模块定义了服务间调用的API接口:
ruoyi-api-system(系统服务接口)
// 远程用户服务接口
@FeignClient(contextId = "remoteUserService",
value = ServiceNameConstants.SYSTEM_SERVICE,
fallbackFactory = RemoteUserFallbackFactory.class)
public interface RemoteUserService {
@GetMapping("/user/info/{username}")
R<LoginUser> getUserInfo(@PathVariable("username") String username,
@RequestHeader(SecurityConstants.FROM_SOURCE) String source);
@PostMapping("/user/register")
R<Boolean> registerUserInfo(@RequestBody SysUser sysUser,
@RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}
// 远程日志服务接口
@FeignClient(contextId = "remoteLogService",
value = ServiceNameConstants.SYSTEM_SERVICE,
fallbackFactory = RemoteLogFallbackFactory.class)
public interface RemoteLogService {
@PostMapping("/operlog")
R<Boolean> saveLog(@RequestBody SysOperLog sysOperLog,
@RequestHeader(SecurityConstants.FROM_SOURCE) String source);
@PostMapping("/logininfor")
R<Boolean> saveLogininfor(@RequestBody SysLogininfor sysLogininfor,
@RequestHeader(SecurityConstants.FROM_SOURCE) String source);
}
2.3.4 业务模块说明
ruoyi-modules-system(系统模块)
- 用户管理
- 部门管理
- 岗位管理
- 角色管理
- 菜单管理
- 字典管理
- 参数配置
- 通知公告
- 日志管理
ruoyi-modules-gen(代码生成模块)
- 数据库表读取
- 代码模板管理
- 前后端代码生成
- 支持单表、主子表、树表
ruoyi-modules-job(定时任务模块)
- 基于Quartz实现
- 在线管理定时任务
- 任务执行日志记录
ruoyi-modules-file(文件服务模块)
- 本地文件存储
- MinIO分布式存储
- FastDFS分布式存储
2.4 Gateway 网关架构
2.4.1 网关功能
Spring Cloud Gateway作为系统的统一入口,承担以下职责:
外部请求
│
▼
┌───────────────────────────────┐
│ Spring Cloud Gateway │
├───────────────────────────────┤
│ │
│ ┌─────────────────────────┐ │
│ │ 路由转发 Route │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ 鉴权过滤 AuthFilter │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ 验证码过滤 Captcha │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ XSS过滤 XssFilter │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ 黑名单过滤 Black │ │
│ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ │
│ │ 限流控制 RateLimiter │ │
│ └─────────────────────────┘ │
│ │
└───────────────────────────────┘
│
┌───────────────────────────┼───────────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ ruoyi-auth │ │ ruoyi-system │ │ ruoyi-gen │
│ /auth/** │ │ /system/** │ │ /code/** │
└───────────────┘ └───────────────┘ └───────────────┘
2.4.2 路由配置
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
# 认证中心
- id: ruoyi-auth
uri: lb://ruoyi-auth
predicates:
- Path=/auth/**
filters:
- CacheRequestFilter
- ValidateCodeFilter
- StripPrefix=1
# 系统模块
- id: ruoyi-system
uri: lb://ruoyi-system
predicates:
- Path=/system/**
filters:
- StripPrefix=1
# 代码生成
- id: ruoyi-gen
uri: lb://ruoyi-gen
predicates:
- Path=/code/**
filters:
- StripPrefix=1
# 定时任务
- id: ruoyi-job
uri: lb://ruoyi-job
predicates:
- Path=/schedule/**
filters:
- StripPrefix=1
# 文件服务
- id: ruoyi-file
uri: lb://ruoyi-file
predicates:
- Path=/file/**
filters:
- StripPrefix=1
2.4.3 过滤器机制
全局过滤器
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpRequest.Builder mutate = request.mutate();
String url = request.getURI().getPath();
// 跳过不需要验证的路径
if (StringUtils.matches(url, ignoreWhite.getWhites())) {
return chain.filter(exchange);
}
// 获取token
String token = getToken(request);
if (StringUtils.isEmpty(token)) {
return unauthorizedResponse(exchange, "令牌不能为空");
}
// 验证token
Claims claims = JwtUtils.parseToken(token);
if (claims == null) {
return unauthorizedResponse(exchange, "令牌已过期或验证不正确!");
}
// 设置用户信息到请求头
String userid = JwtUtils.getUserId(claims);
String username = JwtUtils.getUserName(claims);
mutate.header(SecurityConstants.USER_KEY, userid);
mutate.header(SecurityConstants.DETAILS_USER_ID, userid);
mutate.header(SecurityConstants.DETAILS_USERNAME, username);
return chain.filter(exchange.mutate().request(mutate.build()).build());
}
@Override
public int getOrder() {
return -200;
}
}
2.5 认证授权架构
2.5.1 认证流程
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│ Client │ │ Gateway │ │ Auth │ │ System │
└─────┬──────┘ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘
│ │ │ │
│ 1.登录请求 │ │ │
│─────────────────>│ │ │
│ │ 2.转发请求 │ │
│ │─────────────────>│ │
│ │ │ 3.查询用户 │
│ │ │─────────────────>│
│ │ │ 4.返回用户信息 │
│ │ │<─────────────────│
│ │ │ │
│ │ 5.生成Token │ │
│ │<─────────────────│ │
│ 6.返回Token │ │ │
│<─────────────────│ │ │
│ │ │ │
│ 7.携带Token请求 │ │ │
│─────────────────>│ │ │
│ │ 8.验证Token │ │
│ │─────────────────>│ │
│ │ 9.验证通过 │ │
│ │<─────────────────│ │
│ │ 10.转发业务请求 │ │
│ │─────────────────────────────────────>│
│ 11.返回业务数据 │ │ │
│<─────────────────────────────────────────────────────────│
│ │ │ │
2.5.2 Token 机制
Token结构
public class LoginUser implements Serializable {
// 用户唯一标识
private String token;
// 用户ID
private Long userid;
// 用户名
private String username;
// 登录时间
private Long loginTime;
// 过期时间
private Long expireTime;
// 登录IP
private String ipaddr;
// 权限列表
private Set<String> permissions;
// 角色列表
private Set<String> roles;
// 用户信息
private SysUser sysUser;
}
Token生成与验证
@Service
public class TokenService {
// 令牌有效期(默认30分钟)
@Value("${token.expireTime}")
private int expireTime;
// 创建令牌
public Map<String, Object> createToken(LoginUser loginUser) {
String token = IdUtils.fastUUID();
loginUser.setToken(token);
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
// 缓存登录信息
String userKey = getTokenKey(token);
redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
// 生成JWT
Map<String, Object> claims = new HashMap<>();
claims.put(SecurityConstants.USER_KEY, token);
claims.put(SecurityConstants.DETAILS_USER_ID, loginUser.getUserid());
claims.put(SecurityConstants.DETAILS_USERNAME, loginUser.getUsername());
Map<String, Object> rspMap = new HashMap<>();
rspMap.put("access_token", JwtUtils.createToken(claims));
rspMap.put("expires_in", expireTime);
return rspMap;
}
// 验证令牌
public LoginUser getLoginUser(String token) {
LoginUser user = null;
try {
if (StringUtils.isNotEmpty(token)) {
String userKey = JwtUtils.getUserKey(token);
user = redisService.getCacheObject(getTokenKey(userKey));
}
} catch (Exception e) {
log.error("获取用户信息异常'{}'", e.getMessage());
}
return user;
}
}
2.5.3 权限校验
权限注解
// 登录校验
@RequiresLogin
// 权限校验
@RequiresPermissions("system:user:list")
@RequiresPermissions(value = {"system:user:add", "system:user:edit"}, logical = Logical.OR)
// 角色校验
@RequiresRoles("admin")
@RequiresRoles(value = {"admin", "common"}, logical = Logical.OR)
权限处理器
@Aspect
@Component
public class PreAuthorizeAspect {
@Before("@annotation(requiresPermissions)")
public void doPermissionCheck(JoinPoint joinPoint, RequiresPermissions requiresPermissions) {
// 获取当前用户权限
Set<String> permissions = SecurityUtils.getLoginUser().getPermissions();
// 校验权限
String[] requirePerms = requiresPermissions.value();
Logical logical = requiresPermissions.logical();
if (logical == Logical.AND) {
// AND模式:需要包含所有权限
for (String perm : requirePerms) {
if (!hasPermission(permissions, perm)) {
throw new NotPermissionException(perm);
}
}
} else {
// OR模式:包含任一权限即可
boolean hasAny = false;
for (String perm : requirePerms) {
if (hasPermission(permissions, perm)) {
hasAny = true;
break;
}
}
if (!hasAny) {
throw new NotPermissionException(StringUtils.join(requirePerms, ","));
}
}
}
}
2.6 数据权限架构
2.6.1 数据权限设计
RuoYi-Cloud支持五种数据权限类型:
| 类型 | 说明 |
|---|---|
| 全部数据权限 | 可查看所有数据 |
| 自定数据权限 | 按指定部门过滤数据 |
| 本部门数据权限 | 只能查看本部门数据 |
| 本部门及以下数据权限 | 查看本部门及下级部门数据 |
| 仅本人数据权限 | 只能查看自己的数据 |
2.6.2 实现原理
数据权限注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataScope {
// 部门表的别名
String deptAlias() default "";
// 用户表的别名
String userAlias() default "";
// 权限字符(用于多个角色匹配符合条件的权限)
String permission() default "";
}
切面处理
@Aspect
@Component
public class DataScopeAspect {
@Before("@annotation(dataScope)")
public void doBefore(JoinPoint point, DataScope dataScope) {
clearDataScope(point);
handleDataScope(point, dataScope);
}
protected void handleDataScope(JoinPoint joinPoint, DataScope dataScope) {
// 获取当前用户
LoginUser loginUser = SecurityUtils.getLoginUser();
SysUser currentUser = loginUser.getSysUser();
StringBuilder sqlString = new StringBuilder();
List<String> conditions = new ArrayList<>();
for (SysRole role : currentUser.getRoles()) {
String dataScope = role.getDataScope();
if (DATA_SCOPE_ALL.equals(dataScope)) {
// 全部数据权限
sqlString = new StringBuilder();
conditions.clear();
break;
} else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
// 自定义数据权限
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ",
deptAlias, role.getRoleId()));
} else if (DATA_SCOPE_DEPT.equals(dataScope)) {
// 本部门数据权限
sqlString.append(StringUtils.format(
" OR {}.dept_id = {} ", deptAlias, currentUser.getDeptId()));
} else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
// 本部门及以下数据权限
sqlString.append(StringUtils.format(
" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
deptAlias, currentUser.getDeptId(), currentUser.getDeptId()));
} else if (DATA_SCOPE_SELF.equals(dataScope)) {
// 仅本人数据权限
sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, currentUser.getUserId()));
}
}
// 设置到BaseEntity
BaseEntity baseEntity = (BaseEntity) joinPoint.getArgs()[0];
baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
}
}
2.7 日志架构
2.7.1 日志类型
操作日志
- 记录用户的业务操作行为
- 包含操作模块、操作类型、请求参数、响应结果等
登录日志
- 记录用户登录/登出行为
- 包含登录IP、登录地点、浏览器、操作系统等
2.7.2 日志实现
日志注解
@Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
// 模块
String title() default "";
// 功能
BusinessType businessType() default BusinessType.OTHER;
// 操作人类别
OperatorType operatorType() default OperatorType.MANAGE;
// 是否保存请求的参数
boolean isSaveRequestData() default true;
// 是否保存响应的参数
boolean isSaveResponseData() default true;
// 排除指定的请求参数
String[] excludeParamNames() default {};
}
使用示例
@Log(title = "用户管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody SysUser user) {
return toAjax(userService.insertUser(user));
}
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody SysUser user) {
return toAjax(userService.updateUser(user));
}
@Log(title = "用户管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{userIds}")
public AjaxResult remove(@PathVariable Long[] userIds) {
return toAjax(userService.deleteUserByIds(userIds));
}
2.8 小结
本章详细介绍了RuoYi-Cloud的系统架构设计,包括:
- 微服务架构理念:了解微服务的优势和挑战
- 技术栈选型:掌握Spring Cloud Alibaba生态组件
- 核心模块设计:理解各模块的职责和依赖关系
- 网关架构:了解Gateway的过滤器机制和路由配置
- 认证授权:掌握Token机制和权限校验流程
- 数据权限:理解数据范围控制的实现原理
- 日志系统:了解操作日志的记录方式
理解系统架构是深入学习和二次开发的基础,下一章我们将进行环境准备和项目部署。

浙公网安备 33010602011771号