微服务架构统一基础设施设计实践

Executive Summary

核心观点(金字塔原理)

结论先行: 微服务架构下需要统一封装通用基础能力(身份识别、配置、日志、异常处理等),避免各服务重复开发,提升整体开发效率

支撑论点:

  1. 用户身份识别需统一放入ThreadLocal,服务间通过Feign Header传递
  2. 通用配置(Redis、FeignError等)应抽取为SDK统一提供
  3. 日志、异常、国际化、数据权限等横切关注点需要统一设计拦截器处理

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),简化参数校验代码