[Day14] 微服务开发中 `contract - common` 共享库的问题排查与解决

目录

  1. 前言
  2. 问题一:服务间调用认证信息丢失
  3. [问题二:Feign 调用超时导致业务失败](#问题二:Feign 调用超时导致业务失败)
  4. 总结与反思
  5. 写在最后

前言

在开发 contract - common 这个共享基础库时,尽管代码量不多,却遭遇了不少棘手问题。这些问题涵盖设计层面、使用方式以及框架本身的局限。本文旨在记录这些问题,既为自己留存记忆,也期望能为面临类似困境的开发者提供参考。

问题一:服务间调用认证信息丢失

现象描述

在实际应用中,发现服务间调用频繁失败,日志中明确报错 401 未授权。然而,直接调用接口却一切正常。例如,以下是日志中的错误信息:

log 复制代码
2025 - 12 - 28 10:23:15 | ERROR | c.c.c.feign.ContractFeignClient | Failed to call contract service: 401 Unauthorized

这就好比你去一个地方,正常走大门能进去,但让别人帮你带个东西进去却被告知没权限,很是奇怪。

排查过程

首先,对 Feign 客户端的配置展开检查,结果发现请求头竟然没有传递过去。以下是当时的代码(问题版本):

java 复制代码
// 当时的代码 - 问题版本
@FeignClient(name = "contract - management - service")
public interface ContractFeignClient {
    @GetMapping("/api/contracts/{id}")
    ContractDTO getContractById(@PathVariable("id") Long id);
}

通过抓包分析,进一步确认 Feign 调用确实没有携带 Authorization header。这就像你出门没带钥匙,自然无法打开某些门。

根本原因

Feign 客户端在发起调用时,不会自动传递当前请求的认证信息。这是分布式系统中的常见状况,每个服务都拥有独立的请求上下文。就如同不同的房间有各自独立的门锁,一个房间的钥匙不能自动打开另一个房间的门。

解决方案

为解决该问题,需要实现 RequestInterceptor 接口,手动从当前请求上下文中提取认证信息并传递。具体代码如下:

java 复制代码
@Configuration
public class FeignConfig implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        ServletRequestAttributes attributes =
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();

            // 提取并传递认证相关 Header
            String token = request.getHeader("Authorization");
            if (token != null) {
                template.header("Authorization", token);
            }

            // 传递用户上下文信息
            String userId = request.getHeader("userId");
            String username = request.getHeader("username");
            String tenantId = request.getHeader("tenantId");

            if (userId != null) template.header("userId", userId);
            if (username != null) template.header("username", username);
            if (tenantId != null) template.header("tenantId", tenantId);
        }
    }
}

这段代码就像是一个贴心的小助手,它会在你出门前,帮你检查并带上所有需要的钥匙。

后续优化

后续发现内部服务调用还要走一遍鉴权,这无疑是对资源的浪费。于是,又增加了一个内部免鉴权的机制。虽然具体细节会在后面介绍鉴权中心时详细阐述,但这里简单提及一下:

java 复制代码
// 在拦截器中添加内部调用标识
template.header("X - Internal - Auth - Secret", internalSecret);

// 网关识别后跳过鉴权

这就好比在自己家里,有些门不需要钥匙就能打开,提高了通行效率。

问题二:Feign 调用超时导致业务失败

现象描述

某个批量处理任务时常失败,报错信息为 Read Timeout。如下是日志中的记录:

log 复制代码
2025 - 12 - 08 15:42:30 | ERROR | c.c.c.feign.ContractFeignClient | Read timed out executing GET http://contract - management - service/api/contracts/search

想象一下,你让别人在规定时间内帮你找一样东西,但对方找得太久,时间到了还没找到,就会出现这种情况。

排查过程

对业务代码进行仔细检查后发现,该任务需要调用另一个服务的批量查询接口。当数据量庞大时,对方服务处理时间变长,超出了 Feign 的默认超时时间。这就像你给别人的时间不够,对方还没完成任务,就被判定失败了。

根本原因

Feign 的默认超时设置相对较短,具体如下:

  • 连接超时:10 秒
  • 读超时 :60 秒
    对于一些复杂的查询操作而言,60 秒显然难以满足需求。这就好比你让别人做一件复杂的事情,却只给了很少的时间,事情自然很难完成。

解决方案

为解决超时问题,可在配置文件中对超时时间进行调整:

yaml 复制代码
feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 60000
      # 针对特定服务的配置
      contract - management - service:
        connectTimeout: 5000
        readTimeout: 120000  # 2分钟

同时,对被调用方的查询接口进行优化,增加索引和分页支持。这就像是给对方提供了更好的工具,让他能更快地完成任务,同时也给了他足够的时间。

经验教训

  • 不同类型的接口应该设置不同的超时时间:就像不同的任务需要不同的时间来完成,不能一概而论。
  • 复杂操作要考虑异步处理,不要长时间阻塞:比如你在等一个很长时间才能完成的任务时,可以先去做其他事情,等它完成了再回来处理。
  • 做好降级和重试机制:当事情没做好时,要有备用方案,再尝试一次。

总结与反思

回顾这些问题,多数出自设计层面,而非技术难点。以下是总结的几点经验:

设计层面

  1. 明确定位:共享库应作为"接口层",不依赖具体实现。这就像一个标准的接口,只规定了能做什么,而不关心具体怎么做。
  2. 稳定优先:接口变更务必谨慎,保持向后兼容。就像你给别人提供了一个工具,不能随意改变它的使用方式,不然别人就不会用了。
  3. 充分测试:共享库的 bug 会波及所有服务,所以要进行充分测试,确保质量。

技术层面

  1. Feign 配置:超时、重试、降级等配置都不可或缺,以保障服务的稳定性。
  2. 序列化规范:使用注解明确控制 JSON 序列化,让数据传输更加规范。
  3. 枚举设计:考虑向前兼容,避免使用展示值作为标识,防止出现兼容性问题。

写在最后

在编写这个共享基础库的过程中,遇到的问题比预期更多。然而,每解决一个问题,对微服务架构的理解便更深入一层。希望这些排查记录能为同样从事微服务开发的同学提供帮助。若你也遇到类似问题,欢迎交流讨论!

下期会稍微花点时间将下若依框架在本项目中的应用和改造!

标签:微服务、Feign、共享库、认证信息、超时问题、设计经验、技术优化

相关推荐
一只鱼丸yo15 小时前
从单体到微服务:一次真实迁移实战
微服务·云原生·架构
桌面运维家16 小时前
vDisk VOI架构IO瓶颈怎么办?Windows优化实战
windows·架构
Blossom.11817 小时前
Transformer架构优化实战:从MHA到MQA/GQA的显存革命
人工智能·python·深度学习·react.js·架构·aigc·transformer
Python_Study202517 小时前
制造业数据采集系统选型指南:从技术挑战到架构实践
大数据·网络·数据结构·人工智能·架构
喵叔哟17 小时前
8.健康检查与监控
架构·.net
冬奇Lab18 小时前
【Cursor进阶实战·07】OpenSpec实战:告别“凭感觉“,用规格驱动AI编程
人工智能·ai编程
DKunYu18 小时前
9.熔断和限流 - Alibaba Sentinel
spring cloud·微服务·sentinel
IamZJT_18 小时前
拒绝做 AI 的“饲养员” ❌:前端程序员在 AI 时代的生存与进化指南 🚀
前端·ai编程
踏浪无痕18 小时前
JobFlow 负载感知调度:把任务分给最闲的机器
后端·架构·开源