Also löste ich dieses Problem ein bisschen anders. Ich mochte wirklich nicht die Idee, diese private statische Methode zu enthüllen, um den Cache auszugeben, weil Sie nicht wirklich wissen, worauf Sie dabei hinauslaufen; Sie umgehen im Grunde die Verkapselung und das könnte unvorhergesehene Probleme verursachen. Aber wirklich, ich war besorgt über Race Conditions, wo ich den Cache lösche und bevor ich die Anfrage sende, kommt ein anderer Thread rein und baut eine neue Session auf, so dass mein erster Thread diese Session versehentlich kapert. Schlechte Neuigkeiten ... wie auch immer, hier ist was ich getan habe.
Ich stoppte, um darüber nachzudenken, ob es eine Art gab, den Prozess zu isolieren, und dann erinnerte ein Android-Mitarbeiter von mir die Verfügbarkeit von AppDomains. Wir stimmten beide darin überein, dass ein Tcp/Ssl-Aufruf isoliert von allem anderen laufen sollte. Dies würde ermöglichen, dass die Caching-Logik intakt bleibt, ohne Konflikte zwischen SSL-Sitzungen zu verursachen.
Im Grunde hatte ich ursprünglich meinen SSL-Client geschrieben, um in einer separaten Bibliothek intern zu sein.Dann hatte ich in dieser Bibliothek eine öffentliche Dienstleistung als Proxy/Mediator für diesen Kunden. In der Anwendungsschicht wollte ich die Möglichkeit haben, zwischen den Diensten (in meinem Fall HSM-Dienste) auf der Grundlage des Hardwaretyps zu wechseln, also wickelte ich das in einen Adapter ein und verband diesen mit einer Fabrik. Ok, wie ist das relevant? Nun, es hat es einfach einfacher gemacht, diese AppDomain-Sache sauber zu machen, ohne dieses Verhalten jedem anderen Verbraucher des öffentlichen Dienstes (dem Proxy/Vermittler, von dem ich sprach) aufzuzwingen. Sie müssen dieser Abstraktion nicht folgen, ich teile gerne gute Abstraktionsbeispiele, wenn ich sie finde :)
Nun, im Adapter, anstatt den Dienst direkt anzurufen, erstelle ich im Grunde die Domäne. Hier ist der Ctor:
public VCRklServiceAdapter(
string hostname,
int port,
IHsmLogger logger)
{
Ensure.IsNotNullOrEmpty(hostname, nameof(hostname));
Ensure.IsNotDefault(port, nameof(port), failureMessage: $"It does not appear that the port number was actually set (port: {port})");
Ensure.IsNotNull(logger, nameof(logger));
ClientId = Guid.NewGuid();
_logger = logger;
_hostname = hostname;
_port = port;
// configure the domain
_instanceDomain = AppDomain.CreateDomain(
$"vcrypt_rkl_instance_{ClientId}",
null,
AppDomain.CurrentDomain.SetupInformation);
// using the configured domain, grab a command instance from which we can
// marshall in some data
_rklServiceRuntime = (IRklServiceRuntime)_instanceDomain.CreateInstanceAndUnwrap(
typeof(VCServiceRuntime).Assembly.FullName,
typeof(VCServiceRuntime).FullName);
}
All dies tut, ist erstellt eine benannte Domäne, aus dem mein eigentlichen Dienst in Isolation ausgeführt wird. Nun, die meisten Artikel, die ich über die Art und Weise, wie Sie innerhalb der Domain tatsächlich ausführen, über-vereinfacht, wie es funktioniert. Die Beispiele beinhalten typischerweise das Aufrufen von myDomain.DoCallback(() => ...);
, was nicht falsch ist, aber der Versuch, Daten in diese Domäne hinein und aus ihr heraus zu bekommen, wird wahrscheinlich problematisch werden, da die Serialisierung Sie wahrscheinlich auf Ihren Spuren stört. Einfach ausgedrückt: Objekte, die außerhalb von DoCallback()
instanziiert werden, sind nicht die gleichen Objekte, wenn sie innerhalb von DoCallback
aufgerufen werden, da sie außerhalb dieser Domäne erstellt wurden (siehe Objekt-Marshalling). Sie werden wahrscheinlich alle Arten von Serialisierungsfehlern erhalten. Dies ist kein Problem, wenn die gesamte Operation ausgeführt wird, Eingabe und Ausgabe und alles kann von innen myDomain.DoCallback()
auftreten, aber das ist problematisch, wenn Sie externe Parameter verwenden und etwas über diese AppDomain zurück in die Ursprungsdomäne zurückgeben müssen.
Ich bin hier auf SO auf ein anderes Muster gestoßen, das für mich geklappt hat und dieses Problem gelöst hat. Schauen Sie sich _rklServiceRuntime =
in meinem Beispiel ctor. In diesem Fall wird die Domäne tatsächlich aufgefordert, ein Objekt zu instanziieren, damit Sie als Proxy von dieser Domäne aus agieren können. Dadurch können Sie einige Objekte in und aus dem Mars marshallen. Hier ist meine Implemenation von IRklServiceRuntime
:
public interface IRklServiceRuntime
{
RklResponse Run(RklRequest request, string hostname, int port, Guid clientId, IHsmLogger logger);
}
public class VCServiceRuntime : MarshalByRefObject, IRklServiceRuntime
{
public RklResponse Run(
RklRequest request,
string hostname,
int port,
Guid clientId,
IHsmLogger logger)
{
Ensure.IsNotNull(request, nameof(request));
Ensure.IsNotNullOrEmpty(hostname, nameof(hostname));
Ensure.IsNotDefault(port, nameof(port), failureMessage: $"It does not appear that the port number was actually set (port: {port})");
Ensure.IsNotNull(logger, nameof(logger));
// these are set here instead of passed in because they are not
// serializable
var clientCert = ApplicationValues.VCClientCertificate;
var clientCerts = new X509Certificate2Collection(clientCert);
using (var client = new VCServiceClient(hostname, port, clientCerts, clientId, logger))
{
var response = client.RetrieveDeviceKeys(request);
return response;
}
}
}
Dieses erbt von MarshallByRefObject die es AppDomain Grenzen überschreiten können, und hat eine einzige Methode, die externen Parameter übernimmt und führt die Logik innerhalb der Domäne, die es instanziiert.
Also jetzt zurück zum Serviceadapter: Alle Serviceadapter müssen jetzt _rklServiceRuntime.Run(...)
aufrufen und die notwendigen, serialisierbaren Parameter einspeisen. Jetzt erstelle ich so viele Instanzen des Dienstadapters, wie ich brauche, und sie laufen alle in ihrer eigenen Domäne. Dies funktioniert für mich, weil meine SSL-Aufrufe klein und kurz sind und diese Anfragen innerhalb eines internen Webdienstes gestellt werden, bei dem das Instanziieren von Anfragen wie diesem sehr wichtig ist. Hier ist der vollständige Adapter:
public class VCRklServiceAdapter : IRklService
{
private readonly string _hostname;
private readonly int _port;
private readonly IHsmLogger _logger;
private readonly AppDomain _instanceDomain;
private readonly IRklServiceRuntime _rklServiceRuntime;
public Guid ClientId { get; }
public VCRklServiceAdapter(
string hostname,
int port,
IHsmLogger logger)
{
Ensure.IsNotNullOrEmpty(hostname, nameof(hostname));
Ensure.IsNotDefault(port, nameof(port), failureMessage: $"It does not appear that the port number was actually set (port: {port})");
Ensure.IsNotNull(logger, nameof(logger));
ClientId = Guid.NewGuid();
_logger = logger;
_hostname = hostname;
_port = port;
// configure the domain
_instanceDomain = AppDomain.CreateDomain(
$"vc_rkl_instance_{ClientId}",
null,
AppDomain.CurrentDomain.SetupInformation);
// using the configured domain, grab a command instance from which we can
// marshall in some data
_rklServiceRuntime = (IRklServiceRuntime)_instanceDomain.CreateInstanceAndUnwrap(
typeof(VCServiceRuntime).Assembly.FullName,
typeof(VCServiceRuntime).FullName);
}
public RklResponse GetKeys(RklRequest rklRequest)
{
Ensure.IsNotNull(rklRequest, nameof(rklRequest));
var response = _rklServiceRuntime.Run(
rklRequest,
_hostname,
_port,
ClientId,
_logger);
return response;
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
public void Dispose()
{
AppDomain.Unload(_instanceDomain);
}
}
Beachten Sie die Entsorgungsmethode. Vergessen Sie nicht, die Domain zu entfernen. Dieser Dienst implementiert IRklService, der IDisposable implementiert. Wenn ich ihn verwende, wird er mit einer using
-Anweisung verwendet.
Dies scheint ein bisschen künstlich, aber es ist wirklich nicht und jetzt wird die Logik auf seiner eigenen Domäne isoliert ausgeführt werden, und damit die Cache-Logik bleibt intakt, aber nicht problematisch. Viel besser als sich mit dem SSLSessionCache einzumischen!
Bitte vergeben Sie alle Namensinkonsistenzen, da ich die tatsächlichen Namen schnell nach dem Schreiben des Beitrags bereinigt habe. Ich hoffe, das hilft jemandem!
Oder Fix SslClient Microsoft. – Alexis
@Andrey: Wie ich die ursprüngliche Frage verstehe, ist die Reparatur des Servers keine Option: Es ist nicht unter unserer Kontrolle. – Vlad
@rufanov: Nun, du greifst nach einer großen Waffe.Die Reflexion muss natürlich funktionieren. – Vlad