Crey - netstandard2.0 microservice framework
PM> Install-Package Crey
- DotNetty (transport layer)
- Consul / Static list (service discovery)
- MessagePack (message serialization)
- Castle.Core (for building DynamicProxy)
- Polly (retry strategy)
// contract is referenced by client and service
public interface ITestContract : IMicroService
{
Task<string> Say(string name);
}
// using .net core's GenericHost
var host = Host.CreateDefaultBuilder(args)
.UseMicroServices((context, builder) =>
{
builder
// choose either static discovery list or or Consul based one
//.AddStaticListDiscovery()
.AddConsulDiscovery()
.AddMicroService(builder =>
{
// register contract mapping
builder.AddContract<ITestContract, TestService>();
})
.AddMicroClient();
})
.Build();
await host.RunAsync();
// contract implementation
public class TestService : ITestContract
{
public Task<string> Say(string message)
{
return Task.FromResult($"pong: {message}");
}
}
Configuration
{
"Micro": {
"Service": {
"Host": "192.168.1.24", // define host IP
"Port": 5003 // port to bind to
},
"Discovery": {
"Consul": {
"Server": "http://localhost:8500", // consul address
"Token": ""
}
}
}
}
// create using existing IServiceProvider instance
var proxyFactory = ClientBuilder.Create(provider).CreateProxyFactory();
// OR without it (IServiceProvider is created internally)
var proxyFactory = ClientBuilder.Create(builder =>
{
builder
// choose either static discovery list or Consul based one
//.AddStaticListDiscovery(x =>
//{
// x.Set<ITestContract>(new[] { new ServiceAddress("192.168.1.24", 5003) });
//})
.AddConsulDiscovery(x =>
{
x.Server = "http://localhost:8500";
})
.AddMicroClient();
})
.CreateProxyFactory();
// creating contract proxy instance
var contract = proxyFactory.Create<ITestContract>();
// use it like common .net method (RPC)
var res = await contract.Say("Hello world");
Configuration
{
"Micro": {
"Discovery": {
"Consul": {
"Server": "http://localhost:8500", // consul address
"Token": ""
}
}
}
}
// creating contract proxy instance
var contract = proxyFactory.Create<ITestContract>();
// use Extension on contract instance
await contract.InvokeOneWay((x) => x.Say("Hello OneWay"));
To access context anywhere in code, inject an instance of ICallContextAccessor
in your constructor
public class TestService
{
public TestService(ICallContextAccessor callContextAccessor)
{
}
}
Cross-cutting concepts can be implemented using Middlewares IClientMiddleware
and IServiceMiddleware
First, implement these interfaces. Then register these in client or server builders
builder.AddMiddleware<TMiddleware>();
Following providers are available out of the box:
Configured under discovery
tag in configuration
Consul provider can be configured with these values
- Tags
- Metadata
- Check options
"Consul": {
"Server": "http://localhost:8500",
"Token": "",
"Service": {
"Tags": [
"DEV"
],
"Meta": {
"Environment": "Development"
},
"Check": {
"DeregisterCriticalServiceAfter": "00:10:00", // TimeSpan - 10mins
"Timeout": 5,
"Interval": 1
}
}
Static List provider can be configured with these values
"Static": {
"Crey.Tests.Contracts_v1": [
"Host": "localhost",
"Port": 5003,
"Weight": 1 // optional, used in weighted random algorithm
]
}
Where Key
= $"{AssemblyNamespace}_v{AssemblyVersion.Major}"
- Tests
- Check internal services override possibility
- Kestrel hosting with
ConnectionHandler
- Make
NetworkUtilities
configurable through DI - replace
ProxyFactory
with other abstraction on client creation
BenchmarkDotNet=v0.13.3, OS=Windows 11 (10.0.25193.1000)
Intel Core i7-8550U CPU 1.80GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores
.NET SDK=7.0.101
[Host] : .NET 6.0.12 (6.0.1222.56807), X64 RyuJIT AVX2
DefaultJob : .NET 6.0.12 (6.0.1222.56807), X64 RyuJIT AVX2
Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
---|---|---|---|---|---|---|
CreateContractAndCallMethod | 113.2 ms | 18.02 ms | 53.13 ms | 93.7500 | 31.2500 | 491.28 KB |
Licenced under MIT