Google api Google DriveService C#泄漏HttpConnection对象
我有一个使用C#SDK的应用程序,它利用谷歌的硬盘服务。我正在使用服务帐户凭据,并通过模拟指定我代表的用户 我遇到的问题是,我需要为每个模拟用户创建一个新的DriveService,这些DriveService isntances中的每一个都会创建两个HttpConnection对象,在我处置DriveService()时,这些对象不会被清理。我在单元测试中重新编写了这个。我使用以下方法创建DriveService:Google api Google DriveService C#泄漏HttpConnection对象,google-api,google-drive-api,google-api-dotnet-client,Google Api,Google Drive Api,Google Api Dotnet Client,我有一个使用C#SDK的应用程序,它利用谷歌的硬盘服务。我正在使用服务帐户凭据,并通过模拟指定我代表的用户 我遇到的问题是,我需要为每个模拟用户创建一个新的DriveService,这些DriveService isntances中的每一个都会创建两个HttpConnection对象,在我处置DriveService()时,这些对象不会被清理。我在单元测试中重新编写了这个。我使用以下方法创建DriveService: public DriveService GetDriveService(str
public DriveService GetDriveService(string email)
{
var serviceAccountEmail = "service account email";
var privateKey = "private key";
var serviceAcctCreds = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
User = email,
Scopes = "scopes" }),
}.FromPrivateKey(privateKey));
var driveService = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = serviceAcctCreds,
ApplicationName = "app name",
});
return driveService;
}
然后按如下方式使用:
public async Task DoTheGDriveMemoryTest()
{
var userEmails = new List<string>
{
"email1@example.com", "email2@example.com"
};
var i = 1;
while (true)
{
foreach (var email in userEmails)
{
//recommend using Fiddler Autoresponders here
using (var service = GetDriveService(email))
{
var request = service.Changes.List("delta id");
try
{
var result = await request.ExecuteAsync();
}
catch (Exception e)
{
//do nothing
}
}
}
}
}
public异步任务DoTheGDriveMemoryTest()
{
var userEmails=新列表
{
"email1@example.com", "email2@example.com"
};
var i=1;
while(true)
{
foreach(用户电子邮件中的var电子邮件)
{
//建议在这里使用Fiddler自动应答器
使用(var服务=GetDriveService(电子邮件))
{
var请求=service.Changes.List(“增量id”);
尝试
{
var result=wait request.ExecuteAsync();
}
捕获(例外e)
{
//无所事事
}
}
}
}
}
这导致存储在内存中的HttpConnection对象数量不断增加,而这些对象不会通过垃圾收集进行清理。如果DriveService在幕后使用HttpClient,则可能会遇到dispose无法处理共享对象,或者连接会一直保持到超时 但是HttpClient是不同的。虽然它实现了IDisposable接口,但实际上它是一个共享对象
参见正如Mike M所述,这是HttpClient的一个已知问题,因为它的
Dispose()
方法实际上没有处理实例
不幸的是,由于Google.api.Auth
库将HttpClient
和凭据联系在一起的方式,我看不到一个很好的解决方法
这里有一个非常黑客的解决方法,允许您在所有RPC之间共享一个HttpClient。
定义自定义IHttpClientFactory
:
class SharedHttpClientFactory : IHttpClientFactory
{
public SharedHttpClientFactory()
{
_handler = new ConfigurableMessageHandler(new HttpClientHandler());
_client = new ConfigurableHttpClient(_handler);
}
private readonly ConfigurableMessageHandler _handler;
private readonly ConfigurableHttpClient _client;
private Action _clearHandlers = null;
public ConfigurableHttpClient CreateHttpClient(CreateHttpClientArgs args)
{
_clearHandlers?.Invoke();
var executeInterceptors = args.Initializers.OfType<IHttpExecuteInterceptor>().ToList();
var unsuccessfulResponseHandlers = args.Initializers.OfType<IHttpUnsuccessfulResponseHandler>().ToList();
_clearHandlers = () =>
{
foreach (var executeInterceptor in executeInterceptors)
{
_handler.RemoveExecuteInterceptor(executeInterceptor);
}
foreach (var unsuccssfulResponseHandler in unsuccessfulResponseHandlers)
{
_handler.RemoveUnsuccessfulResponseHandler(unsuccssfulResponseHandler);
}
};
foreach (var executeInterceptor in executeInterceptors)
{
_handler.AddExecuteInterceptor(executeInterceptor);
}
foreach (var unsuccssfulResponseHandler in unsuccessfulResponseHandlers)
{
_handler.AddUnsuccessfulResponseHandler(unsuccssfulResponseHandler);
}
_handler.ApplicationName = args.ApplicationName;
return _client;
}
}
然后使用此工厂创建每个DriveService
:
var factory = new SharedHttpClientFactory();
var client = new StorageService(new Google.Apis.Services.BaseClientService.Initializer
{
HttpClientFactory = factory,
HttpClientInitializer = serviceAcctCreds
});
警告比比皆是:
- 不要处置
,因为这将处置底层共享DriveService
HttpClient
- 所有
实例共享相同的凭据;每次实例化新的DriveService
时,都会更改这些凭据。这意味着所有以前的实例的凭据都已更新DriveService
- 传递到初始值设定项
属性的凭据必须是HttpClientInitializer
实例(与您的代码相同)。如果它是一个ServiceCredential
实例,这将不起作用GoogleCredential
- 这整件事并不是真的建议,绝对是一个令人讨厌的黑客行为,但我看不到更好/更简单的解决方法