yhs0092 opened a new issue, #4821: URL: https://github.com/apache/servicecomb-java-chassis/issues/4821
### Steps to Reproduce 此问题为小概率偶现问题, 触发条件如下: 1. 用户使用手动指定服务端endpoint的功能(对应代码为 `org.apache.servicecomb.loadbalance.LoadbalanceHandler#handleSuppliedEndpoint` 方法). 2. 用户以 reactive 形式发起微服务调用. ### Expected Behavior 如果用户通过 Invocation localContext 传递信息, 在出现此问题时, 用户会发现虽然他正确在 Invocation localContext 中 put 了 key 和 value, 但在 get localContext 时拿到的是 null. ### Servicecomb Version 2.8.24 ### Additional Context ## 根因分析 https://github.com/apache/servicecomb-java-chassis/blob/6873ee5992ff2211a1e0efa33dd30377a914ecbf/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadbalanceHandler.java#L111-L152 问题根因在上述代码片段中. 118行调用 `handleSuppliedEndpoint` 方法, `handleSuppliedEndpoint` 方法内部会判断用户是否手动指定了服务端地址, 如果有, 则这个方法内部就调用了 `invocation.next` 方法继续走请求发送流程, 直到将请求调度到 Eventloop 线程发送再返回`true`. 而在 `handleSuppliedEndpoint` 方法返回之后, `LoadbalanceHandler` 会执行 119 行的 `invocation.addLocalContext(RetryContext.RETRY_LOAD_BALANCE, false)` 方法向 localContext 中添加新的 entry. **于是这里可能形成对 localContext 的并发访问**, 即 Eventloop 线程中的 HttpClientFilter 和 业务发送线程中的 LoadbalanceHandler 同时访问 localContext. **由于当前 Invocation 中的 localContext 为 `HashMap` 类型的, 线程不安全, 当两条线程并发向 HashMap 中put entry时, 可能出现 entry put 失效而且不报错的情况.** 从用户视角来看, 他们会发现自己设置 localContext 成功了, 而且也没有清理过它, 但就是偶尔取不到数据的情况. ## 可能的修复方案 **Invocation 中的 context 也是使用 `HashMap` 存储的, 因此理论上也有可能遇到和 localContext 一样的问题.** 我们能想到的优化方案有以下集中, 但各有缺点, 可能需要大家一起看看如何取舍. 1. localContext 的类型从 `HashMap` 改为 `ConcurrentHashMap` 这个方案的优点在于可以一次性解决其他潜在的类似的并发问题. **缺点在于`ConcurrentHashMap`不支持put null value, 而 `HashMap` 是支持的.** 因此这对用户来说算是个不兼容变化. 2. 调整 LoadbalanceHandler 的代码 将 LoadbalanceHandler 的 `handle` 方法和 `handleSuppliedEndpoint` 方法调整一下, `handleSuppliedEndpoint` 方法只返回 true 或者 false, 将 `invocation.next(asyncResp)` 挪到 `handleSuppliedEndpoint` 方法之外, 先 addLocalContext 再调用. 样例如下: ```java if (handleSuppliedEndpoint(invocation, asyncResp)) { invocation.addLocalContext(RetryContext.RETRY_LOAD_BALANCE, false); invocation.next(asyncResp); // 放到 addLocalContext 之后, 避免形成并发关系 return; } ``` 这个方案优点在于几乎对用户不感知(除了某些业务发现他们能在更早的位置获取到 `RetryContext.RETRY_LOAD_BALANCE` localContext. 缺点是只能解决这次遇到的问题, 如果另外有代码跟 LoadbalanceHandler 这里类似, 则仍然有可能触发这种问题. 3. 包装一个兼容性的 `ConcurrentHashMap` 子类型 Java-Chassis自行扩展一个 `ConcurrentHashMap` 的子类型, 修改涉及 null 的场景, 使其行为和 `HashMap` 一致, 例如将null value转换为特定的占位对象存储来规避空指针异常. 优点是可以解决所有地方可能存在的并发问题, 缺点在于这个方案的工作量是最大的, 而且如果后期 Java 在 `ConcurrentHashMap` 中增加了新的方法, Java-Chassis 可能要持续更新子类型的实现方案. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
