Service Discovery#

While consuming a lot of different API, the problem to solve is to simplify the registration of services and its discoverability.

So the first approach is to have a static discovery, like we have in the previous simple, but it means have a registry of services in a configuration. The service registry may be commited in a tool like puppet, but in case there is a lots of services, this does not scale, periodic run of a puppet agent must run to discover new services.

To avoid the static inventory of service, tools may be used to handle the service registry, and build a client-side service discovery or a server-side discovery to have a dynamic approach.

Static Discovery Example#

Async#

from blacksmith import AsyncStaticDiscovery

sd = AsyncStaticDiscovery(
    {
        ("gandi", "v5"): "https://api.gandi.net/v5/",
        ("github", None): "https://api.github.com/",
        ("sendinblue", "v3"): "https://api.sendinblue.com/v3/",
    }
)

Sync#

from blacksmith import SyncStaticDiscovery

sd = SyncStaticDiscovery(
    {
        ("gandi", "v5"): "https://api.gandi.net/v5/",
        ("github", None): "https://api.github.com/",
        ("sendinblue", "v3"): "https://api.sendinblue.com/v3/",
    }
)

In that case we have a registry of public service.

Note

Because those service does not share the same Authentication mechanism, this example is not really usefull. By the way, writin a custom Authentication Middleware can handle it.

Client Side Service Discovery#

Consul Example#

ConsulDiscovery is consuming the Consul API to fetch host that are registered client side, this is a client-side service discovery.

Async#

from blacksmith import AsyncConsulDiscovery

# all parameters here are optional, the value
# here are the defaults one for the example.
sd = AsyncConsulDiscovery(
    "http://consul:8500/v1",
    service_name_fmt="{service}-{version}",
    service_url_fmt="http://{address}:{port}/{version}",
    unversioned_service_name_fmt="{service}",
    unversioned_service_url_fmt="http://{address}:{port}",
    consul_token="abc",
)

Sync#

from blacksmith import SyncConsulDiscovery

# all parameters here are optional, the value
# here are the defaults one for the example.
sd = SyncConsulDiscovery(
    "http://consul:8500/v1",
    service_name_fmt="{service}-{version}",
    service_url_fmt="http://{address}:{port}/{version}",
    unversioned_service_name_fmt="{service}",
    unversioned_service_url_fmt="http://{address}:{port}",
    consul_token="abc",
)

Warning

Using consul in client require some discipline in naming convention, endoint must match pattern to build the rest endpoint. So every endpoint must follow the same pattern here.

Server Side Service Discovery#

Router Example#

RouterDiscovery is calling every service behind a service gateway, a proxy, that is connected to the service registry to update is configuration.

Async#

from blacksmith import AsyncRouterDiscovery

sd = AsyncRouterDiscovery(
    service_url_fmt="http://router/{service}/{version}",
    unversioned_service_url_fmt="http://router/{service}",
)

Sync#

from blacksmith import SyncRouterDiscovery

sd = SyncRouterDiscovery(
    service_url_fmt="http://router/{service}/{version}",
    unversioned_service_url_fmt="http://router/{service}",
)

Warning

Every endpoint must follow the same pattern here, it works well if the router configuration is based on a service registry, but if the configuration of the router is maded by humans, inconcistency may exists, and the Static Discovery should be used instead.