Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/expecho/servicefabric-remoting-customheaders
This package allows injecting custom message headers into remoting messages (Actors and Reliable Services, V2 remoting only) at runtime. The headers are available client side to read. It also provides message interception using BeforeHandleRequestResponseAsync and AfterHandleRequestResponseAsync to act on remoting events.
https://github.com/expecho/servicefabric-remoting-customheaders
azure azure-service-fabric reliable-actors reliable-stateful-service remoting service-fabric
Last synced: about 24 hours ago
JSON representation
This package allows injecting custom message headers into remoting messages (Actors and Reliable Services, V2 remoting only) at runtime. The headers are available client side to read. It also provides message interception using BeforeHandleRequestResponseAsync and AfterHandleRequestResponseAsync to act on remoting events.
- Host: GitHub
- URL: https://github.com/expecho/servicefabric-remoting-customheaders
- Owner: Expecho
- License: mit
- Created: 2018-06-21T08:24:06.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2021-12-06T09:47:26.000Z (about 3 years ago)
- Last Synced: 2024-12-17T23:46:40.775Z (about 1 month ago)
- Topics: azure, azure-service-fabric, reliable-actors, reliable-stateful-service, remoting, service-fabric
- Language: C#
- Homepage:
- Size: 92.8 KB
- Stars: 12
- Watchers: 5
- Forks: 9
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ServiceFabric.Remoting.CustomHeaders
This package allows injecting custom headers into remoting messages (Actors and Reliable Services, V2 remoting only) at runtime. The headers are available client side to read.
It also provides message interception using BeforeHandleRequestResponseAsync and AfterHandleRequestResponseAsync to act on remoting events.Common used classes:
- [CustomHeaders](https://github.com/Expecho/ServiceFabric-Remoting-CustomHeaders/blob/master/src/ServiceFabric.Remoting.CustomHeaders/CustomHeaders.cs)
- [RemotingContext](https://github.com/Expecho/ServiceFabric-Remoting-CustomHeaders/blob/master/src/ServiceFabric.Remoting.CustomHeaders/RemotingContext.cs)## NuGet
[Download the NuGet package](https://www.nuget.org/packages/ServiceFabric.Remoting.CustomHeaders) [![NuGet Status](http://img.shields.io/nuget/v/ServiceFabric.Remoting.CustomHeaders.svg?style=flat)](https://www.nuget.org/packages/ServiceFabric.Remoting.CustomHeaders/)
## Examples
This repository includes a Service Fabric application for demonstration purposes. A [Console Application](https://github.com/Expecho/ServiceFabric-Remoting-CustomHeaders/blob/master/src/Demo/Program.cs) is used to access the application and shows the usage of the package.
## Usage scenarios
Custom headers can be used to pass data between the sender and the receiver like tracing information or security context data. Using the BeforeHandleRequestResponseAsync and AfterHandleRequestResponseAsync actions additional logging can be applied monitor the flow between remoting calls.
## How to use
### Prepare Reliable Services
Modify the service and create a listener that can handle the requests
```csharp
protected override IEnumerable CreateServiceInstanceListeners()
{
yield return new ServiceInstanceListener(context =>
new FabricTransportServiceRemotingListener(context,
new ExtendedServiceRemotingMessageDispatcher(context, this)));
}
```### Prepare Actors
Register the actor using the `ExtendedActorService` service (usually done in the program.cs file):
```csharp
ActorRuntime.RegisterActorAsync (
(context, actorType) =>
{
return new ExtendedActorService(context, actorType);
}).GetAwaiter().GetResult();
```
### SenderOn the sender side, create a proxy to the actor or service. The `Create` method accepts an instance of the `CustomHeaders` class:
**Calling Reliable Services**
```csharp
var customHeaders = new CustomHeaders
{
{"Header1", DateTime.Now},
{"Header2", Guid.NewGuid()}
};var serviceUri = new Uri("fabric:/ServiceFabric.Remoting.CustomHeaders.DemoApplication/DemoService");
var proxyFactory = new ServiceProxyFactory(handler =>
new ExtendedServiceRemotingClientFactory(
new FabricTransportServiceRemotingClientFactory(remotingCallbackMessageHandler: handler), customHeaders));
var proxy = proxyFactory.CreateServiceProxy(serviceUri); // or in case of actors
var actorResponse = proxy.SayHelloToActor().GetAwaiter().GetResult();
```**Sending Message to Actors**
```csharp
var customHeaders = new CustomHeaders
{
{"Header1", DateTime.Now},
{"Header2", Guid.NewGuid()}
};var serviceUri = new Uri("fabric:/ServiceFabric.Remoting.CustomHeaders.DemoApplication/DemoService");
var proxyFactory = new ActorProxyFactory(handler =>
new ExtendedServiceRemotingClientFactory(
new FabricTransportActorRemotingClientFactory(handler), customHeaders));
var proxy = proxyFactory.CreateActorProxy(serviceUri);
var response = proxy.SayHello().GetAwaiter().GetResult();
```There is an overload of the `Create` method that accepts a `Func`. This is useful in scenarios where the created proxy factory or proxy is reused. Since creating a proxy factory is expensive this is the preferred way if you need dynamic header values. The func is invoked on every request made using the proxy:
```csharp
var customHeadersProvider = new Func(() => new CustomHeaders
{
{"Header1", DateTime.Now},
{"Header2", Guid.NewGuid()}
});
var serviceUri = new Uri("fabric:/ServiceFabric.Remoting.CustomHeaders.DemoApplication/DemoService");
var proxyFactory = new ServiceProxyFactory(handler =>
new ExtendedServiceRemotingClientFactory(
new FabricTransportServiceRemotingClientFactory(remotingCallbackMessageHandler: handler), customHeadersProvider));
var proxy = proxyFactory.CreateServiceProxy(serviceUri);
```
### ReceiverThe receiving service or actor can extract the values in the custom headers using the `RemotingContext` class:
```csharp
public async Task SayHello()
{
var remotingContext =
string.Join(", ", RemotingContext.Keys.Select(k => $"{k}: {RemotingContext.GetData(k)}"));ServiceEventSource.Current.ServiceMessage(Context, $"SayHelloToActor got context: {remotingContext}");
return Task.FromResult($"Got the following message headers: {remotingContext}")
}
```Sample content of remotingContext:
> Header1: 06/24/2018 08:30:18, Header2: 2c95548a-6efd-4855-82eb-29ea827be87b
### Headers passthrough
In case the headers need to flow from one call to the other `CustomHeaders.FromRemotingContext` can be used as demonstrated:
```csharp
public async Task SayHelloToActor()
{
var remotingContext =
string.Join(", ", RemotingContext.Keys.Select(k => $"{k}: {RemotingContext.GetData(k)}"));ServiceEventSource.Current.ServiceMessage(Context, $"SayHelloToActor got context: {remotingContext}");
var proxyFactory = new ActorProxyFactory(handler =>
new ExtendedServiceRemotingClientFactory(
new FabricTransportActorRemotingClientFactory(handler), CustomHeaders.FromRemotingContext));
var proxy = proxyFactory.CreateActorProxy(new ActorId(1));
var response = await proxy.GetGreetingResponseAsync(CancellationToken.None);return $"DemoService passed context '{remotingContext}' to actor and got as response: {response}";
}
```This removes the need to create a new `CustomHeaders` instance based on the current values in the `RemotingContext`.
## Message interception
Messages can be intercepted on both the sending side and the receiving side. This can be used fo example to log method calls or performance.
### Client-side message interception
On the receiving side messages can be intercepted using the `BeforeHandleRequestResponseAsync` and `AfterHandleRequestResponseAsync` extension points when creating a service listener:
**For services**
```csharp
protected override IEnumerable CreateServiceInstanceListeners()
{
yield return new ServiceInstanceListener(context =>
new FabricTransportServiceRemotingListener(context,
new ExtendedServiceRemotingMessageDispatcher(context, this)
{
// Optional, log the call before being handled
BeforeHandleRequestResponseAsync = requestInfo =>
{
var sw = new Stopwatch();
sw.Start();
ServiceEventSource.Current.ServiceMessage(Context, $"BeforeHandleRequestResponseAsync {requestInfo.Service} {requestInfo.Method}");
return Task.FromResult(sw);
},
// Optional, log the call after being handled
AfterHandleRequestResponseAsync = responseInfo =>
{
var sw = (Stopwatch) responseInfo.State;
ServiceEventSource.Current.ServiceMessage(Context, $"AfterHandleRequestResponseAsync {responseInfo.Service} {responseInfo.Method} took {sw.ElapsedMilliseconds}ms");
return Task.CompletedTask;
}
}));
}
````**For actors**
```csharp
ActorRuntime.RegisterActorAsync (
(context, actorType) =>
{
var service = new ExtendedActorService(context, actorType)
{
// Optional, allows call interception. Executed before the response is handled
BeforeHandleRequestResponseAsync = requestInfo =>
{
ActorEventSource.Current.Message($"BeforeHandleRequestResponseAsync {requestInfo.ActorService} {requestInfo.Method} for actor {requestInfo.ActorId.ToString()}");
return Task.CompletedTask;
},
// Optional, allows call interception. Executed after the response is handled
AfterHandleRequestResponseAsync = responseInfo =>
{
ActorEventSource.Current.Message($"AfterHandleRequestResponseAsync {responseInfo.ActorService} {responseInfo.Method} for actor {responseInfo.ActorId.ToString()}");
return Task.CompletedTask;
}
};
return service;
}).GetAwaiter().GetResult();
```### Server-side message interception
On the sending side messages can be intercepted using the `BeforeSendRequestResponseAsync` and `AfterSendRequestResponseAsync` extension points when creating the `ExtendedServiceRemotingClientFactory` on constructor of the `ServiceProxyFactory`:
```csharp
var proxyFactory = new ServiceProxyFactory(handler => // or ActorProxyFactory in case of actors
new ExtendedServiceRemotingClientFactory(
new FabricTransportServiceRemotingClientFactory(remotingCallbackMessageHandler: handler), customHeadersProvider)
{
// Optional, log the call before being handled
BeforeSendRequestResponseAsync = requestInfo =>
{
var sw = new Stopwatch();
sw.Start();
Console.WriteLine($"BeforeSendRequestResponseAsync {requestInfo.Method}");
return Task.FromResult(sw);
},
// Optional, log the call after being handled
AfterSendRequestResponseAsync = responseInfo =>
{
var sw = (Stopwatch)responseInfo.State;
Console.WriteLine($"AfterSendRequestResponseAsync {responseInfo.Method} took {sw.ElapsedMilliseconds}ms");
return Task.CompletedTask;
}
});
```