理解@EnableEurekaServer
@EnableXXX编程模型是Spring Framework中Enable模块驱动,其对应的元注解@Import,用于导入配置类(Configuration Class)。
/**
* Annotation to activate Eureka Server related configuration.
* {@link EurekaServerAutoConfiguration}
*
* @author Dave Syer
* @author Biju Kunjummen
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer {
}
上面的英文注释说@EnableEurekaServer注解用于激活Eureka Server的相关配置。
接下来我们探索一下Eureka Server的配置是如何被激活的。
EurekaServerMarkerConfiguration
我们重点关注@Import注解导入的EurekaServerMarkerConfiguration类。
/**
* Responsible for adding in a marker bean to activate
* {@link EurekaServerAutoConfiguration}.
*
* @author Biju Kunjummen
*/
@Configuration
public class EurekaServerMarkerConfiguration {
@Bean
public Marker eurekaServerMarkerBean() {
return new Marker();
}
class Marker {
}
}
这是一个Configuration Class,注册了一个Bean : org.springframework.cloud.netflix.eureka.server.EurekaServerMarkerConfiguration.Marker,再顺着这个类上的英文注释,我们找到EurekaServerAutoConfiguration类看一下。
EurekaServerAutoConfiguration
@Configuration
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class,
InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
...
}
可以看到,EurekaServerAutoConfiguration类上有一行标注:@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class),前面我们说到EurekaServerMarkerConfiguration类注册一个EurekaServerMarkerConfiguration.Marker类型的Bean ,使得这里的@ConditionalOnBean条件满足,从而激活了EurekaServerAutoConfiguration配置类。
在spring-cloud-netflix-eureka-server jar包下META-INF/spring.factories文件中,我们找到了如下的配置:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
在基于Spring Boot的应用中,我们知道应用启动时会扫描所有jar内部的
META-INF/spring.factories,当发现org.springframework.boot.autoconfigure.EnableAutoConfiguration=XXX的配置时,会将其作为自动配置类加载并处理。这就是Spring Boot自动配置的基本原理,详细原理可以参考@EnableAutoConfiguration注解去理解。
有了上面的基础后,不难得知,激活了EurekaServerAutoConfiguration类,就等于激活了Eureka Server的配置。
EurekaServerInitializerConfiguration
EurekaServerAutoConfiguration类上的注解:@Import(EurekaServerInitializerConfiguration.class) 导入了EurekaServerInitializerConfiguration配置类。
@Configuration
public class EurekaServerInitializerConfiguration
implements ServletContextAware, SmartLifecycle, Ordered
...
类实现了
ServletContextAware、SmartLifecycle和Ordered接口。
@Autowired
private EurekaServerConfig eurekaServerConfig;
@Autowired
private ApplicationContext applicationContext;
@Autowired
private EurekaServerBootstrap eurekaServerBootstrap;
private boolean running;
private int order = 1;
上面是
EurekaServerInitializerConfiguration类依赖注入的外部Bean 和自身的一些属性。
SmartLifecycle接口可以看做是对Spring应用上下文生命周期的补充,在常用的AbstractApplicationContext#refresh/close方法以外,还有AbstractApplicationContext#start方法。
上面这句话来自于偶像小马哥(Java劝退师,Dubbo PMC,Spring Cloud Alibaba架构师)在极客时间的《Spring编程思想核心篇》课程中讲过的一句话。:kissing_smiling_eyes:
public interface SmartLifecycle extends Lifecycle, Phased {
}
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
AbstractApplicationContext
@Override
public void start() {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
@Override
public void stop() {
getLifecycleProcessor().stop();
publishEvent(new ContextStoppedEvent(this));
}
简单概括一下就是:
AbstractApplicationContext#startLifecycle#start
EurekaServerInitializerConfiguration#start当所有单实例 Bean 都初始化完成后,就会回调所有实现了
Lifecycle接口的 Bean 的start方法!
@Override
public void start() {
// 先创建一个线程 异步的方式
new Thread(() -> {
try {
// TODO: is this class even needed now?
// 初始化、启动EurekaServer
eurekaServerBootstrap.contextInitialized(
EurekaServerInitializerConfiguration.this.servletContext);
log.info("Started Eureka Server");
// 发布Eureka已注册的事件
publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
// 修改EurekaServer的运行状态
EurekaServerInitializerConfiguration.this.running = true;
// 发布EurekaServer已启动的事件
publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
}
catch (Exception ex) {
// Help!
log.error("Could not initialize Eureka servlet context", ex);
}
}).start();
}
在EurekaServerAutoConfiguration中,我们注册了EurekaServer相关的一些Bean:
@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);
}
EurekaServerBootstrapBean通过依赖注入 注入给了EurekaServerInitializerConfiguration类。
我们先总结一下EurekaServerInitializerConfiguration#start方法的逻辑
- 初始化、启动EurekaServer
- 发布Eureka已注册的事件
- 修改EurekaServer的运行状态
- 发布EurekaServer已启动的事件
我们先看最重要的逻辑:初始化、启动EurekaServer
public class EurekaServerBootstrap {
...
public void contextInitialized(ServletContext context) {
try {
// 初始化Eureka的运行环境
initEurekaEnvironment();
// 初始化Eureka的运行上下文
initEurekaServerContext();
context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
}
catch (Throwable e) {
log.error("Cannot bootstrap eureka server :", e);
throw new RuntimeException("Cannot bootstrap eureka server :", e);
}
}
...
}
EurekaServer 本身应该是一个完整的 Servlet 应用,在原生的 EurekaServer 中,EurekaServer 的启动类
EurekaBootStrap类会实现ServletContextListener接口( Servlet3.0 规范)来引导启动 EurekaServer 。而基于 SpringBoot 构建的应用一般使用嵌入式 Web 容器,没有所谓 Servlet3.0 规范作用的机会了,所以需要另外的启动方式,于是 SpringCloud 在整合这部分时,借助ApplicationContext中支持的LifeCycle机制,以此来触发 EurekaServer 的启动。
我们的EurekaServer在Spring Boot应用中要完成初始化和启动操作,不能通过监听Servlet容器来完成(内嵌式Web容器不支持),我们只能先注入外部ServletContext对象,然后通过ApplicationContext去触发LifeCycle的生命周期回调,来启动EurekaServer。这个LifeCycle对Spring应用中的生命周期作了补充,同时也触发了EurekaServer的启动。我们在Spring Boot应用中,可以采取这种方式来完成我们想要做的事情。(这是一个可扩展的点,主动牵强启动)
- 初始化Eureka的运行环境
private static final String TEST = "test";
private static final String ARCHAIUS_DEPLOYMENT_ENVIRONMENT = "archaius.deployment.environment";
private static final String EUREKA_ENVIRONMENT = "eureka.environment";
private static final String DEFAULT = "default";
private static final String ARCHAIUS_DEPLOYMENT_DATACENTER = "archaius.deployment.datacenter";
private static final String EUREKA_DATACENTER = "eureka.datacenter";
protected void initEurekaEnvironment() throws Exception {
log.info("Setting the eureka configuration..");
// eureka.datacenter
// Eureka数据中心
String dataCenter = ConfigurationManager.getConfigInstance()
.getString(EUREKA_DATACENTER);
if (dataCenter == null) {
log.info("Eureka data center value eureka.datacenter is not set, defaulting to default");
ConfigurationManager.getConfigInstance()
.setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, DEFAULT);
}
else {
// archaius.deployment.datacenter
ConfigurationManager.getConfigInstance()
.setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, dataCenter);
}
// eureka.environment
// Eureka 环境
String environment = ConfigurationManager.getConfigInstance()
.getString(EUREKA_ENVIRONMENT);
if (environment == null) {
// test
ConfigurationManager.getConfigInstance()
.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST);
log.info(
"Eureka environment value eureka.environment is not set, defaulting to test");
}
else {
// archaius.deployment.environment
ConfigurationManager.getConfigInstance()
.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, environment);
}
}
- 初始化EurekaServer运行的上下文
protected void initEurekaServerContext() throws Exception {
// For backward compatibility 兼容低版本Eureka
JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
XStream.PRIORITY_VERY_HIGH);
XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
XStream.PRIORITY_VERY_HIGH);
if (isAws(this.applicationInfoManager.getInfo())) {
this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig,
this.eurekaClientConfig, this.registry, this.applicationInfoManager);
this.awsBinder.start();
}
// 1. 注册EurekaServerContextHolder,通过它可以很方便的获取EurekaServerContext
EurekaServerContextHolder.initialize(this.serverContext);
log.info("Initialized server context");
// Copy registry from neighboring eureka node
// 2. Eureka复制集群节点注册表
int registryCount = this.registry.syncUp();
this.registry.openForTraffic(this.applicationInfoManager, registryCount);
// Register all monitoring statistics.
EurekaMonitors.registerAllStats();
}
这里很简单,后面很方便就可以获取EurekaServerContext
public class EurekaServerContextHolder {
private final EurekaServerContext serverContext;
private EurekaServerContextHolder(EurekaServerContext serverContext) {
this.serverContext = serverContext;
}
public EurekaServerContext getServerContext() {
return this.serverContext;
}
private static EurekaServerContextHolder holder;
// 注册EurekaServerContextHolder
public static synchronized void initialize(EurekaServerContext serverContext) {
holder = new EurekaServerContextHolder(serverContext);
}
public static EurekaServerContextHolder getInstance() {
return holder;
}
}
这里我们没有说明Eureka复制集群节点注册表的逻辑,文章后面部分会详细说明
EurekaServerAutoConfiguration配置的核心组件
EurekaController
@Bean
@ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true)
public EurekaController eurekaController() {
return new EurekaController(this.applicationInfoManager);
}
private ApplicationInfoManager applicationInfoManager;
public EurekaController(ApplicationInfoManager applicationInfoManager) {
this.applicationInfoManager = applicationInfoManager;
}
这个
Controller是展示控制台页面的
PeerAwareInstanceRegistry
@Bean
public PeerAwareInstanceRegistry peerAwareInstanceRegistry(ServerCodecs serverCodecs) {
this.eurekaClient.getApplications(); // force initialization
return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,
serverCodecs, this.eurekaClient,
this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(),
this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
}
EurekaServer 集群中节点之间同步微服务实例注册表的核心组件
PeerEurekaNodes
@Bean
@ConditionalOnMissingBean
public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry,
ServerCodecs serverCodecs,
ReplicationClientAdditionalFilters replicationClientAdditionalFilters) {
return new RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig,
this.eurekaClientConfig, serverCodecs, this.applicationInfoManager,
replicationClientAdditionalFilters);
}
PeerEurekaNodes是微服务实例的节点集合。一个PeerEurekaNode就是一个微服务节点实例的包装,PeerEurekaNodes就是这组PeerEurekaNode的集合,这种节点是可以被 EurekaServer 集群中的各个注册中心节点共享的(PeerAwareInstanceRegistry)。
@Singleton
public class PeerEurekaNodes {
// 集群间节点同步的核心组件
protected final PeerAwareInstanceRegistry registry;
...
// 节点集合
private volatile List<PeerEurekaNode> peerEurekaNodes = Collections.emptyList();
// 所有节点所在 url
private volatile Set<String> peerEurekaNodeUrls = Collections.emptySet();
// 执行定时任务的线程池
private ScheduledExecutorService taskExecutor;
}
PeerEurekaNodes#start
public void start() {
// 创建一个定时任务线程池
taskExecutor = Executors.newSingleThreadScheduledExecutor(
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "Eureka-PeerNodesUpdater");
thread.setDaemon(true);
return thread;
}
}
);
try {
// 更新集群节点
updatePeerEurekaNodes(resolvePeerUrls());
Runnable peersUpdateTask = new Runnable() {
@Override
public void run() {
try {
updatePeerEurekaNodes(resolvePeerUrls());
} catch (Throwable e) {
logger.error("Cannot update the replica Nodes", e);
}
}
};
// 默认情况下每隔10分钟会同步一次集群节点
taskExecutor.scheduleWithFixedDelay(
peersUpdateTask,
serverConfig.getPeerEurekaNodesUpdateIntervalMs(),
serverConfig.getPeerEurekaNodesUpdateIntervalMs(),
TimeUnit.MILLISECONDS
);
} catch (Exception e) {
throw new IllegalStateException(e);
}
for (PeerEurekaNode node : peerEurekaNodes) {
logger.info("Replica node URL: {}", node.getServiceUrl());
}
}
PeerEurekaNodes#shutdown
public void shutdown() {
// 停止线程池
taskExecutor.shutdown();
//移除所有节点信息
List<PeerEurekaNode> toRemove = this.peerEurekaNodes;
this.peerEurekaNodes = Collections.emptyList();
this.peerEurekaNodeUrls = Collections.emptySet();
for (PeerEurekaNode node : toRemove) {
// Shuts down all resources used for peer replication.
node.shutDown();
}
}
EurekaServerContext
@Bean
public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs,
PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
// 本地服务器上下文,并将 getter 方法暴露给本地服务器的组件(例如注册表)
return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,
registry, peerEurekaNodes, this.applicationInfoManager);
}
@Singleton
public class DefaultEurekaServerContext implements EurekaServerContext {
@PostConstruct
@Override
public void initialize() {
logger.info("Initializing ...");
// 初始化PeerEurekaNodes,定时更新集群节点
peerEurekaNodes.start();
try {
// 初始化PeerAwareInstanceRegistry
registry.init(peerEurekaNodes);
} catch (Exception e) {
throw new RuntimeException(e);
}
logger.info("Initialized");
}
@PreDestroy
@Override
public void shutdown() {
logger.info("Shutting down ...");
registry.shutdown();
peerEurekaNodes.shutdown();
logger.info("Shut down");
}
}
PeerAwareInstanceRegistryImpl#init
初始化
PeerAwareInstanceRegistry
private final MeasuredRate numberOfReplicationsLastMin;
...
protected volatile PeerEurekaNodes peerEurekaNodes;
...
@Override
public void init(PeerEurekaNodes peerEurekaNodes) throws Exception {
// 启动续订租约的频率统计器
this.numberOfReplicationsLastMin.start();
this.peerEurekaNodes = peerEurekaNodes;
initializedResponseCache();
// 开启续订租约最低阈值检查的定时任务
scheduleRenewalThresholdUpdateTask();
// 初始化远程分区注册中心
initRemoteRegionRegistry();
try {
Monitors.registerObject(this);
} catch (Throwable e) {
logger.warn("Cannot register the JMX monitor for the InstanceRegistry :", e);
}
}
-
启动续订租约的频率统计器
private final long sampleInterval; private final Timer timer; private volatile boolean isActive; private final AtomicLong lastBucket = new AtomicLong(0); private final AtomicLong currentBucket = new AtomicLong(0); public synchronized void start() { // 隔一段时间重置 lastBucket 和 currentBucket 的值为 0 if (!isActive) { timer.schedule(new TimerTask() { @Override public void run() { try { // Zero out the current bucket. lastBucket.set(currentBucket.getAndSet(0)); } catch (Throwable e) { logger.error("Cannot reset the Measured Rate", e); } } }, sampleInterval, sampleInterval); isActive = true; } }这个计算次数会体现在 Eureka 的控制台,以及配合 Servo 完成续约次数监控
-
开启续订租约最低阈值检查的定时任务
private void scheduleRenewalThresholdUpdateTask() { timer.schedule(new TimerTask() { @Override public void run() { updateRenewalThreshold(); } }, serverConfig.getRenewalThresholdUpdateIntervalMs(), serverConfig.getRenewalThresholdUpdateIntervalMs()); }配置项中的默认时间间隔可以发现是 15 分钟
/** * Updates the <em>renewal threshold</em> based on the current number of * renewals. The threshold is a percentage as specified in * {@link EurekaServerConfig#getRenewalPercentThreshold()} of renewals * received per minute {@link #getNumOfRenewsInLastMin()}. */ private void updateRenewalThreshold() { try { Applications apps = eurekaClient.getApplications(); int count = 0; // 检查当前已经注册到本地的服务实例是否还保持连接 for (Application app : apps.getRegisteredApplications()) { for (InstanceInfo instance : app.getInstances()) { // always return true if (this.isRegisterable(instance)) { ++count; } } } // 会检查统计好的数量是否比预期的多,如果统计好的服务实例数比预期的数量多,证明 出现了新的服务注册,要替换下一次统计的期望数量值,以及重新计算接下来心跳的数量统计 synchronized (lock) { // Update threshold only if the threshold is greater than the // current expected threshold or if self preservation is disabled. // 0.85 * 客户端数量 if ((count) > (serverConfig.getRenewalPercentThreshold() * expectedNumberOfClientsSendingRenews) || (!this.isSelfPreservationModeEnabled())) { this.expectedNumberOfClientsSendingRenews = count; updateRenewsPerMinThreshold(); } } logger.info("Current renewal threshold is : {}", numberOfRenewsPerMinThreshold); } catch (Throwable e) { logger.error("Cannot update renewal threshold", e); } } private int expectedClientRenewalIntervalSeconds = 30; private double renewalPercentThreshold = 0.85; protected void updateRenewsPerMinThreshold() { this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfClientsSendingRenews * (60.0 / serverConfig.getExpectedClientRenewalIntervalSeconds()) * serverConfig.getRenewalPercentThreshold()); } // 1*(60/30)*0.85每隔 30 秒发一次心跳(一分钟心跳两次),而且必须所有的服务实例的心跳总数要达到前面计算数量的 85% 才算整体微服务正常,其实这也就是 EurekaServer 的自我保护机制。
-
初始化远程分区注册中心
protected void initRemoteRegionRegistry() throws MalformedURLException { Map<String, String> remoteRegionUrlsWithName = serverConfig.getRemoteRegionUrlsWithName(); if (!remoteRegionUrlsWithName.isEmpty()) { allKnownRemoteRegions = new String[remoteRegionUrlsWithName.size()]; int remoteRegionArrayIndex = 0; for (Map.Entry<String, String> remoteRegionUrlWithName : remoteRegionUrlsWithName.entrySet()) { RemoteRegionRegistry remoteRegionRegistry = new RemoteRegionRegistry( serverConfig, clientConfig, serverCodecs, remoteRegionUrlWithName.getKey(), new URL(remoteRegionUrlWithName.getValue())); regionNameVSRemoteRegistry.put(remoteRegionUrlWithName.getKey(), remoteRegionRegistry); allKnownRemoteRegions[remoteRegionArrayIndex++] = remoteRegionUrlWithName.getKey(); } } logger.info("Finished initializing remote region registries. All known remote regions: {}", (Object) allKnownRemoteRegions); }处理在其他区域中运行的 eureka 服务上需要完成的所有注册表操作。主要操作包括从远程区域中获取注册表信息以及定期获取增量信息。
EurekaServerBootstrap
@Bean
public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry,
EurekaServerContext serverContext) {
return new EurekaServerBootstrap(this.applicationInfoManager,
this.eurekaClientConfig, this.eurekaServerConfig, registry,
serverContext);
}
引导启动EurekaServer
ServletContainer
/**
* Register the Jersey filter.
* @param eurekaJerseyApp an {@link Application} for the filter to be registered
* @return a jersey {@link FilterRegistrationBean}
*/
@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(
// "eureka/"
Collections.singletonList(EurekaConstants.DEFAULT_PREFIX + "/*"));
return bean;
}
package com.sun.jersey.spi.container.servlet;
public class ServletContainer extends HttpServlet implements Filter {
}
这是jersy ,它是一个类似于 Spring Web MVC的框架,由于 Eureka 本身也是一个 Servlet 应用,只是它使用的 Web 层框架不是 SpringWebMvc 而是 Jersey 而已,Jersey 在 Eureka 的远程请求、心跳包发送等环节起到至关重要的作用,而这个
ServletContainer的作用,可以理解为 SpringWebMvc 中的DispatcherServlet,以及 Struts2 中的StrutsPrepareAndExecuteFilter。
这里面有一个 DEFAULT_PREFIX ,翻过去发现前缀是 /eureka ,这也解释了为什么微服务注册到 EurekaServer 的时候,defaultZone 要在 ip:port 后面加上 /eureka ,以及后面在调用 EurekaServer 的一些接口时前面也要加上 /eureka 。
javax.ws.rs.core.Application
@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;
}
HttpTraceFilter
@Bean
public FilterRegistrationBean traceFilterRegistration(
@Qualifier("httpTraceFilter") Filter filter) {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(filter);
bean.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return bean;
}
public class HttpTraceAutoConfiguration {
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
static class ServletTraceFilterConfiguration {
@Bean
@ConditionalOnMissingBean
public HttpTraceFilter httpTraceFilter(HttpTraceRepository repository, HttpExchangeTracer tracer) {
return new HttpTraceFilter(repository, tracer);
}
}
}
HttpTraceFilterBean :记录所有请求日志的Servlet过滤器。
EurekaServerConfigBeanConfiguration
@Configuration
protected static class EurekaServerConfigBeanConfiguration {
@Bean
@ConditionalOnMissingBean
public EurekaServerConfig eurekaServerConfig(EurekaClientConfig clientConfig) {
// 注册了默认的 EurekaServer 的配置模型 EurekaServerConfigBean
EurekaServerConfigBean server = new EurekaServerConfigBean();
if (clientConfig.shouldRegisterWithEureka()) {
// Set a sensible default if we are supposed to replicate
server.setRegistrySyncRetries(5);
}
return server;
}
}
总结
