Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Для служб, которые не привязаны к определенному протоколу связи или стеку, таким как веб-API, Windows Communication Foundation или другие, платформа Reliable Services предоставляет механизм удаленного взаимодействия для быстрой и простой настройки удаленных вызовов процедур для служб. В этой статье описывается настройка удаленных вызовов процедур для служб, написанных с помощью C#.
Настройка удаленного доступа для службы
Вы можете настроить удаленный доступ для службы двумя простыми шагами.
- Создайте интерфейс, чтобы ваша служба могла его реализовать. Этот интерфейс определяет методы, которые доступны для удаленного вызова процедуры в вашей службе. Методы должны быть асинхронными и возвращать задачи. Интерфейс должен реализовать
Microsoft.ServiceFabric.Services.Remoting.IService
, чтобы сигнализировать о том, что служба имеет интерфейс удаленного взаимодействия. - Используйте прослушиватель удаленного доступа в службе. Прослушиватель удаленного взаимодействия — это
ICommunicationListener
реализация, которая предоставляет возможности удаленного взаимодействия. ПространствоMicrosoft.ServiceFabric.Services.Remoting.Runtime
имен содержит методCreateServiceRemotingInstanceListeners
расширения для служб без отслеживания состояния и служб с отслеживанием состояния, которые можно использовать для создания прослушивателя удаленного взаимодействия с помощью протокола удаленного взаимодействия по умолчанию.
Примечание.
Пространство имен Remoting
доступно в виде отдельного пакета NuGet под названием Microsoft.ServiceFabric.Services.Remoting
.
Например, следующая служба без отслеживания состояния предоставляет один метод для получения "Hello World" через удаленный вызов процедуры.
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Remoting;
using Microsoft.ServiceFabric.Services.Remoting.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;
public interface IMyService : IService
{
Task<string> HelloWorldAsync();
}
class MyService : StatelessService, IMyService
{
public MyService(StatelessServiceContext context)
: base (context)
{
}
public Task<string> HelloWorldAsync()
{
return Task.FromResult("Hello!");
}
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return this.CreateServiceRemotingInstanceListeners();
}
}
Примечание.
Аргументы и возвращаемые типы в интерфейсе службы могут быть простыми, сложными или настраиваемыми типами, но они должны быть сериализованы с помощью .NET DataContractSerializer.
Вызов методов удаленного сервиса
Примечание.
Если вы используете более одного раздела, необходимо предоставить ServiceProxy.Create() соответствующий ключ ServicePartitionKey. Это не требуется для сценария с одним разделом.
Вызов методов службы с использованием стека удаленного взаимодействия осуществляется через локальный прокси-сервер к службе, используя класс Microsoft.ServiceFabric.Services.Remoting.Client.ServiceProxy
. Метод ServiceProxy
создает локальный прокси-сервер с помощью того же интерфейса, что и служба. С помощью этого прокси-сервера можно удаленно вызывать методы в интерфейсе.
IMyService helloWorldClient = ServiceProxy.Create<IMyService>(new Uri("fabric:/MyApplication/MyHelloWorldService"));
string message = await helloWorldClient.HelloWorldAsync();
Фреймворк удаленного взаимодействия передает исключения, вызываемые службой, клиенту. В результате при ServiceProxy
использовании клиент отвечает за обработку исключений, создаваемых службой.
Время существования прокси для службы
Создание прокси-сервера службы — это упрощенная операция, поэтому можно создать столько, сколько вам нужно. Экземпляры прокси-сервера службы можно повторно использовать до тех пор, пока они необходимы. Если удаленный вызов процедуры вызывает исключение, вы по-прежнему можете повторно использовать тот же экземпляр прокси-сервера. Каждый прокси-сервер службы содержит клиент связи, используемый для отправки сообщений через провод. При вызове удаленных процедур выполняются внутренние проверки, чтобы определить, является ли клиент связи валидным. На основе результатов этих проверок клиент связи повторно создается при необходимости. Таким образом, если возникает исключение, вам не нужно повторно создать ServiceProxy
.
Время существования фабрики сервисных прокси
ServiceProxyFactory — это фабрика, которая создает экземпляры прокси-сервера для различных интерфейсов удаленного взаимодействия. Если вы используете API ServiceProxyFactory.CreateServiceProxy
для создания прокси-сервера, платформа создает одноэлементный прокси-сервер службы.
Полезно создать вручную, если необходимо переопределить свойства IServiceRemotingClientFactory.
Создание фабрики является дорогой операцией. Фабрика прокси-сервера службы поддерживает внутренний кэш коммуникационного клиента.
Рекомендуется кэшировать фабрику прокси-сервера службы до тех пор, пока это возможно.
Обработка исключений в удаленном доступе
Все удаленные исключения, создаваемые API-интерфейсом службы, отправляются клиенту как AggregateException. Удаленные исключения должны иметь возможность быть сериализованными с использованием DataContract. Если это не так, API прокси-сервера выдает ServiceException с ошибкой сериализации в ней.
Прокси-сервер службы обрабатывает все исключения резервирования для раздела службы, для которой он создан. Он снова проверяет конечные точки, если возникают исключения аварийного переключения (не временные исключения) и повторяет вызов с правильной конечной точкой. Число повторных попыток при сбоях в системе не ограничено. При возникновении временных исключений прокси-сервер повторяет вызов.
Параметры повторных попыток по умолчанию предоставляются OperationRetrySettings.
Пользователь может настроить эти значения, передав объект OperationRetrySettings конструктору ServiceProxyFactory.
Используйте стек удаленного взаимодействия версии 2
Начиная с пакета NuGet remoting версии 2.8, вы можете использовать стек remoting V2. Стек удаленного взаимодействия версии 2 работает лучше. Он также предоставляет такие функции, как настраиваемая сериализация и более подключаемые API. Код шаблона продолжает использовать стек удаленного доступа версии 1. Удаленное управление версии 2 несовместимо с версией 1 (предыдущий стек удаленного управления). Следуйте инструкциям из статьи Об обновлении версии 1 до версии 2 , чтобы избежать влияния на доступность службы.
Для включения стека версии 2 доступны следующие подходы.
Использование атрибута сборки для использования стека версии 2
Эти действия изменяют код шаблона для использования стека версии 2 с помощью атрибута сборки.
Измените ресурс конечной точки с
"ServiceEndpoint"
на"ServiceEndpointV2"
в манифесте службы.<Resources> <Endpoints> <Endpoint Name="ServiceEndpointV2" /> </Endpoints> </Resources>
Используйте метод расширения
Microsoft.ServiceFabric.Services.Remoting.Runtime.CreateServiceRemotingInstanceListeners
для создания прослушивателей удаленного взаимодействия, одинаковых для версии 1 и версии 2.protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners() { return this.CreateServiceRemotingInstanceListeners(); }
Отметьте сборку, содержащую интерфейсы для удаленного взаимодействия, с атрибутом
FabricTransportServiceRemotingProvider
.[assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2, RemotingClientVersion = RemotingClientVersion.V2)]
В клиентском проекте изменения кода не требуются. Создайте клиентную сборку вместе со сборкой интерфейса, чтобы убедиться, что атрибут сборки, показанный ранее, используется.
Использование явных классов версии 2 для использования стека версии 2
В качестве альтернативы использованию атрибута сборки стек версии 2 также можно включить с помощью явных классов версии 2.
Эти шаги изменяют код шаблона для использования стека версии 2 с помощью явных классов версии 2.
Измените ресурс конечной точки с
"ServiceEndpoint"
на"ServiceEndpointV2"
в манифесте службы.<Resources> <Endpoints> <Endpoint Name="ServiceEndpointV2" /> </Endpoints> </Resources>
Используйте FabricTransportServiceRemotingListener из
Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Runtime
пространства имен.protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners() { return new[] { new ServiceInstanceListener((c) => { return new FabricTransportServiceRemotingListener(c, this); }) }; }
Используйте FabricTransportServiceRemotingClientFactory из
Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client
пространства имен для создания клиентов.var proxyFactory = new ServiceProxyFactory((c) => { return new FabricTransportServiceRemotingClientFactory(); });
Переход с удаленного доступа версии 1 на удаленный доступ версии 2
Для обновления с версии 1 до версии 2 требуются двухэтапные обновления. Выполните действия, описанные в этой последовательности.
Обновите службу версии 1 до службы версии 2 с помощью этого атрибута. Это изменение гарантирует, что служба использует слушатели версии 1 и версии 2.
a. Добавьте ресурс конечной точки с именем ServiceEndpointV2 в манифест службы.
<Resources> <Endpoints> <Endpoint Name="ServiceEndpointV2" /> </Endpoints> </Resources>
б. Используйте следующий метод расширения для создания удалённого прослушивателя.
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners() { return this.CreateServiceRemotingInstanceListeners(); }
с. Добавьте атрибут сборки для интерфейсов удаленного взаимодействия для использования прослушивателя версии 1 и версии 2 и клиента версии 2.
[assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2|RemotingListenerVersion.V1, RemotingClientVersion = RemotingClientVersion.V2)]
Обновите клиент версии 1 до клиента версии 2 с помощью атрибута клиента версии 2. Этот шаг гарантирует, что клиент использует стек версии 2. Никаких изменений в клиентском проекте или службе не требуется. Создание клиентских проектов с обновленной сборкой интерфейса является достаточным.
Этот шаг является необязательным. Используйте атрибут V2-приемника, а затем обновите V2-сервис. Этот шаг гарантирует, что служба прослушивает только прослушиватель версии 2.
[assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2, RemotingClientVersion = RemotingClientVersion.V2)]
Используйте стек версии 2 для удаленного взаимодействия (совместимый с интерфейсом)
Стек удаленного взаимодействия версии 2 (совместимый с интерфейсом) называется V2_1 и является самой up-to-date версии. У него есть все функции стека удалённого доступа второй версии. Его стек интерфейса совместим со стеком удаленного взаимодействия версии 1, но он несовместим с версиями V2 и V1. Чтобы обновить версию 1 до V2_1, не влияя на доступность службы, выполните действия, описанные в статье Об обновлении версии 1 до версии 2 (совместимая с интерфейсом).
Используйте атрибут сборки для применения стека удаленного взаимодействия версии 2 (совместимого с интерфейсом)
Выполните следующие действия, чтобы поменять на стек V2_1.
Добавьте ресурс конечной точки с именем "ServiceEndpointV2_1" в манифесте службы.
<Resources> <Endpoints> <Endpoint Name="ServiceEndpointV2_1" /> </Endpoints> </Resources>
Используйте метод расширения удаленного взаимодействия для создания прослушивателя удаленного взаимодействия.
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners() { return this.CreateServiceRemotingInstanceListeners(); }
Добавьте атрибут сборки для интерфейсов удаленного взаимодействия.
[assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion= RemotingListenerVersion.V2_1, RemotingClientVersion= RemotingClientVersion.V2_1)]
В клиентском проекте изменения не требуются. Создайте клиентную сборку с помощью сборки интерфейса, чтобы убедиться, что используется предыдущий атрибут сборки.
Используйте явные классы удаленного доступа для создания прослушивателя или клиентской фабрики для версии 2 (совместимой с интерфейсом)
Выполните следующие действия.
Добавьте ресурс конечной точки с именем "ServiceEndpointV2_1" в манифесте службы.
<Resources> <Endpoints> <Endpoint Name="ServiceEndpointV2_1" /> </Endpoints> </Resources>
Используйте прослушиватель удаленного взаимодействия версии 2. Имя ресурса конечной точки службы по умолчанию — "ServiceEndpointV2_1". Он должен быть определен в манифесте службы.
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners() { return new[] { new ServiceInstanceListener((c) => { var settings = new FabricTransportRemotingListenerSettings(); settings.UseWrappedMessage = true; return new FabricTransportServiceRemotingListener(c, this,settings); }) }; }
Используйте фабрику клиентов V2.
var proxyFactory = new ServiceProxyFactory((c) => { var settings = new FabricTransportRemotingSettings(); settings.UseWrappedMessage = true; return new FabricTransportServiceRemotingClientFactory(settings); });
Обновление с удаленного взаимодействия версии 1 до удаленного взаимодействия версии 2 (совместимое с интерфейсом)
Для обновления с версии 1 до версии 2 (совместимой с интерфейсом, известной как V2_1), требуются двухэтапные обновления. Выполните действия, описанные в этой последовательности.
Примечание.
При обновлении с версии 1 до версии 2 убедитесь, что Remoting
пространство имен обновляется для использования версии 2. Пример: Microsoft.ServiceFabric.Services.Remoting.V2.FabricTransport.Client
Обновите службу V1 до службы V2_1, используя следующий атрибут. Это изменение гарантирует, что служба слушает на прослушивателе V1 и V2_1.
a. Добавьте ресурс конечной точки с именем "ServiceEndpointV2_1" в манифесте службы.
<Resources> <Endpoints> <Endpoint Name="ServiceEndpointV2_1" /> </Endpoints> </Resources>
б. Используйте следующий метод расширения для создания прослушивателя для удаленного взаимодействия.
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners() { return this.CreateServiceRemotingInstanceListeners(); }
с. Добавьте атрибут сборки для интерфейсов удаленного взаимодействия для использования прослушивателя версии 1, V2_1 и клиента V2_1.
[assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2_1 | RemotingListenerVersion.V1, RemotingClientVersion = RemotingClientVersion.V2_1)]
Обновите клиента версии 1 до клиента V2_1 с помощью атрибута клиента V2_1. Этот шаг гарантирует, что клиент использует стек V2_1. Никаких изменений в клиентском проекте или службе не требуется. Создание клиентских проектов с обновленной сборкой интерфейса достаточно.
Этот шаг является необязательным. Удалите версию V1 слушателя из атрибута, а затем обновите службу до версии V2. Этот шаг гарантирует, что служба прослушивает только прослушиватель версии 2.
[assembly: FabricTransportServiceRemotingProvider(RemotingListenerVersion = RemotingListenerVersion.V2_1, RemotingClientVersion = RemotingClientVersion.V2_1)]
Использование кастомной сериализации с обернутым сообщением для удаленного взаимодействия
Для оболочечного сообщения для удаленного взаимодействия мы создадим единственный завернутый объект со всеми параметрами как поля в объекте. Выполните следующие действия.
Реализуйте интерфейс
IServiceRemotingMessageSerializationProvider
, чтобы обеспечить реализацию для пользовательской сериализации. В этом фрагменте кода показано, как выглядит реализация.public class ServiceRemotingJsonSerializationProvider : IServiceRemotingMessageSerializationProvider { public IServiceRemotingMessageBodyFactory CreateMessageBodyFactory() { return new JsonMessageFactory(); } public IServiceRemotingRequestMessageBodySerializer CreateRequestMessageSerializer(Type serviceInterfaceType, IEnumerable<Type> requestWrappedType, IEnumerable<Type> requestBodyTypes = null) { return new ServiceRemotingRequestJsonMessageBodySerializer(); } public IServiceRemotingResponseMessageBodySerializer CreateResponseMessageSerializer(Type serviceInterfaceType, IEnumerable<Type> responseWrappedType, IEnumerable<Type> responseBodyTypes = null) { return new ServiceRemotingResponseJsonMessageBodySerializer(); } }
class JsonMessageFactory : IServiceRemotingMessageBodyFactory { public IServiceRemotingRequestMessageBody CreateRequest(string interfaceName, string methodName, int numberOfParameters, object wrappedRequestObject) { return new JsonBody(wrappedRequestObject); } public IServiceRemotingResponseMessageBody CreateResponse(string interfaceName, string methodName, object wrappedRequestObject) { return new JsonBody(wrappedRequestObject); } }
class ServiceRemotingRequestJsonMessageBodySerializer : IServiceRemotingRequestMessageBodySerializer { private JsonSerializer serializer; public ServiceRemotingRequestJsonMessageBodySerializer() { serializer = JsonSerializer.Create(new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }); } public IOutgoingMessageBody Serialize(IServiceRemotingRequestMessageBody serviceRemotingRequestMessageBody) { if (serviceRemotingRequestMessageBody == null) { return null; } using (var writeStream = new MemoryStream()) { using (var jsonWriter = new JsonTextWriter(new StreamWriter(writeStream))) { serializer.Serialize(jsonWriter, serviceRemotingRequestMessageBody); jsonWriter.Flush(); var bytes = writeStream.ToArray(); var segment = new ArraySegment<byte>(bytes); var segments = new List<ArraySegment<byte>> { segment }; return new OutgoingMessageBody(segments); } } } public IServiceRemotingRequestMessageBody Deserialize(IIncomingMessageBody messageBody) { using (var sr = new StreamReader(messageBody.GetReceivedBuffer())) { using (JsonReader reader = new JsonTextReader(sr)) { var ob = serializer.Deserialize<JsonBody>(reader); return ob; } } } }
class ServiceRemotingResponseJsonMessageBodySerializer : IServiceRemotingResponseMessageBodySerializer { private JsonSerializer serializer; public ServiceRemotingResponseJsonMessageBodySerializer() { serializer = JsonSerializer.Create(new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }); } public IOutgoingMessageBody Serialize(IServiceRemotingResponseMessageBody responseMessageBody) { if (responseMessageBody == null) { return null; } using (var writeStream = new MemoryStream()) { using (var jsonWriter = new JsonTextWriter(new StreamWriter(writeStream))) { serializer.Serialize(jsonWriter, responseMessageBody); jsonWriter.Flush(); var bytes = writeStream.ToArray(); var segment = new ArraySegment<byte>(bytes); var segments = new List<ArraySegment<byte>> { segment }; return new OutgoingMessageBody(segments); } } } public IServiceRemotingResponseMessageBody Deserialize(IIncomingMessageBody messageBody) { using (var sr = new StreamReader(messageBody.GetReceivedBuffer())) { using (var reader = new JsonTextReader(sr)) { var obj = serializer.Deserialize<JsonBody>(reader); return obj; } } } }
class JsonBody : WrappedMessage, IServiceRemotingRequestMessageBody, IServiceRemotingResponseMessageBody { public JsonBody(object wrapped) { this.Value = wrapped; } public void SetParameter(int position, string parameName, object parameter) { //Not Needed if you are using WrappedMessage throw new NotImplementedException(); } public object GetParameter(int position, string parameName, Type paramType) { //Not Needed if you are using WrappedMessage throw new NotImplementedException(); } public void Set(object response) { //Not Needed if you are using WrappedMessage throw new NotImplementedException(); } public object Get(Type paramType) { //Not Needed if you are using WrappedMessage throw new NotImplementedException(); } }
Переопределите поставщика сериализации по умолчанию на
JsonSerializationProvider
для прослушивателя удаленного доступа.protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners() { return new[] { new ServiceInstanceListener((c) => { return new FabricTransportServiceRemotingListener(context, _calculatorFactory.GetCalculator(Context), serializationProvider: new ServiceRemotingJsonSerializationProvider()); }) }; }
Переопределите поставщика сериализации по умолчанию для
JsonSerializationProvider
фабрики клиентов удаленного доступа.var proxyFactory = new ServiceProxyFactory((c) => { return new FabricTransportServiceRemotingClientFactory( serializationProvider: new ServiceRemotingJsonSerializationProvider()); });