news 2026/10/18 18:07:51

SpringCloud微服务实战:从拆分边界到前后端分离与链路治理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringCloud微服务实战:从拆分边界到前后端分离与链路治理

简介:这是一套基于SpringCloud Alibaba的微服务前后端商城系统完整工程,覆盖网关、认证、商品、订单、购物车、库存等业务闭环,并集成Nacos、Sentinel、Seata、Redis、RabbitMQ等主流组件,适合作为毕业设计、微服务面试项目或企业二次开发参考。压缩包共341个文件、整体仅1.56MB,以88个Java源码、21个Vue页面、49个JavaScript脚本为主,辅以XML配置、数据库脚本和Dockerfile部署文件,后端逻辑、前端交互与容器化部署均能对照学习;文件类型还包含HTML、CSS、properties、yml等,分别承担页面样式、系统配置与启动参数,目录结构清晰,便于按模块检索。资源内附数据库脚本、架构说明文档与部署教程,可帮助读者理清服务注册发现、分布式事务、流量治理等微服务核心环节的落地方式。目前已有27人学习浏览,尤其适合有SpringBoot基础、希望从单体转向微服务实战的开发者。

1. 基于SpringCloud的微服务前后端系统:它解决的是什么问题

你有没有经历过这种时刻:单体应用在测试环境一切正常,上线后某个接口偶发超时,日志里全是乱成一团的调用堆栈,为了定位一个问题要翻几十个文件。更头疼的是,每次改一行代码就得整个项目重新构建部署,后端团队和前端团队的排期越绑越紧。我遇到的那个项目就是这样,用户量涨到一定程度后,单体架构的瓶颈不只在性能,更在团队协作的边界上。

基于SpringCloud实现微服务前后端系统,做的事情就是把过去一个臃肿的应用,拆成若干个职责单一的服务,每个服务独立部署、独立扩容,前端通过统一的网关入口访问后端能力。这套方案适合业务边界清晰、团队规模中等以上、系统需要持续迭代的开发者。别急着把所有项目都改成微服务,先搞懂它拆什么、怎么拆、拆完如何把数据一致性兜住,这篇笔记就沿着这条路讲清楚。

2. 从单体到微服务:先想清楚拆分边界,再动手搭框架

2.1 微服务不是银弹:什么业务真的需要拆成分布式系统

很多人做微服务是被“主流技术栈”推着走的,但拆服务这件事有真实成本。服务的数量上去了,原先单体里一次方法调用变成了一次网络调用,出问题的可能面从内存溢出扩展到了网络超时、序列化失败、服务雪崩。需要先给自己的系统做一个判断:是性能瓶颈,还是组织协作瓶颈,还是纯粹想跟上技术趋势。

性能瓶颈要看具体位置。如果只是某个接口慢,先做缓存、做索引、做异步化,这些手段在单体里也能用。组织协作瓶颈更接近拆分的真正理由:多个团队要并行开发、各自发布,单体应用的一个发布窗口把所有改动绑在一起,每发布一次都要多方联调。这个场景下拆分的好处是立竿见影的——每个服务有独立的生命周期。

我当时在一个交易类系统里做拆分,标准很简单:业务边界清晰、团队归属明确、数据访问相对独立。订单、用户、商品、支付这四个域各自有独立的表和独立的负责人,拆出来之后互不阻塞。相反,那种大量跨表join、业务强耦合的区域,强行拆开只是把数据库的压力变成了服务的压力。

拆分之前还有一个容易忽略的动作:梳理依赖关系。把服务之间的调用画成一张图,如果发现A调B、B调A、C同时调A和B,说明边界没切干净,这种依赖环在微服务里是排查故障的灾难。

2.2 SpringCloud全家桶选型:注册中心、网关、配置中心各选什么

SpringCloud的生态组件很多,但落到一个实际项目里,每个角色只需要一个稳定可靠的实现。注册中心是微服务的地基,服务要互相发现,靠的就是它。网关是流量的总入口,所有前端请求先进到这里再做路由。配置中心解决的是服务多起来之后配置散落各处的问题。

我常用的选型组合是:Nacos做注册中心和配置中心,SpringCloud Gateway做网关,OpenFeign做服务间调用,Sentinel做限流降级。这套组合的好处是中文资料多、问题排查方便,而且Nacos同时承担注册和配置两个职责,省去部署两套组件的运维成本。Eureka和Consul我也用过,但Eureka已经停止新版本开发,新项目不太建议再选。

稳定性方面,注册中心是绝对的单点。Nacos集群至少部署三个节点,部署时注意三个节点之间要能互相访问。配置中心强依赖Nacos的存储,配置变更属于高频操作,建议开启持久化,避免Nacos重启后配置丢失。

组件角色常用方案选择理由
注册中心Nacos同时支持注册与配置,部署简单
网关SpringCloud Gateway非阻塞模型,性能优于Zuul
服务调用OpenFeign声明式调用,代码侵入低
配置中心Nacos Config与注册中心共用,支持动态刷新
限流降级Sentinel控制台可视化,规则持久化方便

2.3 项目级服务拆分实践:用订单与用户模块演示边界划分

假设你现在要拆一个小的电商系统,最早只有一个后端工程,里面有用户模块和订单模块。拆分的核心是先把代码目录和数据库表做逻辑隔离,再逐步物理隔离。第一步,把两个模块的代码从同一个工程里按包名解耦;第二步,理清模块间的调用关系;第三步,各自引入独立的数据库配置,改造为独立的服务工程。

订单服务在创建订单时需要用户信息,但订单服务不应该直接查用户库,而是调用用户服务的接口。这个接口就是两个服务的契约,接口的入参出参一旦确定,两个团队就可以并行开发。我一般会先用一个独立的API模块来管理这些DTO和Feign接口定义,两个服务都依赖这个模块,避免接口定义不一致的问题。

// 这是用户服务暴露给订单服务的Feign接口定义,放在common-api模块中 @FeignClient(name = "user-service", path = "/api/user") public interface UserClient { @GetMapping("/{userId}") UserInfoDTO getUserById(@PathVariable("userId") Long userId); }

定义好接口后,用户服务实现这个Controller,订单服务在需要用户数据的地方注入UserClient。这样做的好处是契约先行,两边的开发可以同步推进。真正动手拆的时候,先把数据库的访问权限切分清楚,订单表不能让用户服务直接操作,用户表也不能让订单服务直接操作。

我见过不少拆分失败的案例,代码是拆开了,数据库还在共用一个库,服务A可以直接查服务B的表。表面上是微服务,本质还是单体,出了问题定位范围完全没有缩小。数据权限的边界,比代码边界更重要,拆服务的同时一定要把数据库的读写边界一并拆干净。

3. 搭建SpringCloud核心骨架:注册中心、网关与配置中心的最小可运行实现

3.1 用Nacos作为注册中心与配置中心:依赖引入与启动配置

整个微服务系统里,最先启动的一定是Nacos。它的作用是让所有服务实例在这里登记自己的地址,也让其他服务能从这里找到目标服务的实例列表。服务提供方启动时向Nacos注册自己的IP和端口,服务消费方从Nacos获取实例列表并做负载均衡。

Nacos的部署有两种方式,一种是直接下载压缩包启动,另一种是使用Docker。本地开发和测试常用压缩包方式,生产环境我一般用Docker Compose编排,这样方便管理多个中间件。启动之后访问Nacos控制台,能看到所有服务实例的注册情况,这个页面在排查问题时是最高频使用的工具之一。

# docker-compose.yml 中Nacos的部署配置(单节点模式,用于开发测试环境) services: nacos: image: nacos/nacos-server environment: - MODE=standalone - NACOS_AUTH_ENABLE=false ports: - "8848:8848"

每个需要注册到Nacos的微服务,都需要在pom.xml中引入Nacos的注册与配置依赖。注意注册中心的依赖和配置中心的依赖是两个不同的artifact,很多新手会漏掉其中一个,导致服务能注册但读不到配置。

<!-- 注册中心客户端依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- 配置中心客户端依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>

依赖引入之后,就是配置文件的写法。这里有个关键点:服务启动时先从Nacos配置中心拉取配置,然后才启动应用上下文,所以bootstrap.yml里的配置要指向Nacos地址。如果在bootstrap.yml里配错了Nacos的地址,服务启动后会提示找不到配置,然后使用本地配置兜底,这个行为很容易掩盖问题。

# bootstrap.yml 服务启动时最先加载的配置 spring: application: name: order-service cloud: nacos: config: server-addr: 127.0.0.1:8848 file-extension: yaml discovery: server-addr: 127.0.0.1:8848

配置文件里的spring.application.name很重要,它决定了服务在Nacos里的身份标识,也决定了从配置中心读取哪个dataId的配置。Nacos配置中心的dataId命名规则是“服务名.yaml”,对应上面的配置,order-service启动时会去加载order-service.yaml这个配置。

3.2 网关层配置:SpringCloud Gateway的路由、断言与过滤器

网关是所有外部流量的第一道关卡。在网关里做统一的路由转发、跨域处理、认证鉴权。网关本身不写业务逻辑,它的职责是把请求转发到对应的服务。SpringCloud Gateway的路由配置由三个部分组成:id标识、uri转发目标、predicates断言条件。

# application.yml 中的网关路由配置 spring: cloud: gateway: routes: - id: user-service uri: lb://user-service predicates: - Path=/api/user/** filters: - StripPrefix=1 - id: order-service uri: lb://order-service predicates: - Path=/api/order/** filters: - StripPrefix=1

这段配置里lb://user-service表示使用负载均衡方式从Nacos中获取user-service的实例列表,然后转发请求。StripPrefix=1过滤器的作用是将路径前面的层级去掉。比如前端请求/api/user/101到达网关后,经过StripPrefix=1,实际转发到user-service的路径是/user/101。

Predicates可以配置多个条件,比如同时匹配路径和请求方法,多个断言条件之间的关系是“与”。生产环境我一般还会加上一个全局过滤器,用于记录请求日志、生成traceId、校验Token,这个过滤器不修改请求转发路径,只做横切逻辑。

// 全局过滤器:为每个请求生成traceId并校验JWT Token @Component public class AuthGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token = exchange.getRequest().getHeaders().getFirst("Authorization"); if (token == null || !JwtUtils.verify(token)) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } // 校验通过后,把userId放到请求头中,传递给下游服务 ServerHttpRequest request = exchange.getRequest().mutate() .header("X-User-Id", JwtUtils.getUserId(token)) .build(); return chain.filter(exchange.mutate().request(request).build()); } @Override public int getOrder() { return -100; // 数值越小越先执行 } }

这段过滤器的代码看起来简单,但订单很重要。getOrder()返回-100,是为了让这个过滤器在任何路由过滤器之前执行。JWT校验放在网关这一层,下游服务就不需要重复校验Token,只需要信任网关传入的X-User-Id请求头。

3.3 服务间通信与负载均衡:OpenFeign调用链路的搭建

服务拆开之后,服务之间仍然需要互相调用。OpenFeign是SpringCloud生态里声明式HTTP客户端的事实标准。你只需要定义一个接口,加上注解,Spring就会自动生成实现,把方法调用转换成HTTP请求。相比直接用RestTemplate手写URL拼接和参数封装,OpenFeign的代码量少得多,而且集成了Ribbon的负载均衡能力。

// 订单服务中通过Feign调用用户服务的接口 @FeignClient(name = "user-service", fallback = UserClientFallback.class) public interface UserClient { @GetMapping("/user/{id}") UserInfoDTO getUserById(@PathVariable("id") Long userId); }

使用OpenFeign时有几个参数需要关注。connectTimeout是建立连接的超时时间,readTimeout是等待响应的超时时间。服务间的调用一般设置readTimeout为3-5秒,不要设得太长,否则一个下游服务慢,会拖垮上游服务的线程资源。还需要注意OpenFeign默认继承Ribbon的负载均衡策略,轮询是最常用的策略,如果服务实例性能差异大,可以调整为权重策略。

Feign的服务降级需要开启熔断配置。在application.yml中配置feign.circuitbreaker.enabled=true,然后定义fallback类,实现相同接口,返回兜底数据。但降级逻辑不要太复杂,返回一个默认值即可,真正复杂的业务补偿需要放在服务内部处理。

# 订单服务调用用户服务失败时的降级处理 @Component public class UserClientFallback implements UserClient { @Override public UserInfoDTO getUserById(Long userId) { UserInfoDTO dto = new UserInfoDTO(); dto.setUserId(userId); dto.setUserName("未知用户"); // 降级时返回兜底信息 return dto; } }

服务间通信还有一个容易忽略的点是序列化方式。默认使用Jackson进行JSON序列化,如果服务间传递LocalDateTime等Java 8时间类型,需要配置JavaTimeModule,否则会报序列化异常。我在某个项目里就因为忘记配置这个,服务间调用时不时报错,看起来像是偶发网络问题,排查了很久才发现是时间格式序列化的坑。

4. 前后端分离落地:认证鉴权、跨域与接口对接实战

4.1 JWT Token在微服务链路中的传递:网关统一鉴权的方案

前后端分离架构里,前端是一个独立部署的应用,后端被拆成了多个微服务。Token的安全传输是一个关键设计。我的做法是:前端登录成功后拿到JWT Token,之后每次请求在Authorization请求头中携带这个Token,网关负责校验Token的合法性,然后将用户身份信息透传给下游服务。

这样做有一个好处,用户身份的校验逻辑只需要写一遍。网关校验Token通过后,把解析出来的userId放入请求头X-User-Id中,下游服务只要信任这个请求头即可。需要注意的是,下游服务不能再暴露任何外部直接访问的端口,必须保证所有请求都经过网关。

// JWT工具类,只保留核心的生成与校验方法 public class JwtUtils { private static final String SECRET = "your-secret-key-change-in-production"; // 生成Token public static String generateToken(Long userId, String username) { return Jwts.builder() .setSubject(username) .claim("userId", userId) .setExpiration(new Date(System.currentTimeMillis() + 24 * 3600 * 1000)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } // 校验Token,成功返回userId,失败抛出异常 public static Long parseToken(String token) { Claims claims = Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token) .getBody(); return claims.get("userId", Long.class); } }

网关层的过滤器已经在前面写过了,这里补充一个细节:对于登录接口、验证码接口这种白名单地址,不需要校验Token。白名单的判断逻辑通常放在网关过滤器的前面部分,做一个路径前缀匹配即可。有一种更优雅的做法是把白名单配置到Nacos配置中心里,这样调整白名单不需要重新发布网关。

Token过期是前端经常遇到的问题。我习惯在Token中设置24小时有效期,前端在收到401响应时,自动跳转到登录页。如果做单点登录或者多端登录控制,还需要在Redis里存储Token的状态,这样后端可以主动让某个Token失效。这种主动失效的能力在用户修改密码或者被强制下线时非常有用。

4.2 跨域问题的三种处理方式与配置示例

跨域是前后端分离项目必然遇到的问题。浏览器的同源策略会阻止前端页面访问不同域名下的接口。前端开发环境通常运行在localhost的某个端口,而后端接口在另一个端口,这本身就构成了跨域。跨域问题在微服务架构下会被放大,因为后端有多个服务,你可能需要分别处理每个服务的跨域配置。

第一种方式是网关统一处理跨域。这是最推荐的做法,所有跨域相关的处理逻辑集中在网关这一层,后端服务完全不用关心跨域问题。在SpringCloud Gateway中,可以通过配置CorsGlobalConfiguration实现。

# 网关统一配置跨域规则 spring: cloud: gateway: globalcors: cors-configurations: '[/**]': allowedOriginPatterns: "*" allowedMethods: - GET - POST - PUT - DELETE - OPTIONS allowedHeaders: "*" allowCredentials: true maxAge: 3600

第二种方式是在每个微服务上添加@CrossOrigin注解,或者在WebMvcConfigurer里配置。这种方式只适用于服务直接被前端访问的场景,而且存在跨域配置分散、不一致的风险。第三种方式是使用Nginx反向代理,让前端请求和后端接口域名一致,从而避免跨域,这种方式适合生产环境。

我对生产环境的建议是:网关统一处理跨域,但不推荐直接开启allowCredentials=true配合allowedOriginPatterns=*,因为携带凭证的跨域请求本身就存在CSRF风险。更稳妥的做法是只对需要携带Cookie的域名开放credentials,JWT场景下Token放在Authorization头里,其实可以不依赖Cookie。

跨域验证有一个小技巧:浏览器发起复杂请求时会先发一个OPTIONS预检请求。如果预检请求没有得到正确的响应,正式请求就不会发出。排查跨域问题时,先看Network面板里有没有OPTIONS请求,再看这个请求返回的状态码和响应头里的Access-Control-Allow-Origin,基本就能定位问题所在。

4.3 前端调用网关的接口约定:响应体结构、状态码与错误处理

前后端对接效率低,很大一部分原因是响应体结构不统一。每个服务都有自己的返回格式,前端针对不同接口写不同的解析逻辑,这种做法在单体时代还能忍,微服务时代接口多了之后就成了维护灾难。我习惯在网关内做统一的响应体包装,或者要求后端服务都使用同一种REST响应格式。

统一响应体包含三个字段:code表示业务状态码,message是提示信息,data是真正的业务数据。业务状态码和HTTP状态码需要区分开,HTTP状态码表示请求本身是否成功,而业务状态码表示业务逻辑是否正常。

{ "code": 200, "message": "操作成功", "data": { "orderId": 12345, "status": "PAID" } }

这个约定的背后有一个前端可以依赖的规则:code为200时,data字段一定不为空;code非200时,前端需要弹出message中的错误提示。网关在转发响应时,也会做一些特殊处理,比如当服务返回404时,网关将响应体包装成统一格式再返回。

前端的Axios拦截器是这套约定落地的地方。在响应拦截器中,先判断HTTP状态码是否为200,再判断业务code是否为200,两层判断之后才算一次成功请求。401跳转登录、403提示无权限、500统一提示服务器繁忙这个层次的逻辑放在拦截器里统一处理。

服务间调用也有一个类似的问题。OpenFeign调用下游服务时,如果下游服务返回500,Feign默认会抛出异常。我建议在Feign的配置中关闭默认的错误解码器,替换为自定义的ErrorDecoder,这样可以把下游服务的错误信息包装成有意义的异常类型,便于上层服务记录日志和做降级处理。

5. SpringCloud微服务踩坑排查:五个高频故障与修复记录

5.1 Feign调用报Load balancer does not contain an instance for the service

现象:服务A调用服务B,运行一段时间后突然报错,提示Load balancer does not contain an instance for the service,但检查Nacos控制台,服务B明明在线。

原因:这个错误通常有两种情况。第一种是服务B的实例在Nacos中已下线,但服务A的本地缓存还没有刷新。第二种是服务A和B不在同一个Nacos命名空间或分组,服务A根本发现不了服务B。

解决:先核对两个服务的bootstrap.yml配置,确认namespace和group一致。生产环境我建议所有服务统一使用同一个命名空间和分组,不推荐用命名空间做环境隔离。如果命名空间一致,再查网络连通性,从服务A的宿主机telnet服务B的IP和端口,网络不通时Nacos拿到的实例信息也白搭。

# 排查步骤 1. 登录Nacos控制台,查看服务B的实例列表是否正常 2. 查看服务A的日志,确认是否周期性刷新了服务列表 3. 检查服务A的负载均衡缓存开关:spring.cloud.nacos.discovery.cache.enabled

5.2 网关路由503:服务注册了但路由不生效

现象:服务成功注册到Nacos,网关的配置文件里也写好了路由规则,但请求转发时返回503 Service Unavailable。

原因:网关使用lb://前缀做负载均衡路由时,需要依赖注册中心发现服务实例。如果网关的注册中心配置和服务不在同一个Nacos环境,或者网关的依赖中少了loadbalancer的jar包,就会出现这个现象。

解决:检查网关的pom.xml,确认引入了spring-cloud-starter-loadbalancer依赖。SpringCloud 2020版本之后Ribbon被移除了,必须手动引入LoadBalancer。另外检查网关的bootstrap.yml,确认discovery的server-addr指向正确的Nacos地址。

我当时排这个坑花了半天,原因是网关工程只用了一个简单的依赖引用,没注意版本升级后负载均衡器的实现变了。从Ribbon切换到LoadBalancer之后,这个报错一度成为微服务新手最常见的问题榜首。

5.3 配置中心修改配置不生效:@RefreshScope的失效边界

现象:在Nacos配置中心修改了配置并点击发布,但服务运行时的配置值没有变化,必须重启服务才能生效。

原因:SpringCloud Config的自动刷新机制依赖@RefreshScope注解。如果Bean没有被标注@RefreshScope,配置变更后不会重新创建Bean,自然读不到新值。还有一个原因是服务没有引入actuator依赖,缺少/refresh端点。

解决:在需要动态刷新的Bean上添加@RefreshScope注解,并且确保服务引入了spring-boot-starter-actuator依赖。如果配置里有数据库连接、线程池这类资源型配置,不建议使用动态刷新,这类配置的变更涉及底层资源的重新初始化,直接刷新容易产生连接池泄漏。

// 配置类上加上@RefreshScope,配置变更后无需重启 @RefreshScope @Component public class DynamicConfig { @Value("${order.timeout:5000}") private int orderTimeout; // getter方法... }

5.4 服务间传递用户信息丢失:RequestInterceptor没生效

现象:网关正确解析了Token,把userId放到了X-User-Id请求头中。但用户服务调用订单服务时,订单服务读取不到X-User-Id,用户信息丢失。

原因:OpenFeign发起HTTP请求时,默认不会传递当前请求的Header。X-User-Id是网关自定义的头,Feign不会自动转发。需要配置一个RequestInterceptor,把当前请求的Header复制到Feign请求中。

解决:在服务中增加RequestInterceptor的实现,将RequestContextHolder中当前请求的所有Header复制到Feign的请求模板中。注意排除一些不安全的Header,比如Host、Content-Length。

// 将当前线程的请求头传递到Feign发出的请求中 @Configuration public class FeignHeaderConfig { @Bean public RequestInterceptor requestInterceptor() { return template -> { ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attrs != null) { HttpServletRequest request = attrs.getRequest(); String userId = request.getHeader("X-User-Id"); if (StringUtils.hasText(userId)) { template.header("X-User-Id", userId); } } }; } }

这个坑的出现频率极高,因为本地调试时不经过网关,直接调用服务接口,Feign调用链路短,不容易暴露问题。一上了生产环境,链路变成前端到网关到用户服务到订单服务,用户信息就在第二跳丢失了。

5.5 跨域请求被网关拦截:CORS配置冲突

现象:前端请求网关时,控制台报CORS错误,提示没有Access-Control-Allow-Origin响应头。但网关已经配置了跨域,而且单独请求后端服务时跨域正常。

原因:网关和应用层的CORS配置互相冲突。OPTIONS预检请求到达网关时,如果网关的CORS配置和应用层的CORS配置同时生效,响应的Header可能会被覆盖,导致预检失败。

解决:如果使用网关统一处理跨域,需要在后端服务中关闭SpringMVC的CORS处理,避免双重处理。在后端服务的配置中添加spring.web.cors.enabled=false,或者在后端服务中移除@CrossOrigin注解和CorsFilter。Gateway的CORS配置要检查CorsWebFilter是否注册了多次。

解决跨域问题的关键是确定谁负责跨域这个职责。选择了网关统一处理,其他服务就不再关心跨域;反过来,选择了每个服务自治跨域,网关就不必配置CORS规则。职责重叠就是翻车的起点,这条经验也适用于其他横切逻辑的处理。

6. 微服务链路治理的下一步:从可用到可观测

服务拆分之后,故障定位的难度没有降低,反而因为链条变长变得更难排查。订单请求先到网关,网关转发给订单服务,订单服务调用用户服务,用户服务查询数据库,任何一个环节慢几毫秒,都可能积累成一次超时。这时候,链路追踪就不再是可选项,而是必需品。

我习惯在服务中集成Sleuth和Zipkin,为每个请求生成一个traceId,这个ID会在服务间调用时通过请求头传递。日志里有了traceId,就可以把散落在不同服务里的日志串成一条完整的时间线。排查问题的方式从“逐台服务器翻日志”变成了“按traceId聚合一次查询”,效率提升一个数量级。

限流降级的配置同样应该在生产环境前置完成。Sentinel的QPS限流、线程数限流、熔断降级这些规则,不要在出了故障之后才匆忙配置,而是提前在压测阶段就调到合理值。我见过很多系统在正常流量下运行良好,一到促销或活动时段就雪崩,根源就是没有提前压测和配置限流规则。

# Sentinel常用的限流规则配置示例(通过控制台或Nacos持久化) 规则类型: QPS限流 资源名: /api/order/create 阈值: 1000 降级策略: 快速失败 超时时间: 200ms

可观测性和限流配置做完之后,还要做一层服务间的数据一致性兜底。微服务拆分了数据表,分布式事务是个绕不过去的话题。Seata可以处理强一致场景,但性能损耗明显。对于大多数业务,我推荐最终一致性方案,用本地消息表或MQ消息队列来保证。

到现在为止,从拆分边界、框架搭建、前后端对接,到踩坑总结和链路治理,SpringCloud微服务系统的落地路径基本完整了。做这套东西最大的收获不是学会了几个组件,而是建立了一套系统性的排查思路。日志怎么打,traceId怎么传,异常怎么分类,这些思考方式比框架本身更值钱。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 18:07:32

注意力机制叠加图卷积的多任务谣言检测:源码构建与工程实践

简介&#xff1a;面向自然语言处理毕设与期末大作业的多任务谣言检测Python源码与配套数据集。项目基于2022届毕业论文优化&#xff0c;采用BERT编码器获取词向量与句向量&#xff0c;再接入BiGCN或Self-Attention完成谣言检测与立场分类&#xff0c;并重新整理了PHEME、Semeva…

作者头像 李华
网站建设 2026/10/18 18:06:06

希尔排序详解:从插入排序到分组跳跃,原理、代码与复杂度分析

我可以先给个结论&#xff1a;希尔排序是那种“看起来是插入排序的魔改版&#xff0c;实际上讲究却很多”的算法。你如果已经会写插入排序&#xff0c;再看希尔排序的代码会觉得特别眼熟&#xff0c;可一旦被问到“为什么分组之后能变快”这类原理题&#xff0c;很多人又说不清…

作者头像 李华
网站建设 2026/10/18 18:05:13

安全审计不是找漏洞,而是构建可信交付链

1. 安全审计不是“找漏洞比赛”&#xff0c;而是构建可信交付链的底层能力 “security-audit-skill”这个标题乍看像一个技术标签&#xff0c;但在我带过二十多个交付型研发团队、亲手做过137次代码级安全审计之后&#xff0c;越来越确信&#xff1a;它根本不是某种可速成的“…

作者头像 李华
网站建设 2026/10/18 18:04:59

gRPC反射服务详解:原理、接入与调试实战

调试 gRPC 服务&#xff0c;最难的不是写代码&#xff0c;而是"看不清楚"。REST 接口还能用浏览器点点页面&#xff0c;gRPC 走的是二进制 Protocol Buffers 协议&#xff0c;消息结构全都藏在 proto 文件里。我印象最深的一次&#xff1a;接手别人留下的老服务&…

作者头像 李华
网站建设 2026/10/18 18:04:58

SoapUI 5.8.0接口测试工具:从Excel数据驱动到CI回归实战

简介&#xff1a;SoapUI 5.8.0是一款广泛使用的开源接口测试工具安装包&#xff0c;专为REST与SOAP服务调试、功能验证及性能测试设计&#xff0c;适合测试工程师、开发人员及API初学者使用。压缩包共包含316个完整文件&#xff0c;体积约122MB&#xff0c;内部以231个jar核心依…

作者头像 李华
网站建设 2026/10/18 18:04:28

Kafka跨集群数据同步方案对比:MirrorMaker 2.0、Connect与自研管道实战

从"把Kafka数据同步到另一个Kafka"这个需求进入正文。我自己最早接触这件事的时候&#xff0c;第一反应也是"消费一条、生产一条"那么简单&#xff0c;直到某次数据对不上账&#xff0c;从凌晨排查到天亮&#xff0c;才意识到这里面的坑比想象中多得多。同…

作者头像 李华