1. 第三方接口校验工具
调用第三方接口,来实现一些功能,比如开发票,一些返回值是必须要有的,但是供应商某些情况下可能并不会返回。是不是可以做个通用工具,提前设置一下哪些参数必须返回,没有的话就抛异常记录日志。这也就避免了写很多次相同的判断逻辑。接口返回值可以按业务要求分为三类:
- 必须有返回值(强依赖,无补偿),没有就无法继续处理,必须立即报错或中断。
- 可以没有返回值(非关键字段),有最好,没有也可以用默认值处理或忽略。
- 必须有返回值(弱依赖,可补偿),没有就触发补偿机制,如重试、兜底调用、通知人工。
此外,还要支持嵌套的返回值,有些返回值是嵌套起来的,要做的校验更复杂。
最后,这个没座!!!,为甚?因为TMD那个补偿接口调用之后还是补偿不到,气死我了,没心情做。
2. DDD Service们
Application service层,只负责编排业务逻辑。最好就是写的跟自然语言一样,方法名体现业务含义,不应该带技术实现细节,例如sendMessageAndSaveToDb就不是一个好名字,而registerNewUser就很高明。要干什么用方法名描述清楚。将具体的实现,下沉到domain service和Infrastructure Service。
Application service不应包含if/else流程逻辑过多,应该委托domain service或domain model内部封装。domian service封装业务中稳定且核心的领域逻辑,不应该涉及基础设施。
Application service,负责组织逻辑、协调各服务,最好做到是个正常人就能一眼看明白大概流程。而domain service,承载纯粹的领域逻辑,不是技术服务。技术服务调用Infrastructure Service实现。
其实就是分离业务逻辑和具体实现,代码可维护。DDD的价值在于“建模领域”,通过分层解耦,让系统具备高内聚、低耦合,强表达力的结构。
3. 为什么 null 不适合作为 Redis 缓存值
如果返回 null,你无法区分“真的设置了一个 null”还是“key 根本不存在”。
应该用"1",标识我确实设置过这个值,表示该 key 有效且存在。
4. Redisson看门狗
当你通过 Redisson 获取分布式锁时,如果没有手动指定锁的超时时间,Redisson 会默认启用看门狗机制,自动帮你延长锁的持有时间,直到你显式释放锁为止。
每隔:10秒(可配置),Redisson 会自动给锁续命一次(再延长 30 秒)
5. Redisson锁可重入
schedule依赖了admin和web,这就导致这两个的IdUtil2,用的会是相同的workId。但是吧,也没啥问题。保证admin和admin不重复,web和web不重复就ok。admin和web重了也问题不大。
Redisson 的分布式锁是可重入的,允许同一个线程多次加锁而不阻塞。
-
锁重入次数必须匹配 unlock 调用次数,否则锁不会真正释放,会导致死锁。
-
不同线程重复加锁会被阻塞,即使是同一个类或方法,只要线程 ID 不一样就不能重入。
-
锁的唯一性依赖线程 + UUID 标识,所以跨线程重入不支持(也不安全)。
6. Optional orElse() 和 orElseGet()
optional.map(a::getId()).orElse(method());
// 相当于以下代码:method() 在 orElse 调用之前就被执行了
T value = method();
result = optional.map(a::getId).orElse(value);
如果你希望只有在 Optional 为空时才执行 method(),应该用 orElseGet(),orElseGet() 接收的是 Supplier,是惰性求值,method() 只会在 Optional 为空的时候才执行。
optional.map(a::getId).orElseGet(() -> method());
7. id生成器
之前的id生成器,datacenterId和workId是随机出来的,且两者相同...,导致出问题的概率贼大。我优化了两版,最终选择了第二版。
共性:新IdUtil的id生成逻辑和原IdUtil一致,只是将datacenterId和workId改为传参设置的,除此之外不做任何改动。两个方案都依赖redis保证不重复,取值0-1023,拿到后再取高5位和低5位作为datacenterId和workId,传入IdUtil。
8. 最近工作涉及多个系统之间的数据同步,开发此类系统,有哪些方法论吗?
要考虑一致性,又要兼顾性能、解耦与演进。用事件驱动思维解耦业务系统;用可靠消息机制保障最终一致性;用幂等 + 补偿 + 可观测,托底分布式的复杂性。
一、顶层设计哲学:先明确“同步的目标是什么”
1. 是强一致性,还是最终一致性?
2. 是实时同步,还是定时同步?
3. 是单向同步,还是双向同步?
二、常见数据同步架构模式(推荐你熟悉这4种)
1. 事件驱动(Event-Driven Architecture,推荐)
适合:最终一致性、解耦、可扩展同步
2. 可靠消息最终一致性(Recommended for 业务同步)
适合:注册、订单、状态类同步(保证可靠 + 可重试)
3. 数据抓取(CDC / 变更订阅)
适合:历史系统改造、BI 分析同步、缓存构建等
4. ETL / 定时同步
适合:离线分析、次要系统同步
三、同步系统的关键机制设计
1. 幂等机制
2. 补偿机制
3. 链路追踪
4. 配置中心 / 数据订阅
9. 数据库三态问题
你有个字段是true或者false,但是数据库支持null,由于某些原因数据库写入null,会导致系统逻辑错误。
三态问题的本质是:在你只打算支持“是/否”两种状态时,数据库却默认你支持三种(是/否/未知)。你不主动约束,系统就会给你制造歧义和 bug。因此对于像 status 这样的字段,强烈建议加 NOT NULL,并给出默认值(如 DEFAULT 0),这样才能保持业务逻辑稳定、数据语义清晰。
Boolean 字段允许 NULL = 引入第三种逻辑状态 = 系统隐性 bug 温床。
10. user_agent
user_agent 是前端浏览器或客户端在发起请求时,自动携带的一个 HTTP Header 字段,其作用是标识客户端的设备、操作系统、浏览器、版本、设备类型、内核信息等信息。
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/123.0.0.0 Safari/537.36
为什么要记录 User-Agent?
1. 安全审计和风控分析 2. 登录设备识别 3. 数据统计分析 4. 异常排查
11. mysql text 存储结构分析(为什么有人觉得 TEXT 慢)
类型
存储方式
是否在行内存储
VARCHAR
存在行内(页中),定长/变长
✅ 通常在页内,效率更高
TEXT
存在页外,行内只存指针
❌ 页内有指针,值在页外查一次
所以访问 TEXT 会多一次指针寻址开销,这就是它“慢”的来源。但——这是 I/O 层级的微优化,而且:
不要做的事情:
场景
示例
为什么问题大
WHERE
查询条件
WHERE request_params LIKE '%xxx%'
不能走索引,强制全表扫描,性能极差
排序
ORDER BY response_data DESC
TEXT
不能参与有效排序,需要临时表
分组聚合
GROUP BY request_params
无法高效聚合,通常没意义还极慢
连接条件
JOIN ... ON a.request_params = b.xxx
大字段连接逻辑混乱,极易炸掉资源
允许的事情:
场景
示例
为什么安全
只做日志写入
INSERT INTO operation_log (...) VALUES (...)
写性能不会受 TEXT影响
查询时“顺带带出”
SELECT request_params FROM operation_log WHERE id = ?
主键过滤效率高,TEXT 开销可控
审计时人工查看
用日志系统页面展示 TEXT 内容(例如 truncate + tooltip 展示)
用户点开再看,不频繁
最后,用的JSON存的日志,更合适一点。
12. ip 0:0:0:0:0:0:0:1和::1
它们在 IPv6 中表示的是同一个地址 —— IPv6 的本地回环地址,相当于 IPv4 中的 127.0.0.1。
13. 反向代理(如 Nginx、Apache、F5、Kong) 或 负载均衡器 的 HTTP 请求获取ip
1. X-Forwarded-For 最常用的标准头
-
作用: 表示请求经过的代理服务器链,记录原始客户端 IP。
-
格式:X-Forwarded-For: client, proxy1, proxy2
-
其中第一个 IP 是原始客户端 IP,后面是各级代理服务器 IP。
-
使用广泛程度:非常广泛,几乎所有反向代理支持该头。
-
注意事项:需要自行提取第一个 IP 地址。
2. Proxy-Client-IP 早期 WebLogic/Apache 插件使用
- 作用: 早期 Web 服务器插件设置的客户端 IP。
- 常见场景:BEA WebLogic 插件、Apache HTTP Server 某些旧模块。
- 使用频率:已较少使用。
3. L-Proxy-Client-IP WebLogic 专用
- 作用:Oracle WebLogic 的 Web 服务器插件设置的客户端 IP。
- 使用频率:在使用 WebLogic 并配置了 HTTPClusterServlet 时出现。
4. X-Real-IP Nginx 推荐配置
- 作用:直接记录原始客户端 IP。
- 优点:不会包含多个 IP,直接就是客户端 IP。
- 使用频率: Nginx 用户最常用,安全性也更高。
推荐使用顺序的原因:
在实际生产环境中,一个请求可能经过多个代理,最前面的才是真实客户端。这个顺序设计的原则是:
- X-Forwarded-For → 可能包含多个 IP,优先提取第一个。
- X-Real-IP→ 如果由 Nginx 设置,可信度高,结构简单。
- 其他如 WebLogic 专用头(WL-Proxy-Client-IP、Proxy-Client-IP) → 如果部署环境用的是这些中间件才有可能设置。
14. 集群模式下,事务里面操作的key必须落在一个slot里面
redisTransaction.execute(stringObjectRedisOperations -> {
// 必须操作一个slot下的key
});
lua脚本也是。想用的话,需要用到Hash Tag
在 Redis Cluster 模式下,你不能直接指定 slot 编号,但你可以通过控制 key 的格式来强制多个 key 落入同一个 slot。Redis 提供了一种机制:哈希标签(Hash Tag)。
Redis 会对 key 中 {} 包裹的部分做 CRC16 % 16384 计算,作为最终的 slot 编号。只要多个 key 的 {} 中内容一样,它们就会落在同一个 slot。
15. @AllArgsConstructor和@RequiredArgsConstructor,有什么区别
@AllArgsConstructor —— 全参构造器,它会为 类中所有字段(不管是否 final、是否有默认值) 生成一个构造函数。
@RequiredArgsConstructor —— ,只为 final 和 @NonNull 字段生成构造器。
用的时候要注意,注入的时候用@RequiredArgsConstructor!!!
16. java bean is 老古董
字段命名别用is开头,老坑了,各种兼容问题。不是头一次遇到了,这回是MapStruct。
17. 关于浏览器的user-agent,用的谷歌浏览器,为何会有Safari??
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36
很多网站会用正则判断 User-Agent 字符串来识别浏览器,如果没有 Safari 字段,部分只支持 Safari 的老网站可能会拒绝访问或功能异常。为了**“骗过”这些判断逻辑**,Chrome 在自己的 UA 中加上了 Safari。 这其实就是历史兼容性导致的“伪装行为”,现代浏览器几乎都在 UA 中“扮演多个角色”,避免被不够严谨的判断逻辑误伤。
后续发展:User-Agent 将被淘汰,因为 UA 太乱、容易被伪造,Google 等浏览器厂商已经在推进一种新方案:Client Hints,它通过 HTTP Header 更精确地传递浏览器信息,且更隐私友好。