是否可以指示 ServicePartitionClient 与服务结构中的特定节点对话?
Is it possible to instruct ServicePartitionClient to talk to a specific node in service fabric?
我有
public class HttpCommunicationClient : HttpClient, ICommunicationClient
{
public HttpCommunicationClient()
: base(new HttpClientHandler() { AllowAutoRedirect = false, UseCookies = false })
{
}
public HttpCommunicationClient(HttpMessageHandler handler)
: base(handler)
{
}
public HttpCommunicationClient(HttpMessageHandler handler, bool disposeHandler)
: base(handler, disposeHandler)
{
}
#region ICommunicationClient
string ICommunicationClient.ListenerName { get; set; }
ResolvedServiceEndpoint ICommunicationClient.Endpoint { get; set; }
ResolvedServicePartition ICommunicationClient.ResolvedServicePartition { get; set; }
#endregion ICommunicationClient
}
和
public class HttpCommunicationClientFactory : CommunicationClientFactoryBase<HttpCommunicationClient>
{
private readonly Func<HttpCommunicationClient> _innerDispatcherProvider;
public HttpCommunicationClientFactory(IServicePartitionResolver servicePartitionResolver = null, IEnumerable<IExceptionHandler> exceptionHandlers = null, string traceId = null)
: this(() => new HttpCommunicationClient(), servicePartitionResolver, exceptionHandlers, traceId)
{
}
public HttpCommunicationClientFactory(Func<HttpCommunicationClient> innerDispatcherProvider, IServicePartitionResolver servicePartitionResolver = null, IEnumerable<IExceptionHandler> exceptionHandlers = null, string traceId = null)
: base(servicePartitionResolver, exceptionHandlers, traceId)
{
if (innerDispatcherProvider == null)
{
throw new ArgumentNullException(nameof(innerDispatcherProvider));
}
_innerDispatcherProvider = innerDispatcherProvider;
}
protected override void AbortClient(HttpCommunicationClient dispatcher)
{
if (dispatcher != null)
{
dispatcher.Dispose();
}
}
protected override Task<HttpCommunicationClient> CreateClientAsync(string endpoint, CancellationToken cancellationToken)
{
var dispatcher = _innerDispatcherProvider.Invoke();
dispatcher.BaseAddress = new Uri(endpoint, UriKind.Absolute);
return Task.FromResult(dispatcher);
}
protected override bool ValidateClient(HttpCommunicationClient dispatcher)
{
return dispatcher != null && dispatcher.BaseAddress != null;
}
protected override bool ValidateClient(string endpoint, HttpCommunicationClient dispatcher)
{
return dispatcher != null && dispatcher.BaseAddress == new Uri(endpoint, UriKind.Absolute);
}
}
并像下面这样使用它
var servicePartitionClient = new ServicePartitionClient<HttpCommunicationClient>(_httpClientFactory,
_options.ServiceUri,
_options.GetServicePartitionKey?.Invoke(context),
_options.TargetReplicaSelector,
_options.ListenerName,
_options.OperationRetrySettings);
using (var responseMessage = await servicePartitionClient.InvokeWithRetryAsync(httpClient => ExecuteServiceCallAsync(httpClient, context)))
{
await responseMessage.CopyToCurrentContext(context);
}
现在的问题是,如果我在使用 ServicePartitionClient 时知道我希望它连接到特定节点,有什么办法可以做到吗?
情况是它是一个将请求转发到其他服务的网关应用程序,我希望它的行为类似于粘性会话。
我找到了一个解决方案,我在下面的 ExecuteServiceCallAsync
调用中从请求中读取一个 cookie,其中包含关于它连接到哪个节点的信息,如果它是一个粘性会话,如果不存在 cookie,我设置一个来自请求的信息。如果该节点不再存在,则将 cookie 更新为新节点。
using (var responseMessage = await servicePartitionClient.InvokeWithRetryAsync(httpClient => ExecuteServiceCallAsync(httpClient, context)))
{
await responseMessage.CopyToCurrentContext(context);
}
从服务的角度思考比节点更有意义。因此,您不是连接到特定节点,而是实际连接到服务的特定实例。
当您连接到一个服务时,如果它是无状态的,那么根据它是无状态的定义,您连接到哪个实例并不重要。如果您发现用户绑定到服务的特定实例,则该服务是有状态的(它会跟踪某些用户状态),而这正是 有状态服务 的场景类型是为了处理。
我有
public class HttpCommunicationClient : HttpClient, ICommunicationClient
{
public HttpCommunicationClient()
: base(new HttpClientHandler() { AllowAutoRedirect = false, UseCookies = false })
{
}
public HttpCommunicationClient(HttpMessageHandler handler)
: base(handler)
{
}
public HttpCommunicationClient(HttpMessageHandler handler, bool disposeHandler)
: base(handler, disposeHandler)
{
}
#region ICommunicationClient
string ICommunicationClient.ListenerName { get; set; }
ResolvedServiceEndpoint ICommunicationClient.Endpoint { get; set; }
ResolvedServicePartition ICommunicationClient.ResolvedServicePartition { get; set; }
#endregion ICommunicationClient
}
和
public class HttpCommunicationClientFactory : CommunicationClientFactoryBase<HttpCommunicationClient>
{
private readonly Func<HttpCommunicationClient> _innerDispatcherProvider;
public HttpCommunicationClientFactory(IServicePartitionResolver servicePartitionResolver = null, IEnumerable<IExceptionHandler> exceptionHandlers = null, string traceId = null)
: this(() => new HttpCommunicationClient(), servicePartitionResolver, exceptionHandlers, traceId)
{
}
public HttpCommunicationClientFactory(Func<HttpCommunicationClient> innerDispatcherProvider, IServicePartitionResolver servicePartitionResolver = null, IEnumerable<IExceptionHandler> exceptionHandlers = null, string traceId = null)
: base(servicePartitionResolver, exceptionHandlers, traceId)
{
if (innerDispatcherProvider == null)
{
throw new ArgumentNullException(nameof(innerDispatcherProvider));
}
_innerDispatcherProvider = innerDispatcherProvider;
}
protected override void AbortClient(HttpCommunicationClient dispatcher)
{
if (dispatcher != null)
{
dispatcher.Dispose();
}
}
protected override Task<HttpCommunicationClient> CreateClientAsync(string endpoint, CancellationToken cancellationToken)
{
var dispatcher = _innerDispatcherProvider.Invoke();
dispatcher.BaseAddress = new Uri(endpoint, UriKind.Absolute);
return Task.FromResult(dispatcher);
}
protected override bool ValidateClient(HttpCommunicationClient dispatcher)
{
return dispatcher != null && dispatcher.BaseAddress != null;
}
protected override bool ValidateClient(string endpoint, HttpCommunicationClient dispatcher)
{
return dispatcher != null && dispatcher.BaseAddress == new Uri(endpoint, UriKind.Absolute);
}
}
并像下面这样使用它
var servicePartitionClient = new ServicePartitionClient<HttpCommunicationClient>(_httpClientFactory,
_options.ServiceUri,
_options.GetServicePartitionKey?.Invoke(context),
_options.TargetReplicaSelector,
_options.ListenerName,
_options.OperationRetrySettings);
using (var responseMessage = await servicePartitionClient.InvokeWithRetryAsync(httpClient => ExecuteServiceCallAsync(httpClient, context)))
{
await responseMessage.CopyToCurrentContext(context);
}
现在的问题是,如果我在使用 ServicePartitionClient 时知道我希望它连接到特定节点,有什么办法可以做到吗?
情况是它是一个将请求转发到其他服务的网关应用程序,我希望它的行为类似于粘性会话。
我找到了一个解决方案,我在下面的 ExecuteServiceCallAsync
调用中从请求中读取一个 cookie,其中包含关于它连接到哪个节点的信息,如果它是一个粘性会话,如果不存在 cookie,我设置一个来自请求的信息。如果该节点不再存在,则将 cookie 更新为新节点。
using (var responseMessage = await servicePartitionClient.InvokeWithRetryAsync(httpClient => ExecuteServiceCallAsync(httpClient, context)))
{
await responseMessage.CopyToCurrentContext(context);
}
从服务的角度思考比节点更有意义。因此,您不是连接到特定节点,而是实际连接到服务的特定实例。
当您连接到一个服务时,如果它是无状态的,那么根据它是无状态的定义,您连接到哪个实例并不重要。如果您发现用户绑定到服务的特定实例,则该服务是有状态的(它会跟踪某些用户状态),而这正是 有状态服务 的场景类型是为了处理。