@Override
	protected void doHealthCheck(Health.Builder builder) throws Exception {
		Map<String, Object> detailMap = new HashMap<>();

		// If sentinel isn't enabled, set the status up and set the enabled to false in
		// detail
		if (!sentinelProperties.isEnabled()) {
			detailMap.put("enabled", false);
			builder.up().withDetails(detailMap);
			return;
		}

		detailMap.put("enabled", true);

		// Check health of Dashboard
		boolean dashboardUp = true;
		List<Endpoint> consoleServerList = TransportConfig.getConsoleServerList();
		logger.info("Find sentinel dashboard server list: {}", consoleServerList);
		if (CollectionUtils.isEmpty(consoleServerList)) {
			// If Dashboard isn't configured, it's OK and mark the status of Dashboard
			// with UNKNOWN.
			detailMap.put("dashboard",
					new Status(
							Status.UNKNOWN.getCode(),
							"dashboard isn't configured"
					)
			);
		}
		else {
			// If Dashboard is configured, send a heartbeat message to it and check the
			HeartbeatSender heartbeatSender = HeartbeatSenderProvider.getHeartbeatSender();
			// result is true if the heartbeat message is sent successfully
			boolean result = heartbeatSender.sendHeartbeat();
			if (result) {
				detailMap.put("dashboard", Status.UP);
			}
			else {
				logger.warn("Sentinel dashboard heartbeat message can't be sent to the dashboard servers {} one of them can't be connected",
						consoleServerList);
				// If failed to send heartbeat message, means that the Dashboard is DOWN
				dashboardUp = false;
				detailMap.put("dashboard", new Status(
						Status.UNKNOWN.getCode(),
						String.format(
								"the dashboard servers [%s] one of them can't be connected",
								consoleServerList
						)
					)
				);
			}
		}

		// Check health of DataSource
		boolean dataSourceUp = true;
		Map<String, Object> dataSourceDetailMap = new HashMap<>();
		detailMap.put("dataSource", dataSourceDetailMap);

		// Get all DataSources and each call loadConfig to check if it's OK
		// If no Exception thrown, it's OK
		// Note:
		// Even if the dynamic config center is down, the loadConfig() might return
		// successfully
		// e.g. for Nacos client, it might retrieve from the local cache)
		// But in most circumstances it's okay
		Map<String, AbstractDataSource> dataSourceMap = beanFactory
				.getBeansOfType(AbstractDataSource.class);
		for (Map.Entry<String, AbstractDataSource> dataSourceMapEntry : dataSourceMap
				.entrySet()) {
			String dataSourceBeanName = dataSourceMapEntry.getKey();
			AbstractDataSource dataSource = dataSourceMapEntry.getValue();
			try {
				dataSource.loadConfig();
				dataSourceDetailMap.put(dataSourceBeanName, Status.UP);
			}
			catch (Exception e) {
				// If one DataSource failed to loadConfig, means that the DataSource is
				// DOWN
				dataSourceUp = false;
				dataSourceDetailMap.put(dataSourceBeanName,
						new Status(Status.UNKNOWN.getCode(), e.getMessage()));
			}
		}

		// If Dashboard and DataSource are both OK, the health status is UP
		if (dashboardUp && dataSourceUp) {
			builder.up().withDetails(detailMap);
		}
		else {
			builder.unknown().withDetails(detailMap);
		}
	}
A {@link HealthIndicator} for Sentinel, which checks the status of Sentinel Dashboard
and DataSource.

<p>
Check the status of Sentinel Dashboard by sending a heartbeat message to it. If return
true, it's OK.

Check the status of Sentinel DataSource by calling loadConfig method of
{@link AbstractDataSource}. If no Exception thrown, it's OK.

If Dashboard and DataSource are both OK, the health status is UP.
</p>

<p>
Note: If Sentinel isn't enabled, the health status is up. If Sentinel Dashboard isn't
configured, it's OK and mark the status of Dashboard with UNKNOWN. More informations
are provided in details.
</p>

@author cdfive
spring-cloud-alibaba-starters/spring-cloud-starter-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/endpoint/SentinelHealthIndicator.java