- SpringCloud-Spring Cloud Context
- Регистрация службы SpringCloud-Eureka
- Обнаружение службы SpringCloud-Eureka
- Анализ принципа клиента Spring Cloud-Eureka
- Имитация вызова декларативной службы SpringCloud
- Лента SpringCloud-Load Balancer
- Конфигурация центра конфигурации SpringCloud
- SpringCloud-Configuration Center Zookeeper
- Центр конфигурации SpringCloud Apollo
- Принцип центра конфигурации SpringCloud-Config
- SpringCloud — автоматический выключатель Hystrix
В некоторых из предыдущих демонстраций было показано, как использовать SOFABoot для интеграции компонентов Spring Cloud Netflix Eureka. В этой статье сначала будет проанализирован принцип работы Eureka Client.
Нетфликс и Спрингклауд
spring-cloud-commons 模块是 spring 在分布式领域上(服务发现,服务注册,断路器,负载均衡)的规范定义。 spring-cloud-netflix 是基于此规范的具体实现,Netflix OSS 里的各种组件也都实现了这个 commons 规范。 Отношения следующие:
Весенние облако Netflix Eureka Service Discovery Принцип реализации
Основываясь на приведенном выше рисунке, здесь мы берем обнаружение службы в Eureka в качестве примера, чтобы объяснить, как это реализовано. В Spring Cloud предусмотрено два ключевых класса для обнаружения служб: интерфейс DiscoveryClient и аннотация EnableDiscoveryClient.
Интерфейс DiscoveryClient
На следующей диаграмме показано, как SpringCloud интегрируется с Netflix для обнаружения сервисов. Интерфейс DiscoveryClient в Spring Cloud Common реализован в spring-cloud-netflix-eureka-client, а класс реализации — EurekaDiscoveryClient.
Определение интерфейса и методы DiscoveryClient:
/**
* DiscoveryClient表示服务发现常用的读取操作,例如Netflix Eureka或consul.io
* @author Spencer Gibb
*/
public interface DiscoveryClient {
/**
* 实现描述
* @return the description
*/
String description();
/**
* 获取与特定serviceId关联的所有ServiceInstances
* @param serviceId the serviceId to query
* @return a List of ServiceInstance
*/
List<ServiceInstance> getInstances(String serviceId);
/**
* 返回所有已知的服务ID
*/
List<String> getServices();
}
EurekaDiscoveryClient реализует эти методы, но сам EurekaDiscoveryClient реализует не логику взаимодействия с сервером, а через класс com.netflix.DiscoveryClient. Таким образом, spring-cloud-netflix-eureka-client реализует спецификацию Spring Cloud Common, а затем обертывает реализацию netflix.
Комментарий @EnableDiscoveryClient
````java @Цель(ТипЭлементов.ТИП) @Retention(RetentionPolicy.RUNTIME) @Документировано @Унаследовано @Import(EnableDiscoveryClientImportSelector.класс) public @interface EnableDiscoveryClient { // Следует ли автоматически регистрироваться, значение по умолчанию — true. логическое значение autoRegister() по умолчанию true; } ````
EnableDiscoveryClientImportSelector найдет класс с ключом org.springframework.cloud.client.discovery.EnableDiscoveryClient из META-INF/spring.factories.
Для авторегистрации:
- Если свойство автоматической регистрации имеет значение true, к найденным классам будет добавлен класс: AutoServiceRegistrationConfiguration, а AutoServiceRegistrationConfiguration будет использовать @EnableConfigurationProperties(AutoServiceRegistrationProperties.class) для внутреннего запуска создания bean-компонента AutoServiceRegistrationProperties. Подобно eureka и nacos, @ConditionalOnBean(AutoServiceRegistrationProperties.class) используется в их классах автоматической конфигурации, чтобы гарантировать, что AutoServiceRegistration будет создан для регистрации только при наличии bean-компонента AutoServiceRegistrationProperties.
- Если свойство автоматической регистрации имеет значение false, добавьте в среду PropertySource, внутренний элемент конфигурации — spring.cloud.service-registry.auto-registration.enabled, а значение — false (это означает, что AutoServiceRegistrationProperties.class не создан) . Таким образом, эврика не зарегистрируется.
Код, соответствующий приведенной выше логике, выглядит следующим образом:
spring-cloud-netflix-eureka-client сам предоставляет аннотацию EnableEurekaClient в качестве своей роли в этом комментарии
Схема архитектуры Эврика
- потребитель : потребитель службы, роль клиента eureka, может получать информацию о других зарегистрированных службах с сервера eureka, чтобы найти нужную услугу на основе этой информации, а затем инициировать удаленный вызов.
- поставщик : поставщик услуг, роль клиента eureka, может регистрировать и обновлять свою собственную информацию на сервере eureka.Конечно, как клиент eureka, он также может получать информацию о других службах с сервера.
- Сервер Eureka: реестр служб, предоставляющий функции регистрации и обнаружения служб;
- Синхронная репликация: информация о службе регистрации синхронизируется между серверами eureka, что гарантирует, что каждый сервер в кластере может предоставлять полную информацию о службе.
Чтобы ознакомиться с концепцией регистрации и зоны доступности на AWS, обратитесь к соответствующим материалам самостоятельно.
Анализ исходного кода
Чтение информации о конфигурации
Класс автоматической настройки Eureka Client — org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration, который в основном отвечает за инициализацию некоторых информационных служб конфигурации, таких как DiscoveryClient, EurekaServiceRegistry и других основных компонентов.
Также существует класс EurekaDiscoveryClientConfiguration, отвечающий за настройку автоматической регистрации и инициализацию средства проверки работоспособности приложения.
прочитать eureka.client.*
@Bean
@ConditionalOnMissingBean(value = EurekaClientConfig.class, search = SearchStrategy.CURRENT)
public EurekaClientConfigBean eurekaClientConfigBean(ConfigurableEnvironment env) {
EurekaClientConfigBean client = new EurekaClientConfigBean();
if ("bootstrap".equals(this.env.getProperty("spring.config.name"))) {
// 默认情况下,我们不会在引导过程中注册,但是以后会有另一个机会。
client.setRegisterWithEureka(false);
}
return client;
}
EurekaClientConfigBean инкапсулирует информацию о конфигурации, необходимую для взаимодействия между клиентом eureka и сервером eureka, такую как конфигурация eureka.client.service-url.defaultZone в предыдущем демонстрационном проекте.
прочитать eureka.instance.*
@Bean
@ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils,
ManagementMetadataProvider managementMetadataProvider) {
// 代码较长,此处省略
}
EurekaInstanceConfigBean инкапсулирует информацию о конфигурации собственного экземпляра клиента eureka и предоставляет основные метаданные для регистрации службы.
Инициализация основного компонента компонента
Здесь также создаются экземпляры некоторых bean-компонентов ядра.
ApplicationInfoManager
- EurekaClientConfiguration#eurekaApplicationInfoManager
@Bean
@ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT)
public ApplicationInfoManager eurekaApplicationInfoManager(
EurekaInstanceConfig config) {
InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
return new ApplicationInfoManager(config, instanceInfo);
}
- RefreshableEurekaClientConfiguration#eurekaApplicationInfoManager
@Bean
@ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT)
@org.springframework.cloud.context.config.annotation.RefreshScope
@Lazy
public ApplicationInfoManager eurekaApplicationInfoManager(EurekaInstanceConfig config) {
InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
return new ApplicationInfoManager(config, instanceInfo);
}
RefreshScope, отмеченный этой аннотацией, будет динамически обновляться. Включая информацию о свойствах и т. д., обратите внимание, что для динамического обновления класс, отмеченный RefreshScope, не может быть окончательным.
ApplicationInfoManager — это диспетчер информации о приложении, который используется для управления классом информации InstanceInfo экземпляров службы и классом информации конфигурации EurekaInstanceConfig экземпляров службы.
DiscoveryClient
@Bean
public DiscoveryClient discoveryClient(EurekaInstanceConfig config, EurekaClient client) {
return new EurekaDiscoveryClient(config, client);
}
DiscoveryClient Как упоминалось ранее, этот класс представляет собой клиентский интерфейс, используемый для обнаружения служб в Spring Cloud. Обратите внимание, что это интерфейс, предоставляемый SpringCloud, а не класс в netflix.
EurekaServiceRegistry
@Bean
public EurekaServiceRegistry eurekaServiceRegistry() {
return new EurekaServiceRegistry();
}
EurekaServiceRegistry — это класс реализации ServiceRegistry. ServiceRegistry заключается в том, что SpringCloud предоставляет такие методы, как регистрация и отмена регистрации, которые позволяют пользователям предоставлять настраиваемые услуги регистрации.
EurekaRegistration
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient, CloudEurekaInstanceConfig instanceConfig, ApplicationInfoManager applicationInfoManager, ObjectProvider<HealthCheckHandler> healthCheckHandler) {
return EurekaRegistration.builder(instanceConfig)
.with(applicationInfoManager)
.with(eurekaClient)
.with(healthCheckHandler)
.build();
}
Каждая реализация ServiceRegistry имеет собственную реализацию реестра.
- ZookeeperRegistration -> ZookeeperServiceRegistry
- ZookeeperRegistration -> EurekaServiceRegistry
- ConsulRegistration -> ConsulServiceRegistry
Если вам нужна специальная реализация ServiceRegistry , не предоставляйте также реализацию Registration.
обнаружение службы
Базовая ситуация обнаружения службы была упомянута выше, но, поскольку SpringCloud не предоставляет специальных интерактивных операций, com.netflix.discovery.DiscoveryClient выполняет конкретную работу. Итак, что касается обнаружения сервисов, мы будем непосредственно вращаться вокруг этого класса.
LookopService
public interface LookupService<T> {
// 根据服务实例注册的appName 来获取 Application
Application getApplication(String appName);
// 返回当前注册表中所有的服务实例信息
Applications getApplications();
// 根据服务实例Id获取服务实例信息
List<InstanceInfo> getInstancesById(String id);
/**
* 获取下一个可能的服务器,以处理来自从eureka接收到的注册表信息的请求。
* @virtualHostname 与服务器关联的虚拟主机名。
* @secure 指示是HTTP还是HTTPS请求
*
*/
InstanceInfo getNextServerFromEureka(String virtualHostname, boolean secure);
}
Роль интерфейса LookupService заключается в поиске активных экземпляров службы; всего предусмотрено четыре метода, которые хорошо понятны. См. комментарии для роли каждого метода.
EurekaClient
EurekaClient также является интерфейсом, который интегрирует и расширяет LookupService.
Этот интерфейс НЕ пытается очистить текущий клиентский интерфейс для eureka 1.x. для облегчения перехода от eureka 1.x к eureka 2.x. С этой точки зрения наличие EurekaClient должно обеспечить отказоустойчивость при обновлении Eureka1.x до Eureka 2.x.
Eurekaclient в Lookupservice продлен на основе многих способов, следующим образом:
public interface EurekaClient extends LookupService {
// 省去@Deprecated方法和获取服务实例信息的接口方法
// 注册健康检查处理器
public void registerHealthCheck(HealthCheckHandler healthCheckHandler);
// 监听client服务信息的更新
public void registerEventListener(EurekaEventListener eventListener);
// 取消监听
public boolean unregisterEventListener(EurekaEventListener eventListener);
// 获取当前健康检查处理器
public HealthCheckHandler getHealthCheckHandler();
// 关闭 eureka 客户端。还向eureka服务器发送撤销注册请求。
public void shutdown();
// EurekaClientConfig
public EurekaClientConfig getEurekaClientConfig();
// ApplicationInfoManager
public ApplicationInfoManager getApplicationInfoManager();
}
HealthCheckHandler Используется для проверки текущего состояния клиента, которое будет описано позже в механизме пульса.
DiscoveryClient
com.netflix.discovery.DiscoveryClient, этот класс выполнит ряд важных операций в конструкторе, таких как: извлечение информации из реестра, регистрация службы, инициализация механизма пульса, обновление кеша, регистрация запланированных задач по запросу и т. д.
DiscoveryClient(ApplicationInfoManager applicationInfoManager,
EurekaClientConfig config,
AbstractDiscoveryClientOptionalArgs args,
Provider<BackupRegistry> backupRegistryProvider) {
// ...
}
Определения некоторых параметров следующие:
- applicationInfoManager : Менеджер информации о приложениях
- config : информация о конфигурации для взаимодействия между клиентом и сервером.
- ARGS: тип фильтра, предоставленный клиентом (поддерживает Jersey1 и Jersey2), который используется для создания Eurekatransport.
- backupRegistryProvider : Резервное копирование реестра
обнаружение службы
Следующий фрагмент кода также находится в конструкторе DiscoveryClient, вот логика получения информации о службе регистрации:
if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) {
fetchRegistryFromBackup();
}
clientConfig.shouldFetchRegistry() Этот метод получает значение eureka.client.fetch-registry в файле конфигурации Значение по умолчанию — true, что означает, что информация реестра извлекается с сервера eureka.
fetchRegistry(boolean) — это метод получения регистрационной информации с сервера eureka. Параметр используется, чтобы указать, является ли обязательным получение полного объема регистрационной информации; если нет проблем с согласованием информации реестра сервера eureka и клиента, этот метод only Попробуйте выполнить полный сбор данных в первый раз, а затем пошаговый сбор данных.
fetchRegistryFromBackup() Резервная копия, используемая, если служба сервера eureka недоступна.
Реализация низкоуровневой связи EurekaTransport
EurekaTransport — это внутренний класс DiscoveryClient, EurekaTransport инкапсулирует конкретную базовую реализацию на основе трикотажа связи.
FetchRegistry
На картинке выше показан весь процесс получения регистрационной информации. Для условий на желтой наклейке, если одно из них выполнено, будет выполнено полное извлечение, в противном случае будет выполнено добавочное извлечение. Хэш-значение вычисляется для того, чтобы позже сравнить его с информацией о приложении на стороне сервера, чтобы понять, нужно ли снова выполнять операцию извлечения.регистрация службы
Логика регистрации сервиса также реализована в конструкторе DiscoveryClient.Фрагмент кода выглядит следующим образом:
if (clientConfig.shouldRegisterWithEureka() && clientConfig.shouldEnforceRegistrationAtInit()) {
try {
if (!register() ) {
throw new IllegalStateException("Registration error at startup. Invalid server response.");
}
} catch (Throwable th) {
logger.error("Registration error at startup: {}", th.getMessage());
throw new IllegalStateException(th);
}
}
Два условия, которые необходимо выполнить для регистрации на сервере: 1. Разрешить регистрацию на сервере 2. Принудительно регистрировать во время инициализации клиента.
boolean register() throws Throwable {
logger.info(PREFIX + "{}: registering service...", appPathIdentifier);
EurekaHttpResponse<Void> httpResponse;
try {
httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
} catch (Exception e) {
logger.warn(PREFIX + "{} - registration failed {}", appPathIdentifier, e.getMessage(), e);
throw e;
}
if (logger.isInfoEnabled()) {
logger.info(PREFIX + "{} - registration status: {}", appPathIdentifier, httpResponse.getStatusCode());
}
return httpResponse.getStatusCode() == 204;
}
Через объект eurekaTransport служба регистрируется на сервере eureka на основе вызова REST.
Механизм сердцебиения
Механизм биржи инициализации также выполняется в конструкторе обнаружения. В конце концов, существует запланированная конструктор метода инициализации задач, в этом методе, включая сердцебиение инициализации.
Пул потоков heartbeatExecutor:
heartbeatExecutor = new ThreadPoolExecutor(
1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setNameFormat("DiscoveryClient-HeartbeatExecutor-%d")
.setDaemon(true)
.build()
Цикл фиксации планировщика выполняется:
// Heartbeat timer
scheduler.schedule(
new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
),
renewalIntervalInSecs, TimeUnit.SECONDS);
TimedSupervisorTask — класс периодических задач, автоматически настраивающий интервал в eureka. HeartbeatThread — это особый поток, который выполняет любой поток, а обновление renew() выполняется в методе run.
boolean renew() {
EurekaHttpResponse<InstanceInfo> httpResponse;
try {
// 通过 eurekaTransport 来与 server 通信续期
httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
logger.debug(PREFIX + "{} - Heartbeat status: {}", appPathIdentifier, httpResponse.getStatusCode());
// 404 标识当前服务实例不存在
if (httpResponse.getStatusCode() == 404) {
// 记录心跳次数
REREGISTER_COUNTER.increment();
logger.info(PREFIX + "{} - Re-registering apps/{}", appPathIdentifier, instanceInfo.getAppName());
long timestamp = instanceInfo.setIsDirtyWithTime();
// 重新注册
boolean success = register();
if (success) {
instanceInfo.unsetIsDirty(timestamp);
}
return success;
}
// 200 状态正常
return httpResponse.getStatusCode() == 200;
} catch (Throwable e) {
logger.error(PREFIX + "{} - was unable to send heartbeat!", appPathIdentifier, e);
return false;
}
}
сервис в автономном режиме
Закройте клиент eureka, а также отправьте запрос на отмену регистрации на сервер eureka. Этот метод находится в методе завершения работы DiscoveryClient#.
@PreDestroy
@Override
public synchronized void shutdown() {
// 保证原子操作
if (isShutdown.compareAndSet(false, true)) {
logger.info("Shutting down DiscoveryClient ...");
if (statusChangeListener != null && applicationInfoManager != null) {
// 应用管理器取消状态监听
applicationInfoManager.unregisterStatusChangeListener(statusChangeListener.getId());
}
// 清理任务调度执行
cancelScheduledTasks();
// If APPINFO was registered
if (applicationInfoManager != null
&& clientConfig.shouldRegisterWithEureka()
&& clientConfig.shouldUnregisterOnShutdown()) {
//设置服务实例状态为 DOWN
applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN);
//注销注册
unregister();
}
// 关闭 jersey 客户端
if (eurekaTransport != null) {
eurekaTransport.shutdown();
}
heartbeatStalenessMonitor.shutdown();
registryStalenessMonitor.shutdown();
logger.info("Completed shut down of DiscoveryClient");
}
}