第3章:平台架构设计:分层架构、微服务架构、技术选型(Spring Cloud / K8s)
好,咱们进入正题。需求响应聚合平台,说白了就是一个「中间人」系统。它一边连着电网调度,一边连着成千上万的用户侧资源。这种场景下,架构设计要是搞砸了,后面全是坑。我个人习惯,做这类平台先想清楚两件事:一是系统怎么拆,二是技术栈怎么选。
3.1 分层架构:先搭好骨架
我最早做能源项目时,喜欢把所有逻辑塞进一个单体应用。结果呢?每次改个计量规则,整个系统都要重新部署。后来我学乖了——分层,必须分层。
一个典型的分层架构,从上到下大概是这样:
┌─────────────────────────────────────┐
│ 接入层(API Gateway) │
├─────────────────────────────────────┤
│ 应用层(业务逻辑) │
├─────────────────────────────────────┤
│ 服务层(核心能力) │
├─────────────────────────────────────┤
│ 数据层(存储与缓存) │
└─────────────────────────────────────┘
每一层各司其职:
- 接入层:处理认证、限流、协议转换。我建议用统一的API网关,别让业务代码去操心这些事。
- 应用层:编排业务流程。比如「用户注册→资源接入→策略匹配」,这层只管流程,不管具体怎么算。
- 服务层:提供原子能力。像「负荷预测」「基线计算」「结算引擎」,每个都是独立模块。
- 数据层:负责持久化。关系库存交易数据,时序库存量测数据,缓存扛热点。
核心原则:上层依赖下层,下层不依赖上层。我曾经见过一个项目,数据层直接调了应用层的接口,结果改一个字段,全链路都要回归——那叫一个酸爽。
3.2 微服务架构:拆还是不拆?
很多朋友一上来就问:「要不要上微服务?」我的回答是:先看规模。如果你只有几百个用户,单体+分层完全够用。但需求响应平台,用户量级通常是万级甚至百万级,而且业务模块之间耦合度低——这时候微服务就是必然选择。
我建议按「业务边界」来拆。举个例子:
| 服务名称 | 职责 | 典型接口 |
|---|---|---|
| 用户服务 | 管理聚合商、终端用户信息 | 注册、认证、权限 |
| 资源服务 | 管理可调负荷、储能、充电桩 | 资源注册、状态上报 |
| 策略服务 | 生成需求响应策略 | 策略计算、下发 |
| 结算服务 | 计算补贴、收益分成 | 账单生成、对账 |
| 监控服务 | 实时数据采集与告警 | 遥测、事件推送 |
每个服务独立部署、独立数据库。服务之间通过轻量级API通信。嗯,这里要注意:别搞成「分布式单体」——就是拆了服务但数据库还共用一个,那还不如不拆。
避坑指南:我曾经把一个「策略服务」拆得太细,分成了「基线计算」「响应评估」「激励计算」三个微服务。结果每次策略下发要调5次接口,延迟从50ms飙到500ms。后来我合并了强相关的逻辑,才把性能拉回来。微服务的粒度,以「独立变更、独立部署」为准,别为了拆而拆。
3.3 技术选型:Spring Cloud vs K8s
这是大家最纠结的地方。我直接说结论:Spring Cloud 适合「微服务治理」,K8s 适合「容器编排」。两者不是二选一,而是互补关系。
3.3.1 Spring Cloud:服务治理的瑞士军刀
如果你团队Java技术栈比较强,Spring Cloud是首选。它提供了:
- Nacos:服务注册与配置中心。我个人习惯用它替代Eureka,因为Nacos支持动态配置刷新,省去了重启服务的麻烦。
- Gateway:路由、限流、熔断。比Zuul性能好不少。
- Feign + Sentinel:声明式调用 + 流量控制。我在项目中用Sentinel做过一个「按聚合商等级限流」的策略——VIP用户优先响应,普通用户排队,效果不错。
// 一个典型的Feign客户端示例
@FeignClient(name = "resource-service", path = "/resources")
public interface ResourceClient {
@PostMapping("/register")
Result<ResourceVO> register(@RequestBody ResourceDTO dto);
@GetMapping("/status/{resourceId}")
Result<StatusVO> getStatus(@PathVariable("resourceId") String resourceId);
}
小技巧:Feign接口一定要加fallback工厂。我曾经线上环境资源服务挂了,因为没有降级策略,导致用户服务线程池被打满——整个平台瘫痪了10分钟。从那以后,每个Feign接口我都配了Sentinel熔断。
3.3.2 K8s:容器编排的工业标准
如果你的团队运维能力较强,或者系统需要跨云部署,K8s是更好的底座。它解决的是「部署、扩缩容、自愈」的问题。
我建议的部署模式是这样的:
┌─────────────────────────────────────────────┐
│ K8s 集群 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 用户服务 │ │ 资源服务 │ │ 策略服务 │ │
│ │ (Pod x3) │ │ (Pod x5) │ │ (Pod x2) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────────────────────────────────┐ │
│ │ Ingress (统一入口) │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
每个服务跑在独立的Pod里,通过Service暴露内部地址。Ingress负责外部流量接入。当负荷预测高峰期到来,K8s自动扩Pod——我见过一个场景,响应事件触发时,策略服务的Pod从2个自动扩到20个,整个过程不到30秒。
我的建议:Spring Cloud + K8s 组合拳。Spring Cloud管服务间的「东西向流量」(服务调用、熔断、配置),K8s管「南北向流量」(外部请求、部署、扩缩容)。两者各司其职,互不冲突。
3.4 核心架构图:一张图看懂全局
下面这张图,是我在多个项目中沉淀下来的架构模板。你直接拿去用,基本不会出大问题。
这张图里,每一层都对应了具体的技术组件。你想想看,从用户请求进来,到最终数据落库,整个链路清晰明了。我在实际项目中,就是拿着这张图跟团队对齐的——省去了大量口舌。
3.5 技术选型对比:一张表说清楚
| 维度 | Spring Cloud | K8s | 我的建议 |
|---|---|---|---|
| 服务发现 | Nacos / Eureka | Service + DNS | 内部调用用Nacos,外部用K8s Service |
| 配置管理 | Nacos Config | ConfigMap | 动态配置用Nacos,静态配置用ConfigMap |
| 熔断限流 | Sentinel / Hystrix | 无原生支持 | 必须用Sentinel,K8s管不了这个 |
| 部署扩缩 | 需配合Jenkins | 原生HPA | K8s完胜,自动扩缩太香了 |
| 学习成本 | 中(Java开发者友好) | 高(需懂容器化) | 团队Java强选Spring Cloud,运维强选K8s |
我的经验:别纠结「全用Spring Cloud还是全用K8s」。我现在的项目是:Spring Cloud做服务治理,K8s做部署底座。每个微服务打包成Docker镜像,通过K8s部署。服务之间的调用走Feign,配置走Nacos。这套组合我用了两年,线上没出过大问题。
3.6 避坑总结:那些年我踩过的雷
- 服务拆分太细:我曾经把「资源服务」拆成「充电桩服务」「储能服务」「空调服务」三个微服务。结果每次资源注册要调三个接口,数据库还得做分布式事务——后来我合并了,世界清净了。
- 忽略配置中心:早期项目没用Nacos,每次改个数据库连接串都要重新打包部署。后来我强制所有配置外置,改配置就像改文本一样简单。
- K8s资源限制没配:有一次策略服务内存泄漏,因为没有设置limits,Pod把整个Node的内存吃光了,导致其他服务集体OOM。现在我的每个Pod都配了requests和limits,谁也别想抢谁的资源。
嗯,架构设计这块,说白了就是「权衡」。没有银弹,只有适合你场景的方案。你只要记住:分层要清晰,服务要独立,选型要务实。剩下的,交给时间去验证。
公众号:蓝海资料掘金营,微信deep3321