SpringBoot + DingTalk == Alarma de monitoreo de aplicación Java

Antecedentes: en un sistema a gran escala, las excepciones en línea son un resultado inevitable, entonces, ¿cómo podemos encontrar problemas en el funcionamiento de la aplicación lo antes posible y tratarlos a tiempo para evitar expandir el alcance de la influencia?

La respuesta es monitorear la aplicación y alertar sobre mensajes de error.

Este artículo usa SpringBoot + DingTalk para lograr el monitoreo y la alarma de la aplicación, por supuesto, también puede usar WeChat empresarial, notificación por SMS, etc.

Plataforma de desarrollo DingTalk: documentación de acceso de robot personalizado

imagen.png

Después de mirar la documentación de DingTalk, podemos encontrar que la función de notificación de mensajes se puede realizar llamando a la dirección de Webhook para enviar el mensaje de alarma al chat grupal.

Pasos de implementación

Obtener la dirección de llamada

  • Primero cree un grupo DingTalk y cree un robot personalizado

Elija una de las configuraciones de seguridad para fortalecer la seguridad y evitar que la dirección de Webhook filtrada se envíe indiscriminadamente

imagen.png

  • Creado con éxito, copie la dirección del Webhook, deberá usarla más tarde

imagen.png

Crear una aplicación SpringBoot

  • Estructura del proyecto

imagen.png

servicio de advertencia

Definir la interfaz para reportar información de error

Los mensajes de error se agregarán a la cola MonitorMessageQueue

interface WarnService {
    fun reportErrorMsg(moduleName: String, msg: String)

}

@Service
class WarnServiceImpl : WarnService {
    @Autowired
    private lateinit var monitorMessageQueue: MonitorMessageQueue
    override fun reportErrorMsg(moduleName: String, msg: String) {
        monitorMessageQueue.add(MessageDto().apply {
            this.moduleName = moduleName
            this.content = msg
            this.timestamp = System.currentTimeMillis()
        })
    }
复制代码

MonitorMessageQueue cola

Métodos proporcionados por la cola

  • start: inicia el subproceso del demonio
  • drenaje: espere el tiempo de espera para volver al elemento de la cola, aquí establezca un tiempo de espera de 30 segundos para volver
  • add: agrega un elemento a la cola

@Component
@Scope("singleton")
class MonitorMessageQueue {
    private val queue: BlockingQueue<MessageDto> = LinkedBlockingQueue()
    private val logger = LoggerFactory.getLogger(MonitorMessageQueue::class.java)

    @Autowired
    private lateinit var sendService: DataSendService

    @PostConstruct
    private fun start() {
        logger.info("MonitorMessageQueue start")
        val thread = Thread(DataSendThread(this, sendService), "monitor_thread_0")
        thread.isDaemon = true
        thread.start()
    }

    //每个机器人每分钟最多发送20条消息到群里,如果超过20条,会限流10分钟。
    fun drain(): ArrayList<MessageDto> {
        val bulkData = ArrayList<MessageDto>()
        Queues.drain(queue, bulkData, Int.MAX_VALUE, 30, TimeUnit.SECONDS)
        return bulkData
    }

    fun add(message: MessageDto) {
        queue.add(message)
    }
}
复制代码

Hilo de alerta

Supervise la cola MonitorMessageQueue, agrupe y resuma los mensajes, y llame al servicio de envío para enviar mensajes


class DataSendThread(private val queue: MonitorMessageQueue, private val sendService: DataSendService) : Runnable {
    private val sendCount = AtomicLong(0)
    private val stop = false
    private val logger = LoggerFactory.getLogger(DataSendThread::class.java)
    override fun run() {
        while (!stop) {
            val list = queue.drain()
            if (list.isNullOrEmpty()) {
                logger.info("queue isEmpty")
                return
            }
            val format = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
            val mid = UUID.randomUUID().toString().replace("-", "")
            val stringBuilder = StringBuilder("[${format.format(System.currentTimeMillis())}][APP监控报警]")
            stringBuilder.append("\n");
            list.groupBy { it.moduleName }.map {
                stringBuilder.append("${it.key}(${it.value.size}次)")
                stringBuilder.append("\n")
                stringBuilder.append(it.value.firstOrNull()?.content ?: "")
                stringBuilder.append("\n")
            }
            stringBuilder.append("http://127.0.0.1/monitor/detail?mid=${mid}")
            sendService.send(stringBuilder.toString())
            logger.info("send success:${sendCount.addAndGet(1)}")
        }
    }
}
复制代码

Servicio de envío de datos

Procese la firma y llame al Webhook para enviar la información de la alarma al chat grupal de DingTalk


interface DataSendService {
    fun send(content: String)
}

@Service
class DataSendServiceImpl : DataSendService {
    @Autowired
    private lateinit var restTemplate: RestTemplate

    private fun url(timestamp: Long, sign: String): String {
        return "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxx&timestamp=${timestamp}&sign=$sign"

    }

    override fun send(content: String) {
        val timestamp = System.currentTimeMillis()
        println(
            restTemplate.postForObject(
                url(timestamp, calcSign(timestamp)), mapOf(
                    "msgtype" to "text",
                    "text" to mapOf(
                        "content" to content
                    )
                ),
                String::class.java
            )
        )
    }


    private fun calcSign(timestamp: Long): String {
        val secret = "xxxxxxx"

        val stringToSign = """
            $timestamp
            $secret
            """.trimIndent()
        val mac = Mac.getInstance("HmacSHA256")
        mac.init(SecretKeySpec(secret.toByteArray(charset("UTF-8")), "HmacSHA256"))
        val signData = mac.doFinal(stringToSign.toByteArray(charset("UTF-8")))
        return URLEncoder.encode(String(Base64.getEncoder().encode(signData)), "UTF-8")
    }
}
复制代码

ejecutar casos de prueba


@SpringBootTest
internal class WarnServiceImplTest {
    @Autowired
    private lateinit var warnService: WarnService

    @Test
    fun reportErrorMsg() {
        while (true) {
            for (i in 1..((1000 * Math.random()).toInt())) {
                warnService.reportErrorMsg("app-test1", "too many error")
            }
            for (i in (1..((1000 * Math.random()).toInt()))) {
                warnService.reportErrorMsg("app-test2", "too many error")
            }
            Thread.sleep(1000 * 30)
        }
    }
}
复制代码

imagen.png

Supongo que te gusta

Origin juejin.im/post/7074611371043717156
Recomendado
Clasificación