SpringCloud eureka服务端启动过程解析

本文详细解析了SpringCloud Eureka服务端的启动过程,从版本Finchley和SpringBoot 2.0.3开始,探讨了@EnableEurekaServer如何启动EurekaServerAutoConfiguration,并介绍了EurekaServerMarkerConfiguration的 Marker 类的作用。通过@Import导入EurekaServerInitializerConfiguration,进而调用EurekaServerBootstrap的contextInitialized方法来启动Eureka Server。文中还提到了EurekaServerConfigBean、EurekaController、ServerCodecs、PeerAwareInstanceRegistry等核心组件的功能和作用。
摘要由CSDN通过智能技术生成
版本

springCloud:Finchley
springboot:2.0.3
####标题
查看spring-cloud-netflix-eureka-server-2.0.0.jar下面的META-INF/spring-factories,查看项目启动时的自动配置类(参考SpringBoot自动配置分析文章

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration

所以Springboot项目启动时会自动初始化EurekaServerAutoConfiguration.java这个配置类:

@Configuration
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class,
		InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
	/**
	 * List of packages containing Jersey resources required by the Eureka server
 */
private static final String[] EUREKA_PACKAGES = new String[] { "com.netflix.discovery",
		"com.netflix.eureka" };

@Autowired
private ApplicationInfoManager applicationInfoManager;

@Autowired
private EurekaServerConfig eurekaServerConfig;

@Autowired
private EurekaClientConfig eurekaClientConfig;

@Autowired
private EurekaClient eurekaClient;

@Autowired
private InstanceRegistryProperties instanceRegistryProperties;

public static final CloudJacksonJson JACKSON_JSON = new CloudJacksonJson();

@Bean
public HasFeatures eurekaServerFeature() {
	return HasFeatures.namedFeature("Eureka Server",
			EurekaServerAutoConfiguration.class);
}

@Configuration
protected static class EurekaServerConfigBeanConfiguration {
	@Bean
	@ConditionalOnMissingBean
	public EurekaServerConfig eurekaServerConfig(EurekaClientConfig clientConfig) {
		EurekaServerConfigBean server = new EurekaServerConfigBean();
		if (clientConfig.shouldRegisterWithEureka()) {
			// Set a sensible default if we are supposed to replicate
			server.setRegistrySyncRetries(5);
		}
		return server;
	}
}

@Bean
@ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true)
public EurekaController eurekaController() {
	return new EurekaController(this.applicationInfoManager);
}

static {
	CodecWrappers.registerWrapper(JACKSON_JSON);
	EurekaJacksonCodec.setInstance(JACKSON_JSON.getCodec());
}

@Bean
public ServerCodecs serverCodecs() {
	return new CloudServerCodecs(this.eurekaServerConfig);
}

private static CodecWrapper getFullJson(EurekaServerConfig serverConfig) {
	CodecWrapper codec = CodecWrappers.getCodec(serverConfig.getJsonCodecName());
	return codec == null ? CodecWrappers.getCodec(JACKSON_JSON.codecName()) : codec;
}

private static CodecWrapper getFullXml(EurekaServerConfig serverConfig) {
	CodecWrapper codec = CodecWrappers.getCodec(serverConfig.getXmlCodecName());
	return codec == null ? CodecWrappers.getCodec(CodecWrappers.XStreamXml.class)
			: codec;
}

class CloudServerCodecs extends DefaultServerCodecs {

	public CloudServerCodecs(EurekaServerConfig serverConfig) {
		super(getFullJson(serverConfig),
				CodecWrappers.getCodec(CodecWrappers.JacksonJsonMini.class),
				getFullXml(serverConfig),
				CodecWrappers.getCodec(CodecWrappers.JacksonXmlMini.class));
	}
}

@Bean
public PeerAwareInstanceRegistry peerAwareInstanceRegistry(
		ServerCodecs serverCodecs) {
	this.eurekaClient.getApplications(); // force initialization
	return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,
			serverCodecs, this.eurekaClient,
			this.instanceRegistryProperties.getExpectedNumberOfRenewsPerMin(),
			this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
}

@Bean
@ConditionalOnMissingBean
public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry,
		ServerCodecs serverCodecs) {
	return new RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig,
			this.eurekaClientConfig, serverCodecs, this.applicationInfoManager);
}

/**
 * {@link PeerEurekaNodes} which updates peers when /refresh is invoked.
 * Peers are updated only if
 * <code>eureka.client.use-dns-for-fetching-service-urls</code> is
 * <code>false</code> and one of following properties have changed.
 * </p>
 * <ul>
 * <li><code>eureka.client.availability-zones</code></li>
 * <li><code>eureka.client.region</code></li>
 * <li><code>eureka.client.service-url.&lt;zone&gt;</code></li>
 * </ul>
 */
static class RefreshablePeerEurekaNodes extends PeerEurekaNodes
		implements ApplicationListener<EnvironmentChangeEvent> {

	public RefreshablePeerEurekaNodes(
			final PeerAwareInstanceRegistry registry,
			final EurekaServerConfig serverConfig,
			final EurekaClientConfig clientConfig, 
			final ServerCodecs serverCodecs,
			final ApplicationInfoManager applicationInfoManager) {
		super(registry, serverConfig, clientConfig, serverCodecs, applicationInfoManager);
	}

	@Override
	public void onApplicationEvent(final EnvironmentChangeEvent event) {
		if (shouldUpdate(event.getKeys())) {
			updatePeerEurekaNodes(resolvePeerUrls());
		}
	}
	
	/*
	 * Check whether specific properties have changed.
	 */
	protected boolean shouldUpdate(final Set<String> changedKeys) {
		assert changedKeys != null;
		
		// if eureka.client.use-dns-for-fetching-service-urls is true, then
		// service-url will not be fetched from environment.
		if (clientConfig.shouldUseDnsForFetchingServiceUrls()) {
			return false;
		}
		
		if (changedKeys.contains("eureka.client.region")) {
			return true;
		}
		
		for (final String key : changedKeys) {
			// property keys are not expected to be null.
			if (key.startsWith("eureka.client.service-url.") ||
				key.startsWith("eureka.client.availability-zones.")) {
				return true;
			}
		}
		
		return false;
	}
}

@Bean
public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs,
		PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
	return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,
			registry, peerEurekaNodes, this.applicationInfoManager);
}

@Bean
public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry,
		EurekaServerContext serverContext) {
	return new EurekaServerBootstrap(this.applicationInfoManager,
			this.eurekaClientConfig, this.eurekaServerConfig, registry,
			serverContext);
}

/**
 * Register the Jersey filter
 */
@Bean
public FilterRegistrationBean jerseyFilterRegistration(
		javax.ws.rs.core.Application eurekaJerseyApp) {
	FilterRegistrationBean bean = new FilterRegistrationBean();
	bean.setFilter(new ServletContainer(eurekaJerseyApp));
	bean.setOrder(Ordered.LOWEST_PRECEDENCE);
	bean.setUrlPatterns(
			Collections.singletonList(EurekaConstants.DEFAULT_PREFIX + "/*"));

	return bean;
}

/**
 * Construct a Jersey {@link javax.ws.rs.core.Application} with all the resources
 * required by the Eureka server.
 */
@Bean
public javax.ws.rs.core.Application jerseyApplication(Environment environment,
		ResourceLoader resourceLoader) {

	ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(
			false, environment);

	// Filter to include only classes that have a particular annotation.
	//
	provider.addIncludeFilter(new AnnotationTypeFilter(Path.class));
	provider.addIncludeFilter(new AnnotationTypeFilter(Provider.class));

	// Find classes in Eureka packages (or subpackages)
	//
	Set<Class<?>> classes = new HashSet<>();
	for (String basePackage : EUREKA_PACKAGES) {
		Set<BeanDefinition> beans = provider.findCandidateComponents(basePackage);
		for (BeanDefinition bd : beans) {
			Class<?> cls = ClassUtils.resolveClassName(bd.getBeanClassName(),
					resourceLoader.getClassLoader());
			classes.add(cls);
		}
	}

	// Construct the Jersey ResourceConfig
	//
	Map<String, Object> propsAndFeatures = new HashMap<>();
	propsAndFeatures.put(
			// Skip static content used by the webapp
			ServletContainer.PROPERTY_WEB_PAGE_CONTENT_REGEX,
			EurekaConstants.DEFAULT_PREFIX + "/(fonts|images|css|js)/.*");

	DefaultResourceConfig rc = new DefaultResourceConfig(classes);
	rc.setPropertiesAndFeatures(propsAndFeatures);

	return rc;
}

@Bean
public FilterRegistrationBean traceFilterRegistration(
		@Qualifier("httpTraceFilter") Filter filter) {
	FilterRegistrationBean bean = new FilterRegistrationBean();
	bean.setFilter(filter);
	bean.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
	return bean;
}
}
@EnableEurekaServer开启

我们可以看到这个类上面使用了
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class),表明只有当EurekaServerMarkerConfiguration.Marker.class这个bean存在时才会真正的加载这些配置。该bean是在哪里初始化的呢?我们记得在搭建eureka项目是需要在启动器上面加注解@EnableEurekaServer吗,现在看一下源码。
这是一个空的注解,引用EurekaServerMarkerConfiguration这个类

	@Target(ElementType.TYPE)
	@Retention(RetentionPolicy.RUNTIME)
	@Documented
	@Import(EurekaServerMarkerConfiguration.class)
	public @interface EnableEurekaServer {
	
	}

EurekaServerMarkerConfiguration.java中配置了一个bean,正是EurekaServerMarkerConfiguration.Marker.class

@Configuration
public class EurekaServerMarkerConfiguration {

	@Bean
	public Marker eurekaServerMarkerBean() {
		return new Marker();
	}

	class Marker {
	}
}
@Import(EurekaServerInitializerConfiguration.class

表明EurekaServerAutoConfiguration.java配置完成后会加载该配置,调用EurekaServerBootstrap的contextInitialized方法启动eureka server

我们继续看EurekaServerAutoConfiguration.java配置了如下几个bean.

EurekaServerConfigBean: 封装eureka.server为前缀的配置
EurekaController:一个控制器,就是我们通常访问的cloud-eureka后台的,查看注册中心信息,这是springcloud添加的不是 netflix-eureka原生的。
主要代码:
我们平时访问的htt://localhost:8761其实就是这个接口

  @RequestMapping(method = RequestMethod.GET)
	public String status(HttpServletRequest request, Map<String, Object> model) {
		populateBase(request, model);
		populateApps(model);
		StatusInfo statusInfo;
		try {
			statusInfo = new StatusResource().getStatusInfo();
		}
		catch (Exception e) {
			statusInfo = StatusInfo.Builder.newBuilder().isHealthy(false).build();
		}
		model.put("statusInfo", statusInfo);
		populateInstanceInfo(model, statusInfo);
		filterReplicas(model, statusInfo);
		return "eureka/status";
	}

@RequestMapping(value = "/lastn", method = RequestMethod.GET)
public String lastn(HttpServletRequest request, Map<String, Object> model) {
	populateBase(request, model);
	PeerAwareInstanceRegistryImpl registry = (PeerAwareInstanceRegistryImpl) getRegistry();
	ArrayList<Map<String, Object>> lastNCanceled = new ArrayList<>();
	List<Pair<Long, String>> list = registry.getLastNCanceledInstances();
	for (Pair<Long, String> entry : list) {
		lastNCanceled.add(registeredInstance(entry.second(), entry.first()));
	}
	model.put("lastNCanceled", lastNCanceled);
	list = registry.getLastNRegisteredInstances();
	ArrayList<Map<String, Object>> lastNRegistered = new ArrayList<>();
	for (Pair<Long, String> entry : list) {
		lastNRegistered.add(registeredInstance(entry.second(), entry.first()));
	}
	model.put("lastNRegistered", lastNRegistered);
	return "eureka/lastn";
}

ServerCodecs:配置数据传输的格式的帮助类,支持JSON和XML两种格式

class CloudServerCodecs extends DefaultServerCodecs {

	public CloudServerCodecs(EurekaServerConfig serverConfig) {
		super(getFullJson(serverConfig),
				CodecWrappers.getCodec(CodecWrappers.JacksonJsonMini.class),
				getFullXml(serverConfig),
				CodecWrappers.getCodec(CodecWrappers.JacksonXmlMini.class));
	}
}

PeerAwareInstanceRegistry: eureka-server集群节点中的信息同步接口是eureka原生,可能为了扩展,在这springcloud做了一层封装即org.springframework.cloud.netflix.eureka.server.InstanceRegistry,实际上还是交给PeerAwareInstanceRegistry的原生实现类com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl去做的,InstanceRegistry继承了PeerAwareInstanceRegistryImpl

public interface PeerAwareInstanceRegistry extends InstanceRegistry {

void init(PeerEurekaNodes peerEurekaNodes) throws Exception;

/**
 * 从其他节点同步注册信息,如果失败就从下一个节点同步
 */
int syncUp();

/**
 * Checks to see if the registry access is allowed or the server is in a
 * situation where it does not all getting registry information. The server
 * does not return registry information for a period specified in
 * {@link com.netflix.eureka.EurekaServerConfig#getWaitTimeInMsWhenSyncEmpty()}, if it cannot
 * get the registry information from the peer eureka nodes at start up.
 *
 * @return false - if the instances count from a replica transfer returned
 *         zero and if the wait time has not elapsed, otherwise returns true
 */
 boolean shouldAllowAccess(boolean remoteRegionRequired);
//注册接口,真正处理注册流程
 void register(InstanceInfo info, boolean isReplication);
//状态更新
 void statusUpdate(final String asgName, final ASGResource.ASGStatus newStatus, final boolean isReplication);

}

PeerEurekaNodes集群节点信息,管理节点(如添加、减少时)

EurekaServerContext:EurekaServerContext上下文
EurekaServerBootstrap:初始化eureka server的启动,EurekaServerInitializerConfiguration的start方法调用
Application:创建一个jersey应用。构建RESTful服务
扫描com.netflix.discovery,com.netflix.eureka包下所有被@Path和@Provider修饰的资源,封装成Jersey ResourceConfig

 private static final String[] EUREKA_PACKAGES = new String[] { "com.netflix.discovery",
		"com.netflix.eureka" };
public javax.ws.rs.core.Application jerseyApplication(Environment environment,
		ResourceLoader resourceLoader) {

	ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(
			false, environment);

	// Filter to include only classes that have a particular annotation.
	//
	provider.addIncludeFilter(new AnnotationTypeFilter(Path.class));
	provider.addIncludeFilter(new AnnotationTypeFilter(Provider.class));

	// Find classes in Eureka packages (or subpackages)
	//
	Set<Class<?>> classes = new HashSet<>();
	for (String basePackage : EUREKA_PACKAGES) {
		Set<BeanDefinition> beans = provider.findCandidateComponents(basePackage);
		for (BeanDefinition bd : beans) {
			Class<?> cls = ClassUtils.resolveClassName(bd.getBeanClassName(),
					resourceLoader.getClassLoader());
			classes.add(cls);
		}
	}

	// Construct the Jersey ResourceConfig
	//
	Map<String, Object> propsAndFeatures = new HashMap<>();
	propsAndFeatures.put(
			// Skip static content used by the webapp
			ServletContainer.PROPERTY_WEB_PAGE_CONTENT_REGEX,
			EurekaConstants.DEFAULT_PREFIX + "/(fonts|images|css|js)/.*");

	DefaultResourceConfig rc = new DefaultResourceConfig(classes);
	rc.setPropertiesAndFeatures(propsAndFeatures);

	return rc;
}

FilterRegistrationBean:注册过滤器,将 jersey application资源封装成ServletContainer(实现了Filter接口)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值