🎄 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

marc
marc Member, Administrator, Moderator, Employee Posts: 963 admin
edited August 2020 in Tips and Tricks #1

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.

  1. Include spring-boot-starter-actuator and spring-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>
  1. 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
  1. Start your app and run a query!
    A few that I find useful:

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

  • glenn_esl
    glenn_esl Member Posts: 9
    edited February 2022 #2

    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
    
  • marc
    marc Member, Administrator, Moderator, Employee Posts: 963 admin

    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.

  • ajinkyasagar
    ajinkyasagar Member Posts: 24
    edited February 2022 #4

    @marc when this health future will be available?

  • marc
    marc Member, Administrator, Moderator, Employee Posts: 963 admin

    The next Spring Cloud Stream Binder release is targeted for the first half of March so coming very soon :)