$ mn create-app my-app --features discovery-consul
Micronaut Discovery Client
Adds Service Discovery Features for Eureka and Consul
Version:
1 Introduction
Using the CLI
If you are creating your project using the Micronaut CLI, supply either of |
Service Discovery enables the ability for Microservices to find each other without necessarily knowing the physical location or IP address of associated services.
There are many ways Service Discovery can be implemented, including:
-
Manually implement Service Discovery using DNS without requiring a third party tool or component.
-
Delegate the work to a container runtime, such as Kubernetes.
With that in mind, Micronaut tries to flexible to support all of these approaches. As of this writing, Micronaut features integrated support for the popular Service Discovery servers:
-
Eureka
-
Consul
To include Service Discovery in your application simply the first step is to add the discovery-client
dependency to your application:
implementation("io.micronaut:micronaut-discovery-client")
<dependency>
<groupId>io.micronaut</groupId>
<artifactId>micronaut-discovery-client</artifactId>
</dependency>
The discovery-client
dependency provides implementations of the DiscoveryClient interface.
The DiscoveryClient is fairly simple and provides two main entry points:
-
DiscoveryClient.getServiceIds() - Returns all discovered service IDs
-
DiscoveryClient.getInstances(java.lang.String) - Returns all the ServiceInstance objects for a given service ID
Both methods return Publisher instances since the operation to retrieve service ID information may result in a blocking network call depending on the underlying implementation.
If you are using Micronaut’s cache module, the default implementation of the DiscoveryClient
interface is CachingCompositeDiscoveryClient which merges all other DiscoveryClient
beans into a single bean and provides caching of the results of the methods. The default behaviour is to cache for 30 seconds. This cache can be disabled in application configuration:
micronaut:
caches:
discovery-client:
enabled: false
Alternatively you can alter the cache’s expiration policy:
micronaut:
caches:
discovery-client:
expire-after-access: 60s
See the DiscoveryClientCacheConfiguration class for available configuration options.
1.1 Consul Support
Consul is a popular Service Discovery and Distributed Configuration server provided by HashiCorp. Micronaut features a native non-blocking ConsulClient that is built using Micronaut’s support for Declarative HTTP Clients.
Starting Consul
The quickest way to start using Consul is via Docker:
-
Starting Consul with Docker.
docker run -p 8500:8500 consul
Alternatively you can install and run a local Consul instance.
Auto Registering with Consul
To register a Micronaut application with Consul simply add the necessary ConsulConfiguration. A minimal example can be seen below:
micronaut:
application:
name: hello-world
consul:
client:
registration:
enabled: true
defaultZone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}"
Using the Micronaut CLI you can quickly create a new service setup with Consul using: mn create-app my-app --features discovery-consul
|
The consul.client.defaultZone
settings accepts a list of Consul servers to be used by default.
You could also simply set consul.client.host and consul.client.port , however ConsulConfiguration allows you specify per zone discovery services for the purpose load balancing. A zone maps onto a AWS availability zone or a Google Cloud zone.
|
By default registering with Consul is disabled hence you should set consul.client.registration.enabled
to true
. Note that you may wish to do this only in your production configuration.
Running multiple instances of a service may require an additional configuration param. See below. |
If you are running the same applications on the same port across different servers it is important to set the micronaut.application.instance.id
property or you will experience instance registration collision.
micronaut:
application:
name: hello-world
instance:
id: ${random.shortuuid}
Customizing Consul Service Registration
The ConsulConfiguration class features a range of customization options for altering how an instance registers with Consul. You can customize the tags, the retry attempts, the fail fast behaviour and so on.
Notice too that ConsulConfiguration
extends DiscoveryClientConfiguration which in turn extends HttpClientConfiguration allowing you to customize the settings for the Consul client, including read timeout, proxy configuration and so on.
For example:
micronaut:
application:
name: hello-world
consul:
client:
registration:
enabled: true
# Alters the tags
tags:
- hello
- world
# Alters the metadata
meta:
some: value
instance_type: t2.medium
# Alters the retry count
retry-count: 5
# Alters fail fast behaviour
fail-fast: false
defaultZone: "${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}"
Discovery Services from Consul
To discovery other services you could manually interact with the DiscoveryClient, however typically instead you use the Client Annotation to declare how an HTTP client maps to a service.
For example the configuration in the previous section declared a value for micronaut.application.name
of hello-world
. This is the value that will be used as the service ID when registering with Consul.
Other services can discovery instances of the hello-world
service simply by declaring a client as follows:
@Client(id = "hello-world")
interface HelloClient{
...
}
Alternatively you can also use @Client as a qualifier to @Inject
an instance of HttpClient:
@Client(id = "hello-world")
@Inject
RxHttpClient httpClient;
Consul Health Checks
By default when registering with Consul Micronaut will register a TTL check. A TTL check basically means that if the application does not send a heartbeat back to Consul after a period of time the service is put in a failing state.
Micronaut applications feature a HeartbeatConfiguration which starts a thread using HeartbeatTask that fires HeartbeatEvent instances.
The ConsulAutoRegistration class listens for these events and sends a callback to the /agent/check/pass/:check_id endpoint provided by Consul, effectively keeping the service alive.
With this arrangement the responsibility is on the Micronaut application to send TTL callbacks to Consul on a regular basis.
If you prefer you can push the responsibility for health checks to Consul itself by registering an HTTP check:
consul:
client:
registration:
check:
http: true
With this configuration option in place Consul will assume responsibility of invoking the Micronaut applications Health Endpoint.
Controlling IP/Host Registration
Occasionally, depending on the deployment environment you may wish to expose the IP address and not the host name, since by default Micronaut will register with Consul with either the value of the HOST
environment variable or the value configured via micronaut.server.host
.
You can use the consul.client.registration.prefer-ip-address
setting to indicate you would prefer to register with the IP address.
Micronaut will by default perform an IP lookup to try and figure out the IP address, however you can use the consul.client.registration.ip-addr
setting to specify the IP address of the service directly.
consul:
client:
registration:
ip-addr: <your base container ip>
prefer-ip-address: true
This will tell Consul to register the IP that other instances can use to access your service and not the NAT IP it is running under (or 127.0.0.1).
If you use HTTP health checks (see the previous section) then Consul will use the configured IP address to check the Micronaut /health
endpoint.
consul:
client:
registration:
ip-addr: <your base container ip>
prefer-ip-address: true
check:
http: true
1.2 Eureka Support
Netflix Eureka is a popular discovery server deployed at scale at organizations like Netflix.
Micronaut features a native non-blocking EurekaClient as part of the discovery-client
module that does not require any additional third-party dependencies and is built using Micronaut’s support for Declarative HTTP Clients.
Starting Eureka
The quickest way to start a Eureka server is to use to use Spring Boot’s Eureka starters.
As of this writing the official Docker images for Eureka are significantly out-of-date so it is recommended to create a Eureka server following the steps above. |
Auto Registering with Eureka
The process to register a Micronaut application with Eureka is very similar to with Consul, as seen in the previous section, simply add the necessary EurekaConfiguration. A minimal example can be seen below:
micronaut:
application:
name: hello-world
eureka:
client:
registration:
enabled: true
defaultZone: "${EUREKA_HOST:localhost}:${EUREKA_PORT:8761}"
Customizing Eureka Service Registration
You can customize various aspects of registration with Eureka using the EurekaConfiguration. Notice that EurekaConfiguration
extends DiscoveryClientConfiguration which in turn extends HttpClientConfiguration allowing you to customize the settings for the Eureka client, including read timeout, proxy configuration and so on.
eureka:
client:
readTimeout: 5s
registration:
asgName: myAsg # the auto scaling group name
countryId: 10 # the country id
vipAddress: 'myapp' # The Eureka VIP address
leaseInfo:
durationInSecs: 60 # The lease information
metadata: # arbitrary instance metadata
foo: bar
retry-count: 10 # How many times to retry
retry-delay: 5s # How long to wait between retries
appname: some-app-name # optional, eureka instance application name, defaults to ${micronaut.application.name}
hostname: foo.example.com # optional, exposed eureka instance hostname, useful in docker bridged network environments
ip-addr: 1.2.3.4 # optional, exposed eureka instance ip address, useful in docker bridged network environments
port: 9090 # optional, exposed eureka instance port, useful in docker bridged network environments
Property | Type | Description |
---|---|---|
|
int |
|
|
int |
|
|
java.lang.String |
|
|
java.lang.String |
|
|
java.lang.String |
|
|
||
|
int |
|
|
java.lang.String |
|
|
java.lang.String |
|
|
java.lang.String |
|
|
||
|
||
|
java.lang.String |
|
|
java.lang.String |
|
|
java.lang.String |
|
|
java.lang.String |
|
|
java.util.Map |
|
|
long |
|
|
long |
|
|
long |
|
|
long |
|
|
int |
|
|
int |
|
|
boolean |
|
|
java.time.Duration |
|
|
boolean |
|
|
boolean |
|
|
boolean |
|
|
int |
|
|
java.time.Duration |
|
|
java.lang.String |
Eureka Basic Authentication
You can customize the Eureka credentials in the URI you specify to in defaultZone
.
For example:
eureka:
client:
defaultZone: "https://${EUREKA_USERNAME}:${EUREKA_PASSWORD}@localhost:8761"
The above example externalizes configuration of the username and password Eureka to environment variables called EUREKA_USERNAME
and EUREKA_PASSWORD
.
Eureka Health Checks
Like Consul, the EurekaAutoRegistration will send HeartbeatEvent instances with the HealthStatus of the Micronaut application to Eureka.
The HealthMonitorTask will by default continuously monitor the HealthStatus of the application by running health checks and the CurrentHealthStatus will be sent to Eureka.
Secure Communication with Eureka
If you wish to configure HTTPS and have clients discovery Eureka instances and communicate over HTTPS then you should set the eureka.client.discovery.use-secure-port
option to true
to ensure that service communication happens over HTTPS and also configure HTTPS appropriately for each instance.