🎄 Happy Holidays! 🥳
Most of Solace is closed December 24–January 1 so our employees can spend time with their families. We will re-open Thursday, January 2, 2024. Please expect slower response times during this period and open a support ticket for anything needing immediate assistance.
Happy Holidays!
Please note: most of Solace is closed December 25–January 2, and will re-open Tuesday, January 3, 2023.
Using Spring Boot Actuator with Spring Cloud Stream
I figured I'd share a quick post on how to use Spring Boot Actuator with Spring Cloud Stream microservices as it might be useful for others (And me when I want to set this up again in a month! 😝). This allows you to make simple GET requests to see config, state, and stats about your bindings and channels for monitoring or troubleshooting purposes.
- Include
spring-boot-starter-actuator
andspring-boot-starter-web
in your pom for your Cloud Stream microservice (you can also add them via Spring Initializr if you're using that).
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
- Enable in your application config. You can include items one at a time (like bindings below or use the "*" to include anything available). Don't expose all of this in production without researching it more!
spring: jackson: serialization: indent-output: true management: endpoints: web: exposure: include: - bindings - "*" server: port: 8081
- Start your app and run a query!
A few that I find useful:
- Find your bindings: http://localhost:8081/actuator/bindings
- Query a specific binding: http://localhost:8081/actuator/bindings/
- Find available channels: http://localhost:8081/actuator/channels
- See the Integration Graph (this includes stats, such as message counts) http://localhost:8081/actuator/integrationgraph
Example response for my input binding (http://localhost:8081/actuator/bindings/input)
{ "bindingName" : "input", "name" : "input", "group" : "consumerGroup", "pausable" : false, "state" : "running", "extendedInfo" : { "bindingDestination" : "SolaceConsumerDestination{queueName='input.consumerGroup'}", "ExtendedConsumerProperties" : { "autoStartup" : true, "concurrency" : 5, "instanceCount" : 1, "maxAttempts" : 3, "backOffInitialInterval" : 1000, "backOffMaxInterval" : 10000, "backOffMultiplier" : 2.0, "defaultRetryable" : true, "extension" : { "prefix" : "", "provisionDurableQueue" : true, "provisionSubscriptionsToDurableQueue" : true, "queueAccessType" : 0, "queuePermission" : 2, "queueDiscardBehaviour" : null, "queueMaxMsgRedelivery" : null, "queueMaxMsgSize" : null, "queueQuota" : null, "queueRespectsMsgTtl" : null, "anonymousGroupPostfix" : "anon", "polledConsumerWaitTimeInMillis" : 100, "requeueRejected" : false, "queueAdditionalSubscriptions" : [ "topic/>", "a/*/c" ], "autoBindDmq" : false, "provisionDmq" : true, "dmqAccessType" : 0, "dmqPermission" : 2, "dmqDiscardBehaviour" : null, "dmqMaxMsgRedelivery" : null, "dmqMaxMsgSize" : null, "dmqQuota" : null, "dmqRespectsMsgTtl" : null, "republishedMsgTtl" : null } } }, "input" : true }
Hope that's helpful!
-Marc
Comments
-
Thanks, Mark. Great article.
One quick question, any idea why the health is UNKNOWN?
curl http://localhost:8080/actuator/health
{ "status": "UP", "components": { "binders": { "status": "UNKNOWN", "components": { "solace": { "status": "UNKNOWN" } } }, "discoveryComposite": { "description": "Discovery Client not initialized", "status": "UNKNOWN", "components": { "discoveryClient": { "description": "Discovery Client not initialized", "status": "UNKNOWN" } } }, "diskSpace": { "status": "UP", "details": { "total": 97888145408, "free": 75725651968, "threshold": 10485760, "exists": true } }, "ping": { "status": "UP" }, "reactiveDiscoveryClients": { "description": "Discovery Client not initialized", "status": "UNKNOWN", "components": { "Simple Reactive Discovery Client": { "description": "Discovery Client not initialized", "status": "UNKNOWN" } } }, "refreshScope": { "status": "UP" } } }
application.properties
spring.cloud.stream.bindings.articleConsumeSolace-in-0.destination=articles spring.cloud.stream.bindings.articleConsumeSolace-in-0.group=nonexclusive spring.cloud.stream.solace.bindings.articleConsumeSolace-in-0.consumer.durable-subscription=true spring.cloud.stream.solace.bindings.articleConsumeSolace-in-0.consumer.provisionSubscriptionsToDurableQueue=false spring.cloud.stream.solace.bindings.articleConsumeSolace-in-0.consumer.provisionDurableQueue=false spring.cloud.stream.solace.bindings.articleConsumeSolace-in-0.consumer.queueNamePrefix= spring.cloud.stream.solace.bindings.articleConsumeSolace-in-0.consumer.useFamiliarityInQueueName=false spring.cloud.stream.solace.bindings.articleConsumeSolace-in-0.consumer.useDestinationEncodingInQueueName=false spring.cloud.stream.solace.bindings.articleConsumeSolace-in-0.consumer.useGroupNameInQueueName=false spring.cloud.stream.solace.bindings.articleConsumeSolace-in-0.consumer.content-type=application/json # articleError-out-0 spring.cloud.stream.bindings.articleError-out-0.destination=articles/error spring.cloud.stream.bindings.articleError-out-0.group=nonexclusive spring.cloud.stream.solace.bindings.articleError-out-0.producer.provisionSubscriptionsToDurableQueue=false spring.cloud.stream.solace.bindings.articleError-out-0.producer.provisionDurableQueue=false spring.cloud.stream.solace.bindings.articleError-out-0.producer.queueNamePrefix= spring.cloud.stream.solace.bindings.articleError-out-0.producer.useFamiliarityInQueueName=false spring.cloud.stream.solace.bindings.articleError-out-0.producer.useDestinationEncodingInQueueName=false spring.cloud.stream.solace.bindings.articleError-out-0.producer.useGroupNameInQueueName=false spring.cloud.stream.solace.bindings.articleError-out-0.producer.content-type=application/json spring.cloud.stream.binders.solace.type=solace spring.cloud.stream.binders.solace.environment.solace.java.host=tcps:// spring.cloud.stream.binders.solace.environment.solace.java.msgVpn= spring.cloud.stream.binders.solace.environment.solace.java.clientUsername= spring.cloud.stream.binders.solace.environment.solace.java.clientPassword= spring.cloud.stream.binders.solace.environment.solace.java.connectRetries=-1 spring.cloud.stream.binders.solace.environment.solace.java.reconnectRetries=-1 spring.cloud.stream.binders.solace.environment.solace.java.max-redelivery=-1 spring.cloud.stream.binders.solace.environment.solace.java.concurrency=-1 spring.cloud.stream.binders.solace.environment.solace.java.maxAttempts=-1 spring.cloud.stream.binders.solace.environment.solace.java.apiProperties.ssl_validate_certificate=true spring.cloud.stream.binders.solace.environment.solace.java.apiProperties.ssl_trust_store_password= spring.cloud.stream.binders.solace.environment.solace.java.apiProperties.ssl_trust_store=src/main/resources/solace-certificates.jks endpoints.health.sensitive=false management.security.enabled=false management.health.jms.enabled=false management.endpoint.metrics.enabled=true management.endpoint.prometheus.enabled=true management.endpoint.health.show-details=always management.endpoint.health.show-components=always management.endpoints.web.exposure.include=* management.metrics.export.prometheus.enabled=true
0 -
Hi @glenn_esl,
We are actually working on adding spring boot actuator support for the Solace binder right now. Keep your eyes peeled as it should be available in our next Spring Cloud Stream Binder release (expected next month). The previous info that I posted in this thread was just items that were exposed by the framework itself.
It will not be exactly what you see in this PR, but it will end up looking similar to what you see there. Actually, go ahead and watch that PR for updates as it will either be merged w/ changes or closed in favor of another PR.
2 -
@marc when this health future will be available?
0