Skip to content

Service Discovery

For Suite Services, you just need to inject the AppService's interface of the service that you'd like to use and the wiring is done by the Suite Framework.

However, If you'd like to know how it works under the hood, keep reading.

When consuming a service in .Net, you use an HttpClient created through an IHttpClientFactory. The .AddServiceDiscovery() extension method must be added.

When doing so, a DelegatingHandler is added to the HttpClient's pipeline which will assume that the host of the RequestUri is the name of a service. It will attempt to discover a service with that name and replace the RequestUri's host with an address for reaching that service. For example:

C#
internal class ServiceDiscoveryAppSample : SuiteModule
{
    public override void SetupModule(IModuleBuilder builder)
    {
        // This will add the default implementation
        // For now this can be switched at build time only.
        builder.DependsOn<ServiceDiscoveryModule>();
    }

    public override void ConfigureServices(IServiceCollection services,
        ModuleConfigurationContext context)
    {
        // You can construct/decorate your client how you please
        services.AddHttpClient("my-custom-discovery-client", c =>
        {
           // Note that the URL is the ID of the service
           c.BaseAddress = new Uri("http://PositionService/");
        })
           .AddServiceDiscovery();
    }

You can then inject IHttpClientFactory for creating clients.

C#
1
2
3
4
5
6
7
8
public class SampleService {

    public SampleService(IHttpClientFactory factory)
    {
        // PositionService will be replaced with the address that the registry
        // returns for that id.
        factory.CreateClient("my-custom-discovery-client").GetAsync("position/123");
    }

If the service you're consuming is not an HTTP Service, you need to inject the IServiceDiscoveryService and use it to resolved the address instead:

C#
1
2
3
4
5
6
var sd = context.GetRequiredService<IServiceDiscoveryService>();

var output = await sd.GetAddressForServiceAsync("PositionService");

output.Address
output.Port

GetAddressForServiceAsync will return a single address and take care of the load balancing process.

Default Service Discovery Provider

The ServiceDiscoveryModule supports configuring a provider for performing the actual discovery from a service registry.

The default implementation uses values from IConfiguration which means that the actual information for the services may come from different or multiple sources.

Configuration Example

Since the default implementation gets the values from IConfiguration to resolve services, we may use the appsettings.json file to provide them. This is a good setup for development purposes.

For example:

JSON
{
    "ITsynch.Suite.ServiceDiscovery.ServiceDiscoveryDefaultProviderOptions": {
        "services": {
            "identity-service": {
                "addresses": [
                    "identity-instance-01.corporatedomain.com:80",
                    "identity-instance-02.corporatedomain.com:8080"
                ]
            },
            "aims-api-service": {
                "addresses": ["apps-vm.corporatedomain.com/aims"]
            }
        }
    }
}

This will register two services, identity-service and aims-api-service. Some things to note:

  1. No protocol is specified. i.e, no http/s is provided. The addresses is a list of network addresses, which in the case of the default provider, may support a relative path. However, URLs are not supported.
  2. If the connection will be secured or not (https) will be decided at runtime by the consumer through the EnforceSecureChannels config of the Service Discovery Module. It is the consumer's desire to connect securely due to the importance of the data being sent.