alibaba / alibaba/Sentinel

优化CtSph#lookProcessChain 创建拦截链方法(减少第一次创建拦截链持有锁占用时间,提升rt,减少gc压力)

Open
#3,513 8 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
23.1k
Forks
8.1k
PR merge metrics
No merged PRs in 30d

Description

### 原逻辑:
![Image](https://github.com/user-attachments/assets/c47f3fcc-7345-454d-ad9f-7fdb58555eac)

chainMap 是一个 volatile 修饰的 Map。首次获取 ProcessorSlotChain 时,如果为空,旧逻辑会进入同步块,new 一个新的 map,并通过 putAll 拷贝原有数据后再新增,最终整体替换 chainMap,利用 volatile 替换引用实现可见性。

### 优化后:
![Image](https://github.com/user-attachments/assets/46603413-a7e7-4629-979c-5ee99af7c285)
新逻辑为在同步块中直接put。(synchronized本身保证可见性)

### 好处:
1: **提高rt:**
避免在高并发下频繁进行 putAll 拷贝,特别是在 map 较大(如含有数百个资源)的情况下,putAll花费的时间越多,优化可以减少锁占用时间,从而提高rt。

2: **减少GC压力:**
直接put无需频繁创建大量短生命周期的临时 map,降低内存占用和 GC 负担。

**理由:**
看了整个代码后,发现真正使用chainMap的地方也只有lookProcessChain这一个方法,其余的方法比如resetChainMap、getChainMap是**default**修饰的,仅仅用于测试调用,**只能在当前包调用**,所以这里我没有加synchronized。而entrySize方法是一个**public**修饰的方法,为了保证其可见性,我使用了synchronized关键字。

synchronized关键字本身通过内存屏障保证了可见性,因此chainMap不需要加**volatile**关键字修饰。

类似的实现比如java中的Collections.synchronizedMap()。
![Image](https://github.com/user-attachments/assets/d01362fa-7f95-4f57-a292-770890fe5213)

**后续思考:**

![Image](https://github.com/user-attachments/assets/a914b1e7-3295-4f55-9cb5-9499e7319dbd)

当资源数限制(如 6000)放开后,或者可以允许超一点点去追求极致的性能的话,代码还可以进行如下优化:

chainMap使用ConcurrentHashMap,不需要显示进行加锁,直接采用ConcurrentHashMap#computeIfAbsent方法,这样锁的粒度更小,性能更好。

![Image](https://github.com/user-attachments/assets/35886279-1fe3-4010-af05-33fd448d0723)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.