[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、共享库、认证信息、超时问题、设计经验、技术优化

相关推荐
白远山28 分钟前
智慧场馆解决方案软件开发实战:从架构设计到落地部署指南
java·开发语言·架构·需求分析
m0_5873830043 分钟前
折扣卡CPS软件开发实战:从系统架构设计到上线指南
java·小程序·架构·需求分析
敲代码的小霖1 小时前
【开源推广】NovaNova Studio Agent驱动的图片生成、视频生成、漫剧短剧、无限画布项目 v0.1.7版本
开源软件·无限画布
Daorigin_com1 小时前
从“出海合规”到“全域合规”:道本科技用数字底座重构企业合规的逻辑
软件工程·团队开发·软件构建·需求分析·个人开发·设计规范·结对编程
Dawson Zhu1 小时前
基于 OKF 知识图谱的 Text2SQL 领域知识注入实践——半导体晶圆厂数据资产知识库 MVP 剖析
人工智能·语言模型·架构·aigc·agi
小狼154541 小时前
浏览器插件怎样实现可靠的批量网页操作?以发票申请任务为例
chrome·ai编程
晚安日记wanna1 小时前
批量请求失败只弹一个 Toast面试官想听五层
前端·面试·架构
大模型码小白1 小时前
告别造假数据,直接连数据库查真实时序数据喂给 TimechoAI 大模型
java·数据库·人工智能·microsoft·架构
晚安日记wanna1 小时前
大表 DDL 面试翻车现场Online DDL 为什么还会锁死业务
数据库·面试·架构
晚安日记wanna1 小时前
MySQL 主从延迟别只答并行复制
数据库·面试·架构