我有一个使用C#SDK利用Google云端硬盘服务的应用程序。我正在使用服务帐户凭据(具有模拟功能)来指定我代表的用户。
我遇到的问题是,我需要为每个模拟用户创建一个新的DriveService,并且每个DriveService实例都会创建2个HttpConnection对象,当我Dispose()DriveService时,这些对象不会被清除。我已经在单元测试中对此进行了复制。我通过以下方法创建DriveService:
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
}
}
}
}
}
这导致内存中保存的HttpConnection对象数量不断增加,而这些对象无法通过垃圾回收清除。
答案 0 :(得分:0)
如果DriveService在后台使用HttpClient,则您可能会遇到无法在共享库上工作或在超时之前保持连接的情况。
但是HttpClient是不同的。尽管它实现了IDisposable接口,但实际上是一个共享对象。
请参见https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
答案 1 :(得分:0)
正如Mike M所言,HttpClient
的一个已知问题在于它的Dispose()
方法并未真正处理该实例。
不幸的是,由于Google.Apis.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;
}
}
创建该工厂的单个实例:
var factory = new SharedHttpClientFactory();
然后使用此工厂创建每个DriveService
:
var client = new StorageService(new Google.Apis.Services.BaseClientService.Initializer
{
HttpClientFactory = factory,
HttpClientInitializer = serviceAcctCreds
});
大量注意事项:
DriveService
,因为这将处置基础共享HttpClient
。DriveService
实例共享相同的凭据;每次实例化新的DriveService
时,这些凭据都会更改。这意味着所有以前的实例也也已更新其凭据。HttpClientInitializer
属性 的凭据必须是ServiceCredential
实例(与您的代码一样)。如果它是GoogleCredential
实例,则无法使用。