u6f6o
u6f6o

Reputation: 2190

How to configure the vertx instance itself only once and use it in application code and tests?

I am currently migrating one of our clojure services to vert.x, using kotlin as main language. Most things work like a charm, but there is one issue I am struggling with for quite some time now.

All of our services use micrometer.io and prometheus to collect metrics. According to the documentation, integrating micrometer is straight-forward:

val vertx = Vertx.vertx(
    VertxOptions().setMetricsOptions(
        MicrometerMetricsOptions()
            .setPrometheusOptions(VertxPrometheusOptions().setEnabled(true))
            .setEnabled(true)
    )
)

In general, this approach works nicely - metrics are gathered and exposed by adding a separate route:

router.get("/metrics").handler(PrometheusScrapingHandler.create())

The problem I am struggling with is, that there is no way I am aware of to define these vert.x-related settings (VertxOptions) only once and publish them globally - basically whenever a new Vertx instance is created.

This is an issue, because currently I have to define these settings on three different places:

1.) Server.kt

Allows to start the service with my IDE

2.) ServerLauncher

The purpose of this class is to start the server from the commandline with gradle.

3.) Integration tests

Vert.x provides a nifty junit5 extension (VertxExtension) which automatically injects the Vertx and VertxTestContext instances in your test methods. The downside is that there is no way to configure the injected Vertx instance as it always injects it with default settings.

Thus you have to wire everything on your own in your test method:

@Test
@DisplayName("Call prometheus endpoint and verify core metrics are present")
fun callPrometheusEndpoint(testCtx: VertxTestContext) {
    val vertx = Vertx.vertx(
        VertxOptions().setMetricsOptions(
            MicrometerMetricsOptions()
                .setPrometheusOptions(VertxPrometheusOptions().setEnabled(true))
                .setEnabled(true)
        )
    )
    vertx.deployVerticle(
        MyVerticle(),
        testCtx.completing()
    )
    WebClient.create(vertx)
        .get(8080, "localhost", "/internal/prometheus")
        .`as`(BodyCodec.string())
        .send(testCtx.succeeding { resp ->
            testCtx.verify {
                // assertions to follow...
                testCtx.completeNow()
            }
        })
}

I am wondering, if there is any way to define the VertxOptions only once and thus override/complement the default settings used, whenever a Vertx instance is created?

Update 1

I decided to extract a separate Application class in order to configure the Vertx instance and get rid of Server.kt and ServerLauncher.kt.

class Application(
    private val profileSetting: String? = System.getenv("ACTIVE_PROFILES"),
    private val logger: Logger = LoggerFactory.getLogger(Application::class.java)!!
) {

    fun bootstrap() {
        val profiles = activeProfiles()

        val vertx = bootstrapVertx(profiles)
        val configRetriever = bootstrapConfigRetriever(vertx, profiles)

        val myVerticle = MyVerticle(configRetriever)

        vertx.deployVerticle(myVerticle) { startup ->
            if (startup.succeeded()) {
                logger.info("Application startup finished")
            } else {
                logger.error("Application startup failed", startup.cause())
                vertx.close()
            }
        }
    }

    internal fun activeProfiles(): List<String> {
        logger.info("Configured profiles: {}", profileSetting)
        return profileSetting
            ?.let { it.split(',').map { p -> p.trim() }.filter { p -> p.isNotBlank() } }
            ?: emptyList()
    }

    internal fun bootstrapVertx(profiles: List<String>): Vertx {
        registerModules()
        val vertxOptions = VertxOptionsFactory(profiles).create()
        return Vertx.vertx(vertxOptions)
    }

    internal fun bootstrapConfigRetriever(vertx: Vertx, profiles: List<String>): ConfigRetriever {
        return ConfigRetrieverFactory(profiles).create(vertx)
    }

    private fun registerModules() {
        Json.mapper.apply { registerKotlinModule() }
        Json.prettyMapper.apply { registerKotlinModule() }
    }

    companion object {
        @JvmStatic
        fun main(args: Array<String>) = Application().bootstrap()
    }
}

I found no way to pass the configured Vertx instance to the VertxExtention though.

Update 2

I created a pull request that addresses the issue of pre-configuring vertx instances in tests.

Upvotes: 1

Views: 1529

Answers (2)

Evgeny Cheryomushkin
Evgeny Cheryomushkin

Reputation: 380

I'd like to share some more info to @tsegismont answer.

To use prometheus metrics you can define options.json as following (after adding necessary includes to your pom or build.gradle):

{ "metricsOptions": {
  "enabled": true,
  "prometheusOptions" : {
    "enabled": true, 
    "embeddedServerOptions": {"port": 8080},
    "startEmbeddedServer": true 
} } }

it will expose metrices on host:8080/metrics

To put more options into options.json - I just scanned source code of

BareCommand.getMetricsOptions
MicrometerMetricsOptionsConverter.fromJson

There are easy to read json parsing functions.

  1. To add options to your jar: java -jar your-vertx-app.jar -options options.json

  2. To use it from IDE, you may use Launcher.executeCommand:

java:

public static void main(final String[] args) {
    Launcher.executeCommand("run", args);
}

kotlin:

fun main(args: Array<String>) {
    Launcher.executeCommand("run", "com.your.LauncherVerticle", *args)
}

Afther that you can pass -options options.json parameters in case of kotlin or com.your.LauncherVerticle -options options.json in case of java to program arguments of your run configuration. Launcher.executeCommand will use this file.

  1. For unit tests documentation states: "Note The Vertx instance is not clustered and has the default configuration. If you need something else then just don’t use injection on that parameter and prepare a Vertx object by yourself."

Upvotes: 0

tsegismont
tsegismont

Reputation: 9128

Since Vert.x 3.6.0, you can put Vert.x options in a file and load them with -options. That works if you are using the CLI or a Launcher class.

When you have to start Vert.x in embedded mode (eg tests), you can read the file content, and create the VertxOptions instance from a JsonObject.

Upvotes: 2

Related Questions