firebase / firebase/firebase-admin-java

connection keepAlive does not apply.

未关闭
#880 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
needs-triage
主要语言
Java
星标
620
派生
305
平均合并
3 小时 23 分钟
30 天内合并 PR
1

描述

### [REQUIRED] Step 2: Describe your environment

* Operating System version: linux
* Firebase SDK version: firebase-admin-8.1.0.jar
* Library version: 8.1.0.jar
* Firebase Product: Cloud Messaging (auth, database, storage, etc)

### [REQUIRED] Step 3: Describe the problem
We confirmed that keepAlive is applied when using firebase-admin version 6.8.0.
However, after upgrading to firebase-admin 8.1.0, keepAlive is not applied and the connection is immediately disconnected.

#### Steps to reproduce:
firebase-admin 6.8.0 FirebaseMessagingClient.sendSingleRequest()
![image](https://github.com/firebase/firebase-admin-java/assets/5026821/0344f4e8-b2aa-49a4-b76d-508b22c68cfe)
(apache httpclient-4.5.11) ConnectionHolder.releaseConnection() is called due to line 129.
At this time, this.released changes from false to true.
After that, ApiClientUtils.disconnectQuietly(response) -> (apache httpclient-4.5.11) ConnectionHolder.abortConnection() on line 132 is called, but the connection is not disconnected because ConnectionHolder.released = true.
![image](https://github.com/firebase/firebase-admin-java/assets/5026821/8cec24c6-4e52-4c9b-9954-e095daeee24e)
![image](https://github.com/firebase/firebase-admin-java/assets/5026821/67d9df90-b7e5-43cf-acf0-1e7e82ae7275)

However, in firebase-admin 8.1.0, ConnectionHolder.releaseConnection() is not called, but ConnectionHolder.abortConnection() is called, so the connection is disconnected.
![image](https://github.com/firebase/firebase-admin-java/assets/5026821/0896d325-4948-4d2f-8dcd-160e0f3fc2a6)

What happened? How can we make the problem occur?
Establishing connections is expensive. Therefore, recycling connections for a certain period of time can help improve performance. According to what I have checked, the code of the latest version, 9.2.0, is also the same.

#### Relevant Code:

```
private FirebaseMessaging initializeFirebaseMessaging()
throws IOException {
CloseableHttpClient httpClient = newHttpClient(proxyHost, proxyPort);

HttpTransport httpTransport = new ApacheHttpTransport(httpClient);

FirebaseOptions options = FirebaseOptions.builder()
// need credentials etc
.setHttpTransport(httpTransport)
.build();

FirebaseApp.initializeApp(options);
return FirebaseMessaging.getInstance();
}

private CloseableHttpClient newHttpClient(String proxyHost, int proxyPort) {
HttpClientBuilder httpClientBuilder = ApacheHttpTransport.newDefaultHttpClientBuilder()
.evictExpiredConnections()
.setKeepAliveStrategy(
new CustomConnectionKeepAliveStrategy(
TimeUnit.MINUTES.toMillis(3)))
.setConnectionTimeToLive(10, TimeUnit.MINUTES)
.setDefaultRequestConfig(requestConfig())
.disableCookieManagement();
return httpClientBuilder.build();
}

public class CustomConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy {

public static final CustomConnectionKeepAliveStrategy INSTANCE =
new CustomConnectionKeepAliveStrategy(TimeUnit.SECONDS.toMillis(50),
DefaultConnectionKeepAliveStrategy.INSTANCE);

public static final ConnectionKeepAliveStrategy NO_KEEPALIVE = (response, context) -> 0;

private static final long KEEP_ALIVE_SAFE_GAP = 500L;
private final ConnectionKeepAliveStrategy defaultConnectionKeepAliveStrategy;
private final long defaultKeepAliveTimeoutInMillis;

public CustomConnectionKeepAliveStrategy(long defaultKeepAliveTimeoutInMillis) {
this(defaultKeepAliveTimeoutInMillis, DefaultConnectionKeepAliveStrategy.INSTANCE);
}

public CustomConnectionKeepAliveStrategy(long defaultKeepAliveTimeoutInMillis,
ConnectionKeepAliveStrategy defaultConnectionKeepAliveStrategy) {
Args.notNegative(defaultKeepAliveTimeoutInMillis, "defaultKeepAliveTimeoutInMillis");
Args.notNull(defaultConnectionKeepAliveStrategy, "defaultConnectionKeepAliveStrategy");
this.defaultKeepAliveTimeoutInMillis = defaultKeepAliveTimeoutInMillis;
this.defaultConnectionKeepAliveStrategy = defaultConnectionKeepAliveStrategy;
}

public static long adjustKeepAliveTimeout(long keepAliveTimeoutInMillis) {
return Math.max(0L, (keepAliveTimeoutInMillis - KEEP_ALIVE_SAFE_GAP));
}

@Override
public long getKeepAliveDuration(final HttpResponse response, final HttpContext context) {
long keepAliveDuration = defaultConnectionKeepAliveStrategy.getKeepAliveDuration(response, context);
if (keepAliveDuration == -1L) {
return defaultKeepAliveTimeoutInMillis;
}
if (keepAliveDuration > 0L) {
return adjustKeepAliveTimeout(keepAliveDuration);
}
return keepAliveDuration;
}
```

贡献指南

打开贡献指南

调研方向

首先比较 firebase-admin 6.8.0 和 8.1.0 中 FirebaseMessagingClient.sendSingleRequest() 的行为,重点关注 ApiClientUtils.disconnectQuietly(response) 以及报告中描述的 Apache HttpClient ConnectionHolder 调用。使用提供的自定义 keep-alive 策略重现连接生命周期,并验证连接是否按照配置的持续时间被保留和回收。

由索引模型根据 Issue 内容生成。

评估

技术栈
java
领域
networking
Issue 类型
缺陷
难度
4/5
预计耗时
3-5 天
活跃度
停滞
描述清晰度
需要澄清
新手友好度
30/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。