SpringCloud eureka服务端启动过程解析

版本

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
 * eureka.client.use-dns-for-fetching-service-urls is
 * false and one of following properties have changed.
 * 

*
    *
  • eureka.client.availability-zones
  • *
  • eureka.client.region
  • *
  • eureka.client.service-url.<zone>
  • *
*/ static class RefreshablePeerEurekaNodes extends PeerEurekaNodes implements ApplicationListener { 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 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> classes = new HashSet<>(); for (String basePackage : EUREKA_PACKAGES) { Set beans = provider.findCandidateComponents(basePackage); for (BeanDefinition bd : beans) { Class cls = ClassUtils.resolveClassName(bd.getBeanClassName(), resourceLoader.getClassLoader()); classes.add(cls); } } // Construct the Jersey ResourceConfig // Map 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 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 model) {
	populateBase(request, model);
	PeerAwareInstanceRegistryImpl registry = (PeerAwareInstanceRegistryImpl) getRegistry();
	ArrayList> lastNCanceled = new ArrayList<>();
	List> list = registry.getLastNCanceledInstances();
	for (Pair entry : list) {
		lastNCanceled.add(registeredInstance(entry.second(), entry.first()));
	}
	model.put("lastNCanceled", lastNCanceled);
	list = registry.getLastNRegisteredInstances();
	ArrayList> lastNRegistered = new ArrayList<>();
	for (Pair 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> classes = new HashSet<>();
	for (String basePackage : EUREKA_PACKAGES) {
		Set beans = provider.findCandidateComponents(basePackage);
		for (BeanDefinition bd : beans) {
			Class cls = ClassUtils.resolveClassName(bd.getBeanClassName(),
					resourceLoader.getClassLoader());
			classes.add(cls);
		}
	}

	// Construct the Jersey ResourceConfig
	//
	Map 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接口)

你可能感兴趣的:(微服务,eureka)