Статьи

Groovy: добавьте проверки здоровья с использованием Ratpacked

В ядре Ratpack мы можем найти ratpack.health.HealthCheckинтерфейс. Мы можем реализовать этот интерфейс, чтобы проверить, например, доступен ли почтовый сервер, который нам нужен в нашем приложении. Любые объекты, которые реализуют этот интерфейс и зарегистрированы в реестре Guice, обрабатываются Ratpack. Ratpack также предлагает HealthCheckHandlerдля вывода результатов всех проверок работоспособности или одной проверки работоспособности, идентифицированной по имени. Вместо создания нового класса, который реализует HealthCheckинтерфейс, мы также можем использовать HealtCheck.ofметод. Этот метод принимает аргумент с именем нашей проверки и Closureлямбда-выражением с кодом, который выполняет проверку.

Давайте напишем пример приложения Ratpack с использованием Groovy DSL. Сначала мы используем HealthCheck.ofдля простой проверки работоспособности. Мы также используем, HealthCheckHandlerчтобы мы могли запросить информацию о проверке здоровья.

// File: src/ratpack/Ratpack.groovy
import ratpack.exec.Promise
import ratpack.health.HealthCheck
import ratpack.health.HealthCheckHandler
import ratpack.registry.Registry

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        // Add a simple HealtCheck implementation with 
        // the of method. The name of our health check is
        // "application". We simply return a Promise with
        // a HealthCheck.Result value. If we get here we assume
        // the application is UP and running.
        add HealthCheck.of('application') { Registry registry ->
            Promise.value(HealthCheck.Result.healthy("UP"))
        }
    }

    handlers {
        // Assign HealthCheckHandler to health/ endpoint.
        // Optionally we can provide the name of the health check
        // to get the results specific for that health check.
        get('health/:name?', new HealthCheckHandler()) 
    }
}

При запуске приложения и запросить URL http://localhost:5050/healthи http://localhost:5050/health/applicationмы получаем следующий результат:

$ http localhost:5050/health
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate
Expires: 0
Pragma: no-cache
connection: keep-alive
content-encoding: gzip
content-type: text/plain;charset=UTF-8
transfer-encoding: chunked

application : HEALTHY

$ http localhost:5050/health/application
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate
Expires: 0
Pragma: no-cache
connection: keep-alive
content-encoding: gzip
content-type: text/plain;charset=UTF-8
transfer-encoding: chunked

application : HEALTHY

Теперь мы создаем новый класс, который реализует HealthCheckинтерфейс. Мы пишем реализацию проверки работоспособности, которая проверяет, меньше ли свободного места на диске, чем заданное пороговое значение. Если места меньше, проверка работоспособности должна вернуть нездоровый статус:

// File: src/main/groovy/com/mrhaki/ratpack/healthcheck/DiskSpaceHealthCheck.groovy
package com.mrhaki.ratpack.healthcheck

import groovy.transform.CompileStatic
import ratpack.exec.Blocking
import ratpack.exec.Promise
import ratpack.health.HealthCheck
import ratpack.registry.Registry

import static org.apache.commons.io.FileUtils.byteCountToDisplaySize

/**
 * Ratpack {@link HealthCheck} implementation to check for free space 
 * on a disk. If the available free space is less than a given
 * threshold value than the check return unhealthy.
 */
@CompileStatic
class DiskSpaceHealthCheck implements HealthCheck {

    /**
     *  Default disk to check for free space is the local
     * disk our Ratpack app is running. 
     */
    File path = new File(".")

    /**
     *  Default threshold is 50 MB. 
     */
    long threshold = 50L * 1024 * 1024

    /**
     * Name for health check. Name must be unique
     * in Ratpack application. To add more instances
     * of this class use different names.
     */
    String name = 'diskSpace'

    /**
     * Check available free space for the given {@link #path} value. Compare 
     * this with the configured {@link #threshold} value. If available free
     * space is less than threshold return unhealthy result.
     * Otherwise return a healthy status.
     * 
     * @param registry Ratpack registry.
     * @return Unhealthy result when available free space less than threshold, otherwise healthy.
     * @throws Exception Something goes wrong.
     */
    @Override
    Promise<HealthCheck.Result> check(final Registry registry) throws Exception {
        Blocking.get {

            // Get available free space. Operation is potentially blocking
            // so inside a Blocking.get{} block.
            path.freeSpace

        }.map { Long diskFreeSpace ->

            // Format bytes to readable format with KB, MB, GB, etc.
            final String diskFreeSpaceFormatted = byteCountToDisplaySize(diskFreeSpace)
            final String thresholdFormatted = byteCountToDisplaySize(threshold)

            // Check if available free space is above given threshold.
            if (diskFreeSpace >= threshold) {
                // Everything is ok.
                HealthCheck.Result.healthy(
                        'Available: %s (threshold: %s)',
                        diskFreeSpaceFormatted,
                        thresholdFormatted)
            } else {
                // Available free space is below given threshold.
                // Create message with information
                // and signal as unhealthy.
                HealthCheck.Result.unhealthy(
                        'Free disk space below threshold. Available: %s (threshold: %s)',
                        diskFreeSpaceFormatted,
                        thresholdFormatted)
            }
        }
    }
}

Нам нужно только добавить этот класс в наш реестр, и Ratpack подберет его:

// File: src/ratpack/Ratpack.groovy
import com.mrhaki.ratpack.healthcheck.DiskSpaceHealthCheck
import ratpack.exec.Promise
import ratpack.health.HealthCheck
import ratpack.health.HealthCheckHandler
import ratpack.registry.Registry

import static ratpack.groovy.Groovy.ratpack

ratpack {
    bindings {
        add new DiskSpaceHealthCheck(threshold: 50L * 1024 * 1024 * 1024 /* 50GB */)
        add HealthCheck.of('application') { Registry registry ->
            Promise.value(HealthCheck.Result.healthy("UP"))
        }
    }

    handlers {
        get('health/:name?', new HealthCheckHandler()) 
    }
}

Запускаем приложение и проверяем результаты healthконечной точки:

$ http localhost:5050/health
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate
Expires: 0
Pragma: no-cache
connection: keep-alive
content-encoding: gzip
content-type: text/plain;charset=UTF-8
transfer-encoding: chunked

application : HEALTHY
diskSpace : UNHEALTHY [Free disk space below threshold. Available: 41 GB (threshold: 50 GB)]

$ http localhost:5050/health/diskSpace
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store, must-revalidate
Expires: 0
Pragma: no-cache
connection: keep-alive
content-encoding: gzip
content-type: text/plain;charset=UTF-8
transfer-encoding: chunked

diskSpace : UNHEALTHY [Free disk space below threshold. Available: 41 GB (threshold: 50 GB)]

Написано с помощью Ratpack 1.1.1.