微服务架构统一基础设施设计实践
Executive Summary
核心观点(金字塔原理)
结论先行: 微服务架构下需要统一封装通用基础能力(身份识别、配置、日志、异常处理等),避免各服务重复开发,提升整体开发效率
支撑论点:
- 用户身份识别需统一放入ThreadLocal,服务间通过Feign Header传递
- 通用配置(Redis、FeignError等)应抽取为SDK统一提供
- 日志、异常、国际化、数据权限等横切关注点需要统一设计拦截器处理
SWOT 分析
| 维度 | 分析 |
|---|---|
| S 优势 | 提供了具体的代码实现示例,覆盖身份识别、配置、日志、Feign调用等核心场景 |
| W 劣势 | 部分章节(锁工具、国际化、异常统一等)尚未完成,整体方案待补充 |
| O 机会 | 可封装为通用SDK供团队复用,形成微服务开发规范 |
| T 威胁 | 过度统一可能降低灵活性,SDK版本管理和兼容性需要持续维护 |
适用场景
- Spring Cloud微服务架构的基础设施层设计
- 团队级微服务开发规范和SDK建设
- Feign服务间调用的统一配置和错误处理
总结
系统架构设计总体思考
- 关于数据冗余思考?首先判断是否需要冗余?需要查询场景吗?冗余的话,国际化问题如何处理?
小经验
背景:微服务背景下,服务虽然进行了拆分,但是服务依赖运行的还有很多相同的逻辑处理,比如用户身份识别,一些配置,日志(包括Feign调用),错误,数据权限,国际化,异常等等。所以我们在 开发的过程中,为提高效率需要对上述问题进行统一处理(或者各个服务自己独立处理,但工作量会比较多)。
用户身份识别
- 微服务情况下,需要统一处理用户身份放到ThreadLocal中。
通用配置
- Redis配置:继承 CachingConfigurerSupport,统一处理CacheManager,序列化,Error等
- FeignError配置:Feign调用时,出现服务错误,统一处理。实现 ErrorDecoder 即可。
- SDK中统一对外提供服务:
@Configuration
public class BeanConfig {
@Bean
public xxxController xxxController(){
return new xxxController();
}
}
- Feign调用Header传递数据: 可以在用户拦截器中进行参数解析获取使用。(HandlerInterceptorAdapter的实现类中)
public class BasicFeignConfiguration implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.header("key", "value");
}
}
- Feign服务间鉴权:类似
统一日志打印
- Feign服务调用控制:默认情况正式环境不打印Feign日志调用,可以继承:feign.Logger自定义实现日志级别打印,把info级别打印出来。
统一错误处理
- 使用 @ControllerAdvice + @ExceptionHandler 实现全局异常捕获,避免各Controller重复编写try-catch
- 统一错误响应格式:{code, message, data},code为业务错误码,message为提示信息,data为可选的附加数据
- 区分业务异常(BizException)和系统异常(SystemException),业务异常返回明确的业务错误码,系统异常统一返回500
- 服务端记录完整的错误堆栈日志便于排查,客户端只返回用户友好的提示信息,避免暴露内部实现细节
Feign header数据传递
- 实现 RequestInterceptor,在Feign调用时自动转发关键请求头(auth token、traceId、tenantId等),确保服务间上下文透传
- 通过 RequestContextHolder.getRequestAttributes() 获取当前请求的Header信息,注入到Feign请求模板中
- 避免盲目转发所有Header,按白名单机制只传递必要字段,防止Header过大或泄露敏感信息
- 结合 MDC(Mapped Diagnostic Context)传递traceId,实现分布式调用链路的日志关联追踪
跨系统数据权限如何设计
- 行级数据权限:通过MyBatis拦截器自动注入WHERE条件(如 org_id、dept_id),实现数据行级别的隔离过滤
- 列级数据权限:根据用户角色控制字段可见性,敏感字段(如手机号、身份证)按权限脱敏或隐藏
- 权限上下文存储在ThreadLocal中,跨服务调用时通过Feign Header传递权限标识,下游服务解析后注入本地上下文
- 权限拦截层级选择:网关层统一鉴权 vs 服务层各自鉴权,集中式权限中心 vs 分布式权限判断,需根据业务复杂度权衡
Lock锁工具
- 分布式锁抽象层:统一接口支持Redis(Redisson)和ZooKeeper等多种后端实现,业务代码无需关心底层锁机制
- 注解式声明锁:@DistributedLock(key=”order:{#orderId}”),通过AOP实现声明式加锁,减少模板代码
- Key设计规范:业务前缀 + 唯一业务ID(如 lock:order:123456),设置合理的自动过期时间防止死锁
- 支持可重入锁和锁超时配置,获取锁失败时可选择快速失败或等待重试策略
国际化如何设计?
- 基于Spring的 MessageSource + LocaleResolver 实现国际化框架,按locale加载对应的messages资源文件
- 错误码与i18n消息key映射:如 error.user.not.found 对应中文”用户不存在”、英文”User not found”,统一通过错误码查找多语言文案
- 通过请求Header中的 Accept-Language 或用户个人偏好设置来确定当前locale
- 数据库多语言字段设计:可采用独立的i18n翻译表(entity_id + locale + field_value)或JSON列存储多语言内容
拦截器如何统一?
- 通过 WebMvcConfigurer.addInterceptors() 注册拦截器链,按顺序配置拦截路径和排除路径
- 常用拦截器:认证鉴权拦截器、日志记录拦截器、限流拦截器、数据权限注入拦截器
- SDK提供基础拦截器链和默认配置,各业务服务可通过继承或配置进行扩展和覆盖
- 拦截器执行顺序至关重要:认证(auth) -> 权限(permission) -> 日志(logging) -> 业务处理(business)
异常如何统一?
- 定义异常层级体系:BaseException -> BizException(业务异常)、SystemException(系统异常)、RemoteCallException(远程调用异常)
- 每个异常携带错误码枚举(ErrorCode),用于国际化消息映射和客户端错误类型识别
- Feign ErrorDecoder 将HTTP错误响应反序列化还原为对应的类型化异常,保持异常语义在服务间传递
- 全局异常处理器记录完整堆栈信息用于问题排查,对客户端返回脱敏后的安全响应信息
常用的类
- Result
:统一API响应封装类,包含 code(状态码)、message(提示信息)、data(响应数据),提供 success() 和 fail() 静态工厂方法 - PageResult
:分页查询响应封装,包含 total(总记录数)、pageSize(每页大小)、pageNum(当前页码)、list(数据列表) - BaseEntity:实体基类,包含通用字段 id、createTime、updateTime、createdBy、updatedBy,统一审计信息管理
- AssertUtils:业务断言工具类,条件不满足时直接抛出BizException,如 AssertUtils.notNull(user, ErrorCode.USER_NOT_FOUND),简化参数校验代码