![]() |
VOOZH | about |
dotnet add package Dilan.GrpcServiceDiscovery.Grpc --version 1.0.2
NuGet\Install-Package Dilan.GrpcServiceDiscovery.Grpc -Version 1.0.2
<PackageReference Include="Dilan.GrpcServiceDiscovery.Grpc" Version="1.0.2" />
<PackageVersion Include="Dilan.GrpcServiceDiscovery.Grpc" Version="1.0.2" />Directory.Packages.props
<PackageReference Include="Dilan.GrpcServiceDiscovery.Grpc" />Project file
paket add Dilan.GrpcServiceDiscovery.Grpc --version 1.0.2
#r "nuget: Dilan.GrpcServiceDiscovery.Grpc, 1.0.2"
#:package Dilan.GrpcServiceDiscovery.Grpc@1.0.2
#addin nuget:?package=Dilan.GrpcServiceDiscovery.Grpc&version=1.0.2Install as a Cake Addin
#tool nuget:?package=Dilan.GrpcServiceDiscovery.Grpc&version=1.0.2Install as a Cake Tool
👁 NuGet version (Dilan.GrpcServiceDiscovery.Grpc)
This library provides a fast, easy to setup way to implement a discovery service pattern within any .NET solution/project. Although there are several other more complex solutions out there that you might have consider, this one of mine have some features you might find matching your problem as it matched mine.
Dependencies:
The BlazorServer that is also available in this library is just a front end of the server logic and thus, its use is optional. If you prefer running the discovery server in other application you can do so very easily.
The need for this library came from a project I was working on where we had several web apps that were develop using Core 2.0. Those appps were dependant on Microsoft core web app packages. In this project we also had some services running as windows service, using framework 4.8 and we were lucky or unlucky to share several common libraries as those services were also making use of Microsoft web packages for health checks. Due to cybersecurity reasons we were forced to upgrade our web apps to updated versions of Core.Web libraries, which seemed to be impossible without destroying many things, because new libraries were not compatible with 4.8 framework.
By using this discovery service pattern that is based in GRCP we were able to reduce services configuration and the dependency on Core.Web libraries. (which by the way is a real pain)
I know is not the current trend, where everything has to be a wep application to be "modern", but this is a clean solution to have a discovery server pattern that would be used with several different services wihout adding a ton of core.web dependencies and allowing having functionality between full framework and updated web apps, while still being multi platform.
With this library you will be able to still have old style window full framework services, that can register into the service discovery and be communicated with. With the same library you can have state of the art web apps that can register into the service discovery and be communicated with.
This discovery server now becames a healthCheck provider, a DNS server and a load balancer, all together in a small, efficient and fast web application.
This library comes in two assemblies:
Easiest way is download the code and compile it. You can also get it from nuget or from github releases.
nuget package available https://www.nuget.org/packages/Dilan.GrpcServiceDiscovery.Grpc
See release assets: https://github.com/dilandau2001/Dilan.ServiceDiscovery/releases
The server constructor looks like this
public ServiceDiscoveryServer(
ILogger<ServiceDiscoveryServer> logger,
IServerManagerLogic logic,
ServiceConfigurationOptions options,
IMulticastClient client)
Where
If you are not using dependency injection, and you like default options, you would create an instance and start your discovery server like this:
var options = new ServiceConfigurationOptions();
var server = new ServiceDiscoveryServer(
new ConsoleLogger<ServiceDiscoveryServer>(),
new ServerManagerLogic(options),
options,
new MulticastClient(new ConsoleLogger<MulticastClient>()));
server.Start();
Server options are:
/// <summary>
/// Gets or sets the listening port for the server.
/// <remarks>By default is 6000</remarks>
/// </summary>
public int Port { get; set; } = 6000;
/// <summary>
/// Gets or sets the refresh time passed to clients.
/// This is the time the server will communicate the clients they have to refresh its status.
/// <remarks>By default is 1 second</remarks>
/// </summary>
public int RefreshTimeInSeconds { get; set; } = 1;
/// <summary>
/// Gets or sets the time out in seconds.
/// This is the time the server will wait for incoming registrations.
/// If a client service fails to register in less than this time then the service is configured to offline.
/// Note this time should be higher than the refresh time so the clients have plenty of time to even miss a few messages.
/// </summary>
public int TimeOutInSeconds { get; set; } = 5;
/// <summary>
/// Time in milliseconds for the timer that is checking if there are any service time out.
/// </summary>
public int TimeOutCheckingTimeMs { get; set; } = 1000;
/// <summary>
/// Enables auto discovery.
/// If enabled the server will be sending a multicast messages to the AutoDiscoverMulticastGroup,
/// with a frequency in seconds of AutoDiscoverFreq and to the port AutoDiscoverPort.
/// Clients can capture this message to retrieve discovery service ip and port.
/// </summary>
public bool EnableAutoDiscover { get; set; } = true;
/// <summary>
/// Auto discovery multicast group.
/// </summary>
public string AutoDiscoverMulticastGroup { get; set; } = "224.0.0.100";
/// <summary>
/// Auto discovery multicast port.
/// </summary>
public int AutoDiscoverPort { get; set; } = 5478;
/// <summary>
/// Auto discovery send data frequency.
/// </summary>
public int AutoDiscoverFreq { get; set; } = 5;
/// <summary>
/// Gets or sets a value indicating whether [use secure connection].
/// If secure connection is true and certificate name is found in the machine.
/// </summary>
/// <value>
/// <c>true</c> if [use secure connection]; otherwise, <c>false</c>.
/// </value>
public bool UseSecureConnection { get; set; } = true;
/// <summary>
/// Gets or sets the name of the certificate issuer name.
/// </summary>
/// <value>
/// The name of the certificate issuer name.
/// </value>
public string CertificateIssuerName { get; set; } = "dilan.ServiceDiscovery";
/// <summary>
/// Gets or sets a value indicating whether [use certificate file].
/// If this setting is set to true and UseSecureConnection is true then the Certificate file
/// is searched inside the application folder.
/// If this setting is false, then the certificate is searched in the Computer certificate repository.
/// (In windows the Manage Workstation Certificates)
/// </summary>
/// <value>
/// <c>true</c> if [user certificate file]; otherwise, <c>false</c>.
/// </value>
public bool UseCertificateFile { get; set; } = false;
/// <summary>
/// Gets or sets the use certificate file password.
/// When UseCertificateFile is used, in order to open the certificate file name.pfx you need
/// to pass the password in order to get the private key.
/// </summary>
/// <value>
/// The use certificate file password.
/// </value>
public string UseCertificateFilePassword { get; set; } = "dilandau2001";
If you use the BlazorServer server, this is done for you.
The BlazorServer is a Blazor application that provide a nice front end of the server discovery. As it is based in a Blazor application it can be run in several ways:
Service configuration is read from app settings and can be manually modified to match your needs.
Current settings look like this:
{
"Logging": {
"LogLevel": {
"Default": "Trace",
"Microsoft.AspNetCore": "Trace"
}
},
"AllowedHosts": "*",
"ServiceConfigurationOptions": {
"Port": 6000,
"RefreshTimeInSeconds": 2,
"TimeOutInSeconds": 5,
"TimeOutCheckingTimeMs": 1000
},
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://localhost:5294"
},
"Https": {
"Url": "https://localhost:5295"
}
}
}
}
The client constructor looks like this
public ServiceDiscoveryClient(
ILogger<ServiceDiscoveryClient> logger,
ClientConfigurationOptions options,
IMulticastClient multicastClient,
IEnumerable<IMetadataProvider> metadataProviders)
Where
If you are not using dependency injection, and you like default options, you would instance and start your discovery client like this:
var options = new ClientConfigurationOptions();
var client = new ServiceDiscoveryClient(
new ConsoleLogger<ServiceDiscoveryClient>(),
options,
new MulticastClient(new ConsoleLogger<MulticastClient>),
new List<IMetadataProvider>{new SystemInfoMetadataProvider()});
client.Start();
Client options are the following:
/// <summary>
/// Gets or sets the listening port for the server.
/// The client will used to make calls to.
/// <remarks>By default is 6000</remarks>
/// </summary>
public int Port { get; set; } = 6000;
/// <summary>
/// Host name of ip of discovery server service.
/// Client will used to make calls to it.
/// If empty, then auto discover will be used automatically.
/// </summary>
public string DiscoveryServerHost { get; set; }
/// <summary>
/// Service address. This address is send to the discovery server as callback address.
/// This is the address we are registering in the service discovery as telling others how to reach me.
/// </summary>
public string ServiceAddress { get; set; }
/// <summary>
/// Gets or sets the name of the service this client will register in the discovery server.
/// </summary>
public string ServiceName { get; set; } = "ServiceName";
/// <summary>
/// Gets or sets the port this service is listening to requests.
/// </summary>
public int CallbackPort { get; set; } = 6001;
/// <summary>
/// Auto discovery multicast group.
/// If DiscoveryServerHost is empty. Then auto discovery is used.
/// The client subscribes to this multicast group waiting for specific broadcasts coming from the server side.
/// </summary>
public string AutoDiscoverMulticastGroup { get; set; } = "224.0.0.100";
/// <summary>
/// Auto discovery multicast port.
/// The client waits for messages coming from the server in this port, only if auto discovery is enabled.
/// (See DiscoveryServerHost)
/// </summary>
public int AutoDiscoverPort { get; set; } = 5478;
/// <summary>
/// Default client scope. Similar to a tag, domain, or environment where this client is under.
/// It allows you to group this client as part of a set of clients of different services.
/// </summary>
public string Scope { get; set; } = string.Empty;
/// <summary>
/// Gets or sets a value indicating whether [use secure connection].
/// If secure connection is true and certificate name is found in the machine.
/// </summary>
/// <value>
/// <c>true</c> if [use secure connection]; otherwise, <c>false</c>.
/// </value>
public bool UseSecureConnection { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether [allow invalid certificates] is enabled.
/// If enabled, invalid certificates like self-signed or untrusted certificates will be accepted.
/// By using an untrusted invalid certificate you are encrypting the communication from end 2 end
/// but you will be not safe against a man in the middle attack.
/// </summary>
/// <value>
/// <c>true</c> if [allow invalid certificates]; otherwise, <c>false</c>.
/// </value>
/// <remarks>Configuring the SSL communication is always a difficult task.
/// You need to create a proper certificate for the server part.
/// As a rule of thumb the issuer name usually matches the machine name, or dns of the server machine, where the server is running,
/// and the client should reach it using this dns and not the ip. Also the certification provider authority should be trusted by the client.
/// For Self-signed certificates you could achieve this trust by adding server certificate to the Trusted authorities in the client side.</remarks>
public bool AllowInvalidCertificates { get; set; } = true;
Default server will be available at:
https://localhost:5295/
Default page show a Services tab where all registered services are shown. You will be able to sort, find and enable/disable services from this page. Enabling and disabling services is done in runtime. The page automaticaly refreses on every change.
PRs accepted and I will be really greatfull for them.
There are several things I haven't addressed yet.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 was computed. net5.0-windows net5.0-windows was computed. net6.0 net6.0 was computed. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 was computed. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 netcoreapp2.0 was computed. netcoreapp2.1 netcoreapp2.1 was computed. netcoreapp2.2 netcoreapp2.2 was computed. netcoreapp3.0 netcoreapp3.0 was computed. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 netstandard2.0 is compatible. netstandard2.1 netstandard2.1 was computed. |
| .NET Framework | net461 net461 was computed. net462 net462 was computed. net463 net463 was computed. net47 net47 was computed. net471 net471 was computed. net472 net472 was computed. net48 net48 was computed. net481 net481 was computed. |
| MonoAndroid | monoandroid monoandroid was computed. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen40 tizen40 was computed. tizen60 tizen60 was computed. |
| Xamarin.iOS | xamarinios xamarinios was computed. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.