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.
Note
Take a look at the example!
https://github.com/mardiros/blacksmith/tree/master/examples/consul_sd
Nomad Example¶
When using Consul Connect in a Nomad cluster, upstreams declared in a jobspec make available a mTLS connection on a local_bind_port. Addresses of these services are injected as environment variables during deployment of the job.
Async¶
from blacksmith import AsyncNomadDiscovery
# no parameter needed, discovery use environment variables.
sd = AsyncNomadDiscovery()
# no version needed, only the service name
service = sd.get_endpoint("service_name")
Sync¶
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.
Note
Take a look at the example!
https://github.com/mardiros/blacksmith/tree/master/examples/consul_template_sd