Анализ принципа реализации компонента регистрации и обнаружения сервисов Eureka client

задняя часть Микросервисы

существуетпредыдущая статьяПредставлено, как использовать компоненты обнаружения регистрации службы: Eureka, и приведены примеры использования. Исходя из этого, в этой статье будет объяснена внутренняя история реализации клиента Eureka в сочетании с подробностями углубленной реализации исходного кода, чтобы понять, почему. Клиенты должны сосредоточиться на следующем:

  • Получить информацию о реестре с сервера Eureka
  • Полная информация о вытягиваемом реестре
  • Инкрементное извлечение информации реестра
  • Таймер обновления кэша реестра и таймер продления аренды (пульс)
  • Регистрация службы и регистрация службы по запросу
  • Автономный режим экземпляра службы

Эта статья взята из книги, опубликованной автором«Продвинутая архитектура микросервисов Spring Cloud»

Структура клиента Эврика

В версии SpringCloud для Finchley вы можете зарегистрироваться как клиент Eureka без добавления каких-либо дополнительных аннотаций, просто добавьте в файл pomspring-cloud-starter-netflix-eureka-clientзависимость.

Чтобы отслеживать работающий механизм Eureka, читатели могут включить режим отладки SpringBoot, чтобы просмотреть больше выходных журналов:

logging:
  level:
    org.springframework: DEBUG

Проверитьspring-cloud-netflix-eureka-clientизsrc/main/resource.META-INF/spring.factories, чтобы увидеть, какие классы автоматической настройки есть у Eureka Client:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration

За исключением классов автоматической настройки, связанных с центром настройки, можно найти три класса автоматической настройки, тесно связанные с Eureka Client:

  • EurekaClientAutoConfiguration
  • RibbonEurekaAutoConfiguration
  • EurekaDiscoveryClientConfiguration

Далее мы проанализируем эти классы, чтобы увидеть, какую начальную настройку должен выполнить обычный клиент Eureka.

EurekaClientAutoConfiguration

Класс автоматической настройки Eureka Client отвечает за настройку и инициализацию ключевых бинов в EurekaClient.Ниже приводится введение и функции наиболее важных бинов в нем.

EurekaClientConfig

Предоставляет информацию о конфигурации, необходимую клиенту Eureka для регистрации на сервере Eureka, а SpringCloud предоставляет для него класс конфигурации по умолчанию.EurekaClientConfigBean, может иметь префикс в файле конфигурацииeureka.client+ имя свойства для переопределения.

ApplicationInfoManager

Этот класс управляет информационным классом экземпляра службы.InstanceInfo, который включает информацию, требуемую реестром на сервере Eureka, представляющую данные, отправленные каждым клиентом Eureka в реестр для обнаружения службы. Также управляет информацией о конфигурации экземпляраEurekaInstanceConfig, SpringCloud предоставляетEurekaInstanceConfigBeanКласс конфигурации для конфигурации по умолчанию также можно настроить в файле конфигурации.application.ymlпрошедшийeureka.instance+ Имя свойства для пользовательской конфигурации.

EurekaInstanceConfigBean

наследоватьEurekaInstanceConfigИнтерфейс — это соответствующая информация о самом экземпляре службы, которую клиент Eureka должен предоставить для регистрации на сервере. Он в основном используется для обнаружения службы:

Обычно эта информация находится в конфигурационном файлеeureka.instanceПод префиксом SpringCloud прошелEurekaInstanceConfigBeanКласс конфигурации предоставляет соответствующую конфигурацию по умолчанию. Ниже приведены некоторые из дополнительных ключевых атрибутов, эта информация будет зарегистрирована в реестре.


public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig, EnvironmentAware {

// 服务实例的应用名
private String appname; 
// 服务实例的Id,通常和appname共同唯一标记一个服务实例
private String instanceId; 
// 自定义添加的元数据,由用户使用以适配扩展业务需求
private Map<String, String> metadataMap;
// 如果服务实例部署在AWS上,该类将持有服务实例部署所在的数据中心的准确信息
private DataCenterInfo dataCenterInfo;
// 服务实例的Ip地址
private String ipAddress;
// 服务实例主页地址
private String homePageUrl;
// 服务实例健康检查地址
private String healthCheckUrlPath;
// 服务实例的状态地址
private String statusPageUrlPath

.....
}

DiscoveryClient

Это интерфейс верхнего уровня, определенный SpringCloud для обнаружения сервисов. В Netflix Eureka или консуле есть соответствующие конкретные классы реализации. Предоставляются следующие методы:


public interface DiscoveryClient {

   // 获取实现类的描述
	String description();

	// 通过服务Id获取服务实例的信息
	List<ServiceInstance> getInstances(String serviceId);

	// 获取所有的服务实例的Id
	List<String> getServices();

}

Соответствующая схема структуры класса его реализации в Eureka:

EurekaDiscoveryClientнаследоватьDiscoveryClient, но глядя наEurekaDiscoveryClientв коде вы обнаружите, что это делается путем объединения классаEurekaClientРеализовать функции интерфейса следующим образомgetInstanceинтерфейс:

@Override
public List<ServiceInstance> getInstances(String serviceId) {
	List<InstanceInfo> infos = this.eurekaClient.getInstancesByVipAddress(serviceId,false);
	List<ServiceInstance> instances = new ArrayList<>();
	for (InstanceInfo info : infos) {
		instances.add(new EurekaServiceInstance(info));
	}
	return instances;
}

EurekaClientОтcom.netflix.discoveryпакет, его реализация по умолчаниюcom.netflix.discovery.DiscoveryClient, который относится к исходному коду eureka-client, который обеспечивает множество ключевых функций, таких как регистрация Eureka Client на сервере, продление аренды, переход в автономный режим и получение информации реестра на сервере. SpringCloud вызывает метод обнаружения сервисов в Eureka комбинированно, примерноEurekaClientПодробный анализ кода будет представлен в основном коде клиента. подходитьspring-cloud, пружина обеспечиваетCloudEurekaClientнаследоватьcom.netflix.discovery.DiscoveryClient, прикрываяonCacheRefreshedпредотвратить вspring-bootИнтерфейс вызывается, когда он не был инициализированNullPointException.

Отношения между вышеуказанными классами конфигурации очень тесные, и между данными существует определенная связь, поэтому отношения между ними представлены ниже.

прежде всегоEurekaInstanceConfig, код находится наEurekaClientAutoConfiguration

@Bean
@ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils, ManagementMetadataProvider managementMetadataProvider) {
	// 从配置文件中读取属性
	String hostname = getProperty("eureka.instance.hostname");
	boolean preferIpAddress = Boolean.parseBoolean(getProperty("eureka.instance.prefer-ip-address"));
	String ipAddress = getProperty("eureka.instance.ipAddress");
	boolean isSecurePortEnabled = Boolean.parseBoolean(getProperty("eureka.instance.secure-port-enabled"));

	String serverContextPath = env.getProperty("server.context-path", "/");
	int serverPort = Integer.valueOf(env.getProperty("server.port", env.getProperty("port", "8080")));

	Integer managementPort = env.getProperty("management.server.port", Integer.class);// nullable. should be wrapped into optional
	String managementContextPath = env.getProperty("management.server.context-path");// nullable. should be wrapped into optional
	Integer jmxPort = env.getProperty("com.sun.management.jmxremote.port", Integer.class);//nullable
	EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
   // 设置非空属性
	instance.setNonSecurePort(serverPort);
	instance.setInstanceId(getDefaultInstanceId(env));
	instance.setPreferIpAddress(preferIpAddress);
	if (StringUtils.hasText(ipAddress)) {
		instance.setIpAddress(ipAddress);
	}

	if(isSecurePortEnabled) {
		instance.setSecurePort(serverPort);
	}

	if (StringUtils.hasText(hostname)) {
		instance.setHostname(hostname);
	}
	String statusPageUrlPath = getProperty("eureka.instance.status-page-url-path");
	String healthCheckUrlPath = getProperty("eureka.instance.health-check-url-path");

	if (StringUtils.hasText(statusPageUrlPath)) {
		instance.setStatusPageUrlPath(statusPageUrlPath);
	}
	if (StringUtils.hasText(healthCheckUrlPath)) {
		instance.setHealthCheckUrlPath(healthCheckUrlPath);
	}

	ManagementMetadata metadata = managementMetadataProvider.get(instance, serverPort, serverContextPath, managementContextPath, managementPort);

   .....
	return instance;
}

Как видно из приведенного выше кода,EurekaInstanceConfigСвойства в основном черезEurekaInstanceConfigBeanРеализация обеспечивает, а также пытается прочитать часть конфигурации из файла конфигурации, например.eureka.instance.hostname,eureka.instance.status-page-url-path,eureka.instance.health-check-url-pathПодождите, он представляет информацию, которая должна быть у экземпляра приложения, а затем эта часть информации будет инкапсулирована какInstanceInfo, зарегистрирован на Eureka Server.

InstanceInfoчерезInstanceInfoFactory(org.springframework.cloud.netflix.eureka) пакетEurekaInstanceConfigсоздан со свойствами в , гдеInstanceInfoСвойства в основномvolatile, что обеспечивает непротиворечивость и атомарность этого типа информации в памяти.

Код находится наInstanceInfoFactory.

public InstanceInfo create(EurekaInstanceConfig config) {
		LeaseInfo.Builder leaseInfoBuilder = LeaseInfo.Builder.newBuilder()
				.setRenewalIntervalInSecs(config.getLeaseRenewalIntervalInSeconds())
				.setDurationInSecs(config.getLeaseExpirationDurationInSeconds());
		// 创建服务实例的信息用来注册到eureka server上
		InstanceInfo.Builder builder = InstanceInfo.Builder.newBuilder();

		String namespace = config.getNamespace();
		if (!namespace.endsWith(".")) {
			namespace = namespace + ".";
		}
		builder.setNamespace(namespace).setAppName(config.getAppname())
				.setInstanceId(config.getInstanceId())
				.setAppGroupName(config.getAppGroupName())
				.setDataCenterInfo(config.getDataCenterInfo())
				.setIPAddr(config.getIpAddress()).setHostName(config.getHostName(false))
				.setPort(config.getNonSecurePort())
				.enablePort(InstanceInfo.PortType.UNSECURE,
						config.isNonSecurePortEnabled())
				.setSecurePort(config.getSecurePort())
				.enablePort(InstanceInfo.PortType.SECURE, config.getSecurePortEnabled())
				.setVIPAddress(config.getVirtualHostName())
				.setSecureVIPAddress(config.getSecureVirtualHostName())
				.setHomePageUrl(config.getHomePageUrlPath(), config.getHomePageUrl())
				.setStatusPageUrl(config.getStatusPageUrlPath(),
						config.getStatusPageUrl())
				.setHealthCheckUrls(config.getHealthCheckUrlPath(),
						config.getHealthCheckUrl(), config.getSecureHealthCheckUrl())
				.setASGName(config.getASGName());

       ....
		InstanceInfo instanceInfo = builder.build();
		instanceInfo.setLeaseInfo(leaseInfoBuilder.build());
		return instanceInfo;
	}

с последующимApplicationInfoManager, код находится наEurekaClientAutoConfiguration.

@Bean
@ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT)
public ApplicationInfoManager eurekaApplicationInfoManager(EurekaInstanceConfig config) {
	InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
	return new ApplicationInfoManager(config, instanceInfo);
		}

комбинируяEurekaInstanceConfigа такжеInstanceInfoсозданныйApplicationInfoManager, который принадлежит Application Information Manager.

@Bean
@ConditionalOnMissingBean(value = EurekaClientConfig.class, search = SearchStrategy.CURRENT)
public EurekaClientConfigBean eurekaClientConfigBean() {
	EurekaClientConfigBean client = new EurekaClientConfigBean();
	if ("bootstrap".equals(propertyResolver.getProperty("spring.config.name"))) {
		// We don't register during bootstrap by default, but there will be another
		// chance later.
		client.setRegisterWithEureka(false);
	}
	return client;
}

Как упоминалось ранее,EurekaClientConfigДержите клиент Eureka, чтобы взаимодействовать с Eureka Server, аналогичнымserviceUrl(Адрес сервера),EurekaClientпройти черезEurekaClientConfigИнформация о конфигурации и сервер Eureka для регистрации и обнаружения служб.

Ну наконец тоEurekaClient,пройти черезApplicationInfoManagerа такжеEurekaClientConfigСоздание комбинации, т.е.EurekaClientВ то же время информация об экземпляре службы Клиента хранится для обнаружения службы, а конфигурация, зарегистрированная на сервере EUREKA, используется для регистрации службы.

@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config){
		return new CloudEurekaClient(manager, config, this.optionalArgs, this.context);
}

Общая структура классов выглядит следующим образом

ЭврикаРегистрация, ЭврикаСервисРеестри, ЭврикаАвтоСервисРегистрация

Это связанный класс, добавленный Spring Cloud для адаптации Eureka для регистрации службы в реестре служб.Давайте сначала рассмотрим структуру связанного класса.

RegistrationнаследоватьServiceInstance, представляет экземпляр службы, зарегистрированный в системе обнаружения служб, и должен содержать такую ​​информацию, как имя хоста, порт и т. д.RegistrationдаServiceInstanceкласс фасада

public interface ServiceInstance {

	//获取服务实例的serviceId
	String getServiceId();

	//获取服务实例的hostname
	String getHost();

   //获取服务实例的端口号
	int getPort();

	boolean isSecure();

	//获取服务实例的uri地址
	URI getUri();

	//获取服务实例的元数据key-value对
	Map<String, String> getMetadata();
}

Соответствует Эврике,EurekaRegistrationДостигнутоRegistration, посмотри в нем код, только что скопировалEurekaInstanceConfigBeanинформация о конфигурации в , при введенииEurekaClient, который обеспечивает реализацию регистрации службы Eureka Client.

@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Он предоставляет соответствующий интерфейс для регистрации экземпляра службы в реестре службы:


public interface ServiceRegistry<R extends Registration> {

	//注册服务实例,registration当中通常有关于服务实例的信息,例如hostname和port
	void register(R registration);

   //注销服务实例
	void deregister(R registration);

	//关闭ServiceRegistry,通常在服务关闭的时候被调用
	void close();

	//设置服务实例的状态
	void setStatus(R registration, String status);

	//获取服务实例的状态
	<T> T getStatus(R registration);
}

из которых вEurekaServiceRegistryРеализация регистрации и офлайн выглядит следующим образом:

@Override
public void register(EurekaRegistration reg) {
   // 初始化EurekaRegistration中的EurekaClient,如果为null
	maybeInitializeClient(reg);

    // 修改服务的状态为UP
	reg.getApplicationInfoManager()
			.setInstanceStatus(reg.getInstanceConfig().getInitialStatus());
    // 设置健康检查
	reg.getHealthCheckHandler().ifAvailable(healthCheckHandler ->
			reg.getEurekaClient().registerHealthCheck(healthCheckHandler));
}
	
private void maybeInitializeClient(EurekaRegistration reg) {
	reg.getApplicationInfoManager().getInfo();
	reg.getEurekaClient().getApplications();
}
	
@Override
public void deregister(EurekaRegistration reg) {
	if (reg.getApplicationInfoManager().getInfo() != null) {
		// 设置服务的状态为DOWN	
		reg.getApplicationInfoManager().setInstanceStatus(InstanceInfo.InstanceStatus.DOWN);
	}
}

В приведенном выше коде видно, что регистрация и автономный режим службы изменяют только текущий статус службы.EurekaClientКласс реализации интерфейса имеет специальныеInstanceStatusМониторинг изменения состояния, при изменении информации об экземпляре службы будут инициироваться различные события для обработки.

EurekaAutoServiceRegistration, как видно из названия, это автоматическая регистрация экземпляров сервиса.Как видно из предыдущей диаграммы классов, этот класс наследуетSmartLifecycleинтерфейс, которыйorg.springframework.contextСоответствующие классы в пакете, описаниеEurekaAutoServiceRegistrationКлассы управляются жизненным циклом Spring.

...
@EventListener(ServletWebServerInitializedEvent.class)
public void onApplicationEvent(ServletWebServerInitializedEvent event) {
	int localPort = event.getWebServer().getPort();
	if (this.port.get() == 0) {
		log.info("Updating port to " + localPort);
		this.port.compareAndSet(0, localPort);
		start();
	}
}

@EventListener(ContextClosedEvent.class)
public void onApplicationEvent(ContextClosedEvent event) {
	if( event.getApplicationContext() == context ) {
		stop();
	}
}

В приведенном выше коде класс прослушиваетServletWebServerInitializedEventа такжеContextClosedEventДва события, вызываемые на этапе инициализации приложения.start()и вызов фазы закрытия контекста приложенияstop(), по сути, это автоматическая операция регистрации сервиса и офлайн при запуске и закрытии приложения соответственно.

EurekaAutoServiceRegistrationРегистрация службы и автономный режим напрямую называются методами в EurekaServiceRegistry.

@Override
public void start() {
	// only set the port if the nonSecurePort or securePort is 0 and this.port != 0
	if (this.port.get() != 0) {
		if (this.registration.getNonSecurePort() == 0) {
			this.registration.setNonSecurePort(this.port.get());
		}

		if (this.registration.getSecurePort() == 0 && this.registration.isSecure()) {
			this.registration.setSecurePort(this.port.get());
		}
	}

	if (!this.running.get() && this.registration.getNonSecurePort() > 0) {

       // 注册服务
		this.serviceRegistry.register(this.registration);

		this.context.publishEvent(
				new InstanceRegisteredEvent<>(this, this.registration.getInstanceConfig()));
		this.running.set(true);
	}
}
@Override
public void stop() {
   // 服务下线
	this.serviceRegistry.deregister(this.registration);
	this.running.set(false);
}

EurekaDiscoveryClientConfiguration

EurekaDiscoveryClientConfigurationПросто сделайте две вещи, мониторRefreshScopeRefreshedEventсобытия и инъекцииEurekaHealthCheckHandlerКласс реализации интерфейса.

RefreshScopeRefreshedEventСобытие обычно вызывается при обновлении компонента, управляемого пружиной, что означает, что конфигурация и параметры среды приложения могут измениться, поэтому необходимо перерегистрировать службу, чтобы предотвратить несоответствие информации об экземпляре службы в реестре местная информация.

@EventListener(RefreshScopeRefreshedEvent.class)
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
	// 保证了一个刷新事件发生后client的重新注册
	if(eurekaClient != null) {
		eurekaClient.getApplications();
	}
	if (autoRegistration != null) {
		// 重新注册防止本地信息与注册表中的信息不一致
		this.autoRegistration.stop();
		this.autoRegistration.start();
	}
}

RibbonEurekaAutoConfiguration

Конфигурационный класс для настройки балансировки нагрузки в Eureka Конкретное содержание Ribbon будет объяснено в других главах, которые мы здесь пропустим.

Основной код клиента

Пакетная структура

Основной код находится наeureka-client, модуль проектаeureka-client, версия v1.8.7, которая является версией eureka, от которой зависит версия Finchley Spring Cloud.

Структура пакета выглядит следующим образом

Краткое введение пакета:

  • com.netflix.appinfo: В основном о классе информации о конфигурации eureka-client, как упоминалось выше.EurekaInstanceConfig,InstanceInfo, который также содержит аналогичныеAmazonInfo,DataCenterInfoИнтерфейсы, тесно связанные с адаптацией архитектуры в AWS, здесь подробно описываться не будут, и заинтересованные читатели могут разобраться сами.
  • com.netflix.discovery: в основном реализует функции обнаружения и регистрации служб Eureka-Client.
    • com.netflix.discovery.converters: в основном решает вопросы кодирования и декодирования передачи данных между службами Eureka и поддерживает JSON, XML и другие форматы.
    • com.netflix.discovery.guice: GoogleизguiceПакет конфигурации внедрения зависимостей, аналогичныйSpringконфигурация.
    • com.netflix.discovery.provider: предоставленная реализация сериализации и десериализации запросов и ответов в Джерси, реализация по умолчаниюDefaultJerseyProvider.
    • com.netflix.discovery.providers: В настоящее время толькоDefaultEurekaClientConfigProvider,поставкаEurekaClientConfigЗаводской метод.
    • com.netflix.discovery.shared: Eureka Client совместно использует повторно используемые методы с Eureka Server.
      • com.netflix.discovery.shared.dns: преобразователь DNS.
      • com.netflix.discovery.shared.resolver: парсер конечной точки Euraka,EurekaEndpointОтносится к конечной точке службы, обычно относится к адресу доступа сервера Eureka, реализация по умолчанию:DefaultEndpoint,ClusterResolverРазрешите настроенный адрес сервера Eureka дляEurekaEndpoint, в котором используется шаблон проектирования делегатора.Схема классов выглядит следующим образом, и процесс обработки запроса делегирования очевиден.
      • com.netflix.discovery.shared.transport: клиент для HTTP-связи между клиентом Eureka и сервером Eureka, а также класс инкапсуляции для запроса и ответа на связь.

DiscoveryClient

DiscoveryClientМожно сказать, что основной класс клиента Eureka, отвечающий за ключевую логику взаимодействия с сервером Eureka и имеет следующие функции:

  • Зарегистрируйте экземпляр службы на Eureka Server;
  • Обновите договор с Eureka Server;
  • Отменить контракт с Eureka Server при отключении службы;
  • Запросите список сервисов/экземпляров, зарегистрированных в Eureka Server.

DiscoverClientДиаграмма основных классов выглядит следующим образом:

DiscoveryClientИнтерфейс верхнего уровняLookupService, основная цель — обнаружить активные экземпляры службы.

public interface LookupService<T> {

	//根据服务实例注册的appName来获取,获取一个封装有相同appName的服务实例信息的容器
   Application getApplication(String appName);
	//返回当前注册的所有的服务实例信息
   Applications getApplications();
   	//根据服务实例的id获取
   	List<InstanceInfo> getInstancesById(String id);
   //获取下一个可能的Eureka Server来处理当前对注册表信息的处理,一般是通过循环的方式来获取下一个Server
   InstanceInfo getNextServerFromEureka(String virtualHostname, boolean secure);
}

ApplicationСписок нескольких экземпляров, содержащих определенное приложение, можно понимать как информацию о кластере одной и той же службы, и все они висят под одним и тем же именем службы appName.InstanceInfoПредставляет экземпляр службы, часть кода выглядит следующим образом:

public class Application {
    
    private static Random shuffleRandom = new Random();

    //服务名
    private String name;

    @XStreamOmitField
    private volatile boolean isDirty = false;

    @XStreamImplicit
    private final Set<InstanceInfo> instances;

    private final AtomicReference<List<InstanceInfo>> shuffledInstances;

    private final Map<String, InstanceInfo> instancesMap;
    
    .....
}

Чтобы обеспечить атомарные операции и уникальность данных и предотвратить грязные данные,Applicationсредняя параInstanceInfoОперации все синхронные операции, чувствуюApplication.addInstanceметод.

public void addInstance(InstanceInfo i) {
	instancesMap.put(i.getId(), i);
	synchronized (instances) {
	instances.remove(i);
	instances.add(i);
	isDirty = true;
	}
}

Синхронизируя блок кода, гарантируется, что только один поток изменяет экземпляры за раз, и обратите внимание, что instancesMap используетConcurrentHashMapРеализация гарантирует атомарную работу, поэтому ее не нужно контролировать синхронизированными блоками кода.

ApplicationsПредставляет информацию о коллекции зарегистрированных экземпляров службы в Eureka Server, в основном дляApplicationБольшинство операций внутри также являются синхронными операциями.

EurekaClientнаследоватьLookupServiceинтерфейс, дляDiscoveryClientПредоставляет интерфейс верхнего уровня в попытке облегчить переход от eureka 1.x к eureka 2.x, что объясняетEurekaClientЭтот интерфейс является относительно стабильным интерфейсом и будет сохранен даже на следующем крупном этапе.

EurekaCientсуществуетLookupServiceБлагодаря расширению большего количества интерфейсов он предоставляет более богатые функции для получения экземпляров службы, в основном в том числе:

  • Предоставляет различные способы полученияInstanceInfo, например, по региону, адресу Eureka Server и т. д.;
  • Предоставляет данные для локальных клиентов (регион, зона доступности и т.д.), что тесно связано с AWS;
  • Обеспечить регистрацию клиентов и приобретение процессоров проверки здоровья;

Удалите интерфейсы, связанные с запросами, и сосредоточьтесь наEurekaClientСледующие два интерфейса в:

    // 为Eureka Client注册健康检查处理器
    // 一旦注册,客户端将通过调用新注册的健康检查处理器来对注册中instanceInfo
    // 进行一个按需更新,随后按照eurekaclientconfig.getinstanceinforeplicationintervalseconds()
    // 中配置的指定时间调用HealthCheckHandler
    public void registerHealthCheck(HealthCheckHandler healthCheckHandler);

    // 为eureka client注册一个EurekaEventListener(事件监听器)
    // 一旦注册,当eureka client的内部状态发生改变的时候,将会调用EurekaEventListener.onEvent()
    // 触发一定的事件。可以通过这种方式监听client的更新而非通过轮询的方式询问client
    public void registerEventListener(EurekaEventListener eventListener);
    

Eureka Server обычно идентифицирует состояние экземпляра с помощью тактов. В Eureka Client есть задание на время, которое регулярно проходитHealthCheckHandlerОпределить состояние текущего клиента.Если состояние клиента изменится, будет инициировано новое событие регистрации для синхронизации соответствующей информации об экземпляре службы в реестре Eureka Server.

public interface HealthCheckHandler {
    InstanceInfo.InstanceStatus getStatus(InstanceInfo.InstanceStatus currentStatus);
}

существуетspring-cloud-netflix-eureka-clientИнтерфейс, реализующий это,EurekaHealthCheckHandler, основная комбинацияspring-boot-actuatorсерединаHealthAggregatorа такжеHealthIndicatorправильно понялspring-bootОпределение состояния приложения.

В основном это следующие состояния:

public enum InstanceStatus {
	UP, // 可以接受服务请求
	DOWN, // 无法发送流量-健康检查失败
	STARTING, // 正在启动,无法发送流量
	OUT_OF_SERVICE, // 服务关闭,不接受流量
	UNKNOWN; // 未知状态
    }

Шаблон события в Eureka, это очевидный шаблон наблюдателя, ниже представлена ​​его диаграмма классов:

Регистрация и обнаружение клиентской службы

существуетDiscoveryClientВ коде есть специальные коды, реализующие функции регистрации и обнаружения сервисов. существуетDiscoveryClientВ конструкторе клиент Eureka будет выполнять такие операции, как извлечение информации из реестра с сервера Eureka и саморегистрация.DiscoveryClientКонструктор выглядит следующим образом:

DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, 
AbstractDiscoveryClientOptionalArgs args, Provider<BackupRegistry> backupRegistryProvider) 

ApplicationInfoManagerа такжеEurekaClientConfigКак мы видели в предыдущем введении, один — это класс, который инкапсулирует информацию о конфигурации текущего экземпляра службы, а другой — это класс, который инкапсулирует информацию о конфигурации для взаимодействия между клиентом и сервером.AbstractDiscoveryClientOptionalArgsа такжеBackupRegistryне вводится

BackupRegistryКод интерфейса выглядит следующим образом:

@ImplementedBy(NotImplementedRegistryImpl.class)
public interface BackupRegistry {

    Applications fetchRegistry();

    Applications fetchRegistry(String[] includeRemoteRegions);
}

Он действует как резервный реестр.Если клиент Eureka не может получить информацию о реестре с какого-либо сервера Eureka,BackupRegistryбудет вызываться для получения информации о реестре, но реализация по умолчаниюNotImplementedRegistryImpl, то есть не реализовано.

public abstract class AbstractDiscoveryClientOptionalArgs<T> {
	// 生成健康检查回调的工厂类,HealthCheckCallback已废弃
   	Provider<HealthCheckCallback> healthCheckCallbackProvider;
   // 生成健康处理器的工厂类
   Provider<HealthCheckHandler> healthCheckHandlerProvider;
   // 向Eureka Server注册之前的预处理器
   PreRegistrationHandler preRegistrationHandler;
   // Jersey过滤器集合,Jersey1和Jersey2均可使用
   Collection<T> additionalFilters;
   // Jersey客户端,主要用于client与server之间的HTTP交互
   EurekaJerseyClient eurekaJerseyClient;
   // 生成Jersey客户端的工厂
   TransportClientFactory transportClientFactory;
   // 生成Jersey客户端的工厂的工厂
   TransportClientFactories transportClientFactories;
   // Eureka事件的监听器
   private Set<EurekaEventListener> eventListeners;
....
}

AbstractDiscoveryClientOptionalArgsиспользуется для ввода некоторых необязательных параметров, а некоторыеjersey1а такжеjersey2универсальный фильтр,@Inject(optional = true)Атрибут указывает на необязательность параметра

В методе построения игнорируйте большинство операций присваивания и постепенно поймите, что свойства в классе конфигурации будут влиять наDiscoveryClientКакое влияние оказывает ваше поведение

if (config.shouldFetchRegistry()) {
	this.registryStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRY_PREFIX + "lastUpdateSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L});
} else {
	this.registryStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
}
if (config.shouldRegisterWithEureka()) {
	this.heartbeatStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRATION_PREFIX + "lastHeartbeatSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L});
} else {
	this.heartbeatStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
}

config.shouldFetchRegistry() (соответствующая конфигурацияeureka.client.fetch-register), если true, это означает, что Eureka Client будет получать информацию из реестра от Eureka Server, config.shouldRegisterWithEureka (соответствующая конфигурацияeureka.client.register-with-eureka), если это правда, клиент Eureka будет зарегистрирован на сервере Eureka.

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

Затем инициализируйте пул потоков таймера на основе пула потоков.ScheduledExecutorService, размер пула потоков равен 2, один используется для пульса, другой — для обновления кэша, а пулы потоков пульса и обновления кэша (ThreadPoolExecutor) инициализируются одновременно. оScheduledExecutorServiceа такжеThreadPoolExecutorОтношения между ними здесь не раскрываются.

scheduler = Executors.newScheduledThreadPool(2,
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-%d")
                            .setDaemon(true)
                            .build());

            heartbeatExecutor = new ThreadPoolExecutor(
                    1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>(),
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-HeartbeatExecutor-%d")
                            .setDaemon(true)
                            .build()
            );  // use direct handoff  
            cacheRefreshExecutor = new ThreadPoolExecutor(
                    1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>(),
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d")
                            .setDaemon(true)
                            .build()
            );  // use direct handoff

Затем инициализируйте клиент Jersy для HTTP-взаимодействия между Eureka Client и Eureka Server иAbstractDiscoveryClientOptionalArgsСвойства в используются для построенияEurekaTransport.

eurekaTransport = new EurekaTransport();
scheduleServerEndpointTask(eurekaTransport, args);

EurekaTransportдаDiscoveryClientВнутренний класс, в котором инкапсулируетсяDiscoveryClientКлиент из Джерси делает HTTP-вызовы на сервер Eureka:

private static final class EurekaTransport {
   // Server endPoint解析器
	private ClosableResolver bootstrapResolver;
	// Jersy客户端生成工厂
	private TransportClientFactory transportClientFactory;
	// 注册客户端
	private EurekaHttpClient registrationClient;
	// 注册客户端生成工厂
	private EurekaHttpClientFactory registrationClientFactory;
	// 发现服务客户端
	private EurekaHttpClient queryClient;
	// 发现服务客户端生成工厂
 	private EurekaHttpClientFactory queryClientFactory;
 	
 	....

}

Об АМСregionСоответствующая конфигурация в пропущена.

Другие клиенты будут представлены в следующей статье, так что следите за обновлениями.

Узнайте больше об этой книге:адрес

Рекомендуемое чтение

Коллекция микросервисов

Подписывайтесь на свежие статьи, приглашаю обратить внимание на мой публичный номер

微信公众号