Springcloud-注册中心-Eureka源码分析

海康VisionMaster表面缺陷滤波实战:从参数调优到工业检测避坑指南 本文深入探讨海康VisionMaster表面缺陷滤波技术在工业检测中的应用,从参数调优到ROI绘制与Blob分析的协同策略,提供金属外壳、锂电池铝壳等复杂表面的缺陷检测实战指南。通过多级滤波架构和动态ROI生成法,显著提升检出率至98.7%,误检率低于0.5%,为工业质检提供智能化解决方案。 阅读详情

SpringCloud对于注册中心提供了很多的选择,如下所示:

  1. 满足CAP中的Eureka-通过集群保证高可用,集群中每一个节点都是对等的,数据的一致通过节点相互通信来保证,所以无法保证强一致性,本身提供了自我保护机制(如果eureka服务在15分钟之内有超过85%的client无法通信,那么可能eureka与client出现来网络问题,eureka服务开启自我保护机制,不会再剔除90s内没有和eureka续约的client了。这个机制在开发阶段需要关闭),极大保证了服务的高可用。只要集群中存在一个节点都可以对外提供服务。
  2. 满足CAP中的CP的zookeeper-基于本身的ZAB协议保证数据的强一致性,集群中由master统一处理写请求,集群中服务器的个数必须满足n/2+1,否则服务不可用
  3. 满足CAP中的基于CP的Consul-基于raft共识算法来保证数据的强一致性,与zk类似集群中由master统一写请求,集群中服务器的个数必须满足n/2+1,否则服务不可用
  4. 既可以满足CAP中CP也可以使用AP的阿里新一代注册中心Nacos-其中CP也是通过raft算法实现,AP是通过自研的Distro算法实现。

基础准备

首先我们基于的springcloud的最新版本-Hoxton来介绍,这里使用的是springboot2.2.5,关于springboot与springCloud的版本对应关系可以参考下表:

SpirngCloud VersionBoot Version

Hoxton

2.2.x

Greenwich

2.1.x

Finchley

2.0.x

Edgware

1.5.x

Dalston

1.5.x

1:Eureka

1 创建一个简单的Eureka注册中心

1.1 创建一个maven父工程

我们在通过maven搭建springcloud脚手架工程的第一件事情就是创建一个maven父工程, 如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.fc.springCloud</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <!-- 定义引用类库版本 -->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
        <java.version>1.8</java.version>
    </properties>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <dependencies>
        <!-- 引入公共jar -->

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- 应用监控-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- 应用测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <!--引用springcloud对应版本-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <modules>
        <module>eureka</module>
        <module>eurekaClient</module>
    </modules>
</project>

1.2 创建Eureka注册中心

在上述的父工程中创建一个子model,命名为eurekaServer,用以构建注册中心

pom文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>demo</artifactId>
        <groupId>com.fc.springCloud</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>eureka-server</artifactId>
    <packaging>jar</packaging>
    <description>注册中心</description>

    <dependencies>

        <!-- SpringCloud Eureka 注册中心依赖 在不同版本此jar包是不同的 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- actuator监控-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

    <build>
        <finalName>eureka-center</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

yml配置文件(这里也可以使用properties,个人喜欢使用yml,比较层次分明),可参考后续详细配置,自行删减。

上述为一个基础配置项,对于包含的其他项配置,后续通过源码分析介绍。

java启动类:


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * 
 * 注册中心
 * @author fangyuan
 *
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableEurekaServer
public class EurekaServerApplication {

	public static void main(String[] args) {
		SpringApplication.run(EurekaServerApplication.class, args);
		
	}
}

最终通过http://localhost:18809/ 访问出现下面页面代表注册中心启动成功。

2: Eureka源码分析

2.1:Eureka服务端源码分析

Eureka server在整个微服务架构中的

对于Eureka与springcloud结合相关的代码在spring-cloud-starter-netflix-eureka-server包下

从上述的启动类中我们通过@EnableEurekaServer注解表明该服务注册中心,所以代码的研读的入口也在此,点开此注解的源码:

/**
 *激活Eureka服务器相关配置的注释。
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer {

}

其中该注解通过@Import(该注解在这里的用于导入@Configuration注解的配置类,对于该注解不熟悉可自行百度下)导入EurekaServerMarkerConfiguration配置类,点开此类发现

继续向下寻找,点开EurekaServerAutoConfiguration此类,发现该类实现了WebMvcConfigurer接口(该接口提供很多自定义的拦截器,我们可以通过它实现跨域设置、类型转化器等等,详细用法可自行百度)


/**
 *该类用于初始化eureka依赖的Bean对象
 * @author Gunnar Hillert
 * @author Biju Kunjummen
 * @author Fahim Farook
 */
@Configuration(proxyBeanMethods = false)
//初始化EurekaServerInitializerConfiguration对象
@Import(EurekaServerInitializerConfiguration.class)
//标明当spring上下文中必须包含EurekaServerMarkerConfiguration.Marker类对象bean才会初始化,实际上也就代表类项目中必须包含@EnableEurekaServer注解
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
/**通过EnableConfigurationProperties注解将包含@ConfigurationProperties注解的两类
 *EurekaDashboardProperties与InstanceRegistryProperties与对应的配置文件中的设置属性对应
 *EurekaDashboardProperties是关于Eureka仪表板(UI)的配置属性,一般使用默认即可参考yml中eureka.dashboard
 *InstanceRegistryProperties是关于实例注册相关配置属性,参考yml中eureka.instance.registry
 *对应可参考后续yml中详细配置描述
 */ 
@EnableConfigurationProperties({ EurekaDashboardProperties.class,
		InstanceRegistryProperties.class })
//加在server.properties配置文件
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration implements WebMvcConfigurer {

    。。。

    //是否开启
    @Bean eureka界面
	@ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true)
	public EurekaController eurekaController() {
		return new EurekaController(this.applicationInfoManager);
	}


    //初始化动作
	@Bean
	@ConditionalOnMissingBean
	public ReplicationClientAdditionalFilters replicationClientAdditionalFilters() {
		return new ReplicationClientAdditionalFilters(Collections.emptySet());
	}

    //初始化InstanceRegistry对象,该类用于eureka服务对于各client节点注册管理(包括对等节点处理-这里的对等节点指的就是eureka集群中的节点)这里springcloud对PeerAwareInstanceRegistry进行了包装处理,核心方法的执行本质还是使用PeerAwareInstanceRegistry在执行之前会进行handler拦截触发event事件发布
	@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());
	}

    //用于管理包含Peererekanode(该节点是用于管理对等节点信息的管理)集合内生命周期的一个协助类-默认实现为RefreshablePeerEurekaNodes(该类其实是springcloud对PeerEurekaNodes的增强添加了监听EnvironmentChangeEvent事件-若该事件发生,那么代表当前服务环境发生变更了,需要进行校验是否重新修改所有的PeerEurekaNodes)Peererekanode中通过定时线程池定时(配置)来检测Peererekanode
	@Bean
    //从这个ConditionalOnMissingBean注解来看,我们可以在代码中自定义初始化该PeerEurekaNodes
	@ConditionalOnMissingBean
	public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry,
			ServerCodecs serverCodecs,
			ReplicationClientAdditionalFilters replicationClientAdditionalFilters) {
		return new RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig,
				this.eurekaClientConfig, serverCodecs, this.applicationInfoManager,
				replicationClientAdditionalFilters);
	}

    //初始化Eureka服务上下文对象,用于初始化和关闭eureka服务一些动作 实际对PeerEurekaNodes与PeerAwareInstanceRegistry管理 例如对peerEurekaNodes中定时任务启动和关闭与registry中任务的开启和关闭
	@Bean
	public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs,
			PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
		return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,
				registry, peerEurekaNodes, this.applicationInfoManager);
	}

    //初始化EurekaServerBootstrap对象用于初始化eureka基础环境
	@Bean
	public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry,
			EurekaServerContext serverContext) {
		return new EurekaServerBootstrap(this.applicationInfoManager,
				this.eurekaClientConfig, this.eurekaServerConfig, registry,
				serverContext);
	}

    //注册jersey框架对外提供rest接口,提供与client与对等节点同步的接口
    @Bean
	public FilterRegistrationBean<?> jerseyFilterRegistration(
			javax.ws.rs.core.Application eurekaJerseyApp) {
		FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<Filter>();
		bean.setFilter(new ServletContainer(eurekaJerseyApp));
		bean.setOrder(Ordered.LOWEST_PRECEDENCE);
		bean.setUrlPatterns(
				Collections.singletonList(EurekaConstants.DEFAULT_PREFIX + "/*"));

		return bean;
	}

    。。。

}


/**
 *该类用于eureka服务的一些参数
 * @author Dave Syer
 */
@Configuration(proxyBeanMethods = false)
public class EurekaServerInitializerConfiguration implements
           //通过ServletContextAware用以获取ServletContext对象
          ServletContextAware, 
          //实现SmartLifecycle该接口,作用当Spring容器加载所有bean并完成初始化之后,会接着回调实现该接口的类中对应的方法(start()方法)  
          SmartLifecycle,
          //作用用于spring初始化相同接口时顺序
          Ordered {

	private static final Log log = LogFactory
			.getLog(EurekaServerInitializerConfiguration.class);

    //包含服务注册中心有关的所有配置
	@Autowired
	private EurekaServerConfig eurekaServerConfig;

	private ServletContext servletContext;

	@Autowired
	private ApplicationContext applicationContext;

	@Autowired
	private EurekaServerBootstrap eurekaServerBootstrap;

    //当前eureka服务器状态
	private boolean running;
    //定义order等级 
	private int order = 1;

	@Override
	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}


    
    
	@Override
	public void start() {
        //启动一个线程用于执行后续初始化动作减少主线程的压力
		new Thread(() -> {
			try {
				// TODO: is this class even needed now?
				eurekaServerBootstrap.contextInitialized(
						EurekaServerInitializerConfiguration.this.servletContext);
				log.info("Started Eureka Server");
                //目前eureka服务种存在5种事件 该事件可以通过实现ApplicationListener接口来接受
                /**
                 *EurekaRegistryAvailableEvent——eureka自身注册时触发内部通知事件
                 *EurekaServerStartedEvent——eureka服务启动时触发内部通知事件
                 *EurekaInstanceCanceledEvent——eureka客户端节点下线时触发内部通知事件
                 *EurekaInstanceRenewedEvent——eureka客户端节点更新时触发内部通知事件
                 *EurekaInstanceRegisteredEvent——eureka客户端节点注册时触发内部通知事件
                 *     
                 */     
				publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
				//修改eureka服务器状态
                EurekaServerInitializerConfiguration.this.running = true;
				publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
			}
			catch (Exception ex) {
				// Help!
				log.error("Could not initialize Eureka servlet context", ex);
			}
		}).start();
	}

	...
}

/**
 *
 *
 */
public class EurekaServerBootstrap {

	private static final Log log = LogFactory.getLog(EurekaServerBootstrap.class);

	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 EurekaServerConfig eurekaServerConfig;

	protected ApplicationInfoManager applicationInfoManager;

	protected EurekaClientConfig eurekaClientConfig;

	protected PeerAwareInstanceRegistry registry;

	protected volatile EurekaServerContext serverContext;

	protected volatile AwsBinder awsBinder;

	
	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);
		}
	}

    //销毁上下文中数据
	public void contextDestroyed(ServletContext context) {
		try {
			log.info("Shutting down Eureka Server..");
			context.removeAttribute(EurekaServerContext.class.getName());

			destroyEurekaServerContext();
			destroyEurekaEnvironment();

		}
		catch (Throwable e) {
			log.error("Error shutting down eureka", e);
		}
		log.info("Eureka Service is now shutdown...");
	}

    //设置当前eureka基础环境
	protected void initEurekaEnvironment() throws Exception {
		log.info("Setting the eureka configuration..");

		String dataCenter = ConfigurationManager.getConfigInstance()
				.getString(EUREKA_DATACENTER);
        //yml中可设置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 {
			ConfigurationManager.getConfigInstance()
					.setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, dataCenter);
		}

        //yml中可设置eureka.environment时 默认为空
		String environment = ConfigurationManager.getConfigInstance()
				.getString(EUREKA_ENVIRONMENT);
		if (environment == null) {
			ConfigurationManager.getConfigInstance()
					.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST);
			log.info(
					"Eureka environment value eureka.environment is not set, defaulting to test");
		}
		else {
			ConfigurationManager.getConfigInstance()
					.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, environment);
		}
	}

	protected void initEurekaServerContext() throws Exception {
		// For backward compatibility
		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();
		}
        //该动作用于将serverContext对象EurekaServerContextHolder管理,该类提供了一个静态方法用于获取serverContext对象,这样就可以使一些不被ioc容器管理的对象也可以获取serverContext对象
		EurekaServerContextHolder.initialize(this.serverContext);

		log.info("Initialized server context");

		// Copy registry from neighboring eureka node
       //初始化同步eureka集群中其它服务中拥有的实例注册表信息
		int registryCount = this.registry.syncUp();

        //开启对外通道意思就是完成eureka服务初始化动作,修改服务状态为UP,表明可以对外提供服务
        //同时通过InstanceRegistry进行初始化 并开启定时(通过eviction-interval-timer-in-ms设置)周期任务执行EvictionTask 代码在父抽象类AbstractInstanceRegistry中postInit()方法
		this.registry.openForTraffic(this.applicationInfoManager, registryCount);

		// Register all monitoring statistics.
		EurekaMonitors.registerAllStats();
	}

	/**
	 * Server context shutdown hook. Override for custom logic
	 * @throws Exception - calling {@link AwsBinder#shutdown()} or
	 * {@link EurekaServerContext#shutdown()} may result in an exception
	 */
	protected void destroyEurekaServerContext() throws Exception {
		EurekaMonitors.shutdown();
		if (this.awsBinder != null) {
			this.awsBinder.shutdown();
		}
		if (this.serverContext != null) {
			this.serverContext.shutdown();
		}
	}

	/**
	 * Users can override to clean up the environment themselves.
	 * @throws Exception - shutting down Eureka servers may result in an exception
	 */
	protected void destroyEurekaEnvironment() throws Exception {
	}

	protected boolean isAws(InstanceInfo selfInstanceInfo) {
		boolean result = DataCenterInfo.Name.Amazon == selfInstanceInfo
				.getDataCenterInfo().getName();
		log.info("isAws returned " + result);
		return result;
	}

}

 InstanceRegistry相关类图:

InstanceRegistry该类也是springcloud对于eureka的PeerAwareInstanceRegistry类的包装,对PeerAwareInstanceRegistry提供对各PeerNode对等节点的管理的功能(任何客户端发送到集群中的任意一台eureka中动作,都需要向该eureka服务保管的对等节点列表中任意一个函数都要进行相同的处理,其中包含注册register方法,删除cancel方法,续约等功能)的增强,并且开启各种不同的定时任务(例如驱逐失效服务节点,监控当前服务与各客户端连连接情况,失败率是否超过了一定阈值:默认是15min(renewal-threshold-update-interval-ms)内超过85%(通过renewal-percent-threshold设置)的,是否需要开启自我保护机制),本质是对操作进行拦截并推送Event事件,在应用中我们可以监控不同事件做些不同处理。其它都是使用PeerAwareInstanceRegistry的原装方法。

EurekaServerContext相关类图:

用于PeerAwareInstanceRegistry与PeerEurekaNodes相关任务的启动与关闭,默认实现为DefaultEurekaServerContext,通过@PostConstruct注解在DefaultEurekaServerContext构造函数初始化完成之后被调用,进行对各个任务(例如更新对等节点任务,节点错误阈值监控是否需要开启自我保护机制)的启动,以及通过@PreDestroy注解关闭对应任务。以及对如下所示:

 

RefreshablePeerEurekaNodes相关类图: 

该类实际上是springcloud的构建对PeerEureakNodes(该类是对集群中对等节点的一个维护,包括增删改查等操作)的包装类,对PeerEureakNodes提供功能的增强,添加了 ApplicationListener监听器,监听了EnvironmentChangeEvent事件。一旦Environment发送了变更,需要判断PeerEureakNodes是否发送了变更,其它依旧使用PeerEureakNodes方法。PeerEureakNodes中维护了一个newSingleThreadScheduledExecutor的线程池,通过peer-eureka-nodes-update-interval-ms定时的调用(updatePeerEurekaNodes)去更新集群中对等节点的信息 

PeerEurekaNode:

该类用于表示一个用于节点共享信息的对等节点。使用该类用于处理复制所有对等节点的更新操作,如注册、续订、取消、过期和状态更改等

上述为该类包含的的核心方法有:

register:注册

cancel:取消

heartbeat:心跳检测 

等这些方法实际在服务端收到client请求时被调用,详细参考源码

EurekaServerBootstrap:

该类负载对EurekaServer中一些初始化动作,该类初始化动作的触发由EurekaServerInitializerConfiguration(该类实现了SmartLifecycle接口,在spring中bean初始化完成之后调用start()方法来做一些动作)中start()方法完成。该方法中就调用EurekaServerBootstrap中的contextInitialized()方法,该方法中由两个动作,initEurekaEnvironment()初始化化基础属性,比较重要的initEurekaServerContext()方法,该方法在初始化的时候需要同步其它的eureka中的节点注册表,InstanceRegistry调用syncUp()函数来做到。通过openForTraffic(),该函数由多个作用:

  1. 变更erurek服务状态为up对外提供服务
  2. 调用updateRenewsPerMinThreshold 计算每分钟续订的阈值并设置numberOfRenewsPerMinThreshold属性
  3. 调用AbstractInstanceRegistry中postInit()方法来做开启定时(通过eviction-interval-timer-in-ms设置)周期任务

 

EvictionTask:驱逐任务

该类用于驱逐一定时间内(默认是90s-该值由client自行设置)没有和eureka进行续约的客户端。

/**
 *
 *该类是抽象类AbstractInstanceRegistry中的一个内部类
 *用以eureka服务对清理无效节点
 *
 */   
 class EvictionTask extends TimerTask {
        //存储上一次evictionTask执行纳秒时间
        private final AtomicLong lastExecutionNanosRef = new AtomicLong(0l);

        @Override
        public void run() {
            try {
                //获取补偿时间(以ms为单位)-该时间是因为一些原因(例如由于时钟偏移或gc的stop allword)导致并不会按照设置的周期严格执行,实际上可能会出现evictionTask是根据配置的周期在之后执行。而作出的一定的补偿机制
                long compensationTimeMs = getCompensationTimeMs();
                logger.info("Running the evict task with compensationTime {}ms", compensationTimeMs);
                evict(compensationTimeMs);
            } catch (Throwable e) {
                logger.error("Could not run the evict task", e);
            }
        }

        long getCompensationTimeMs() {
            //获取当前时间以纳秒为单位
            long currNanos = getCurrentTimeNano();
            //通过atomic获取上一次任务执行时间 并将最新的时间写入
            long lastNanos = lastExecutionNanosRef.getAndSet(currNanos);
            
            if (lastNanos == 0l) {
                return 0l;
            }
            //计算上一次迭代以来该任务的实际执行时间
            long elapsedMs = TimeUnit.NANOSECONDS.toMillis(currNanos - lastNanos);
            //获取最终补偿时间为上一次迭代以来该任务的实际执行时间(当前时间与上一次执行完成时间之差通过)与配置的执行时间(eviction-interval-timer-in-ms)之差
            long compensationTime = elapsedMs - serverConfig.getEvictionIntervalTimerInMs();
            return compensationTime <= 0l ? 0l : compensationTime;
        }
        
        long getCurrentTimeNano() {  // for testing
            return System.nanoTime();
        }

    }

 

yml文件

server:
  port: 18809
  ##tomcat配置
  tomcat:
    ##最大连接数 默认为8192
    max-connections: 200
    ##最大线程数
    max-threads: 300
    min-spare-threads: 0
    uri-encoding: UTF-8
    accept-count: 100
spring:
  application:
    name: eureka-server
##eureka相关配置
eureka:
  ##自定义设置当前eureka环境 用于区别可能存在的多数据中心部署情况
  ##默认为test
  environment: test
  ##默认为default
  datacenter: default
  ##ui仪表盘相关配置
  dashboard:
    ##仪表盘访问露肩 这一块有关代码在EurekaController类中 默认为http://ip:port/
    path: /admin
    ##是否开启仪表盘 默认为true 若和admin或其他产品结合可关闭此选项
    enabled: true
  instance:
    hostname: localhost
    ##实例注册
    registry:
      ##用于确定何时取消租约的值,对于独立租约默认为1。对于对等复制的eurekas,应设置为0
      default-open-for-traffic-count: 1
      ##预期发送续订的客户端数,默认为1 建议设置为非零,以确保即使是独立服务器也可以将其逐出策略调整为注册数
      ##如果为零,则即使成功注册也不会重置InstanceRegistry.register()中的速率阈值
      expected-number-of-clients-sending-renews: 1
    ##设置多长时间对没有进行续约的服务进行剔除 
    ##该值如果设置过大存在因实例不在时其它请求通过路由到该服务时导致服务不可用,设置过小时会出现网络波动时该服务被过早从服务列表中剔除
    ##该值应该大于lease-renewal-interval-in-seconds值 该值一般由client来设置
    lease-expiration-duration-in-seconds: 90
  client:
    ##指示此实例是否应向eureka服务器注册其信息以供其他服务发现。
    ##默认为他true 在某些情况下不希望发现该实例,而只希望发现其他实例时可以设置为false
    registerWithEureka: false
    ##该客户端是否应从eureka服务器获取eureka注册表信息 默认为true
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  ##服务端有关配置
  server:
    ##驱逐时间定义
    eviction-interval-timer-in-ms: 60000
    ##对等节点互相更新时间 定时器触发 默认10min
    peer-eureka-nodes-update-interval-ms: 600000
    ##设置开启自我保护策略的失败比例阈值 默认为85%
    renewal-percent-threshold: 0.85
    ##设置开启自我保护策略的时间阈值默认为15分钟
    renewal-threshold-update-interval-ms: 900000

 

2.2:Eureka客户端源码分析

源码所在jar地址如下所示:

在META-INF中spring.factories中配置了

其中在eureka中会包含eureka客户端初始化入口类 EurekaClientAutoConfiguration与EurekaDiscoveryClientConfiguration两个配置类

eureka client主要有以下作用:

  1. 上线通知
  2. 启动时拉取
  3. 定期从server端pull最新的服务注册表信息
  4. 定时发送服务端心跳检测
  5. 下线通知

所以这里也针对上述功能做部分代码解析

EurekaClientAutoConfiguration类:该类初始化EurekaInstanceConfigBean(配置信息类) ,DiscoveryClient(cloud对DiscoveryClient的包装实现类CloudEurekaClient)

在DiscoveryClient中对应的构造函数中第一次全量拉取注册列表数据,如下所示

初始化了3个定时调度器,如下所示:

通过initScheduledTasks()函数构建:

其中CacheRefreshThread为具体更新注册表代码:

往下追踪下去发现与第一次pull公用一个fetchRegistry方法:根据条件来判断是否全量pull拉取 

剩下的 getAndStoreFullRegistry与getAndUpdateDelta就是通过http访问服务端去pull数据

心跳检测: 

初始化时注册:

 当client进行关闭时,spring在bean被销毁之前执行shutdown动作

 

 3:Eureka作为注册中心存在的问题

作为一个新手在使用eureka在生产环境中可能会遇到一些问题,例如新服务上线后,服务消费者不能访问到刚上线的新服务,需要过⼀段时间后才能访问?或是将服务下线后,服务还是会被调⽤到,⼀段时候后才彻底停⽌服务,访问前期会导致频繁报错?上线⼀个新的服务实例,但是服务消费者⽆感知,过了⼀段时间才知道某⼀个服务实例下线了,服务消费者⽆感知,仍然向这个服务实例在发起请求,这其实就是服务发现的⼀个问题,当我们需要调⽤服务实例时,信息是从注册中⼼Eureka获取的,然后通过Ribbon选择⼀个服务实例发起调⽤,如果出现调⽤不到或者下线后还可以调⽤的问题,原因肯定是服务实例的信息更新不及时导致的。这也是Eureka的client的主动pull更新模型导致(pull拉取时间过短对eureka服务端村砸性能压力,后续的阿里的Nacos提供了push与pull的双重保障)

Eureka服务发现慢的原因主要有两个,⼀部分是因为服务缓存导致的,另⼀部分是因为客户端缓存导致的。

  1. 服务端缓存 :服务注册到注册中⼼后,服务实例信息是存储在注册表中的,也就是内存中。但Eureka为了提⾼响应速 度,在内部做了优化,加⼊了两层的缓存结构,将Client需要的实例信息,直接缓存起来,获取的时候直接从缓存中拿数据然后响应给 Client。 第⼀层缓存是readOnlyCacheMap,readOnlyCacheMap是采⽤ConcurrentHashMap来存储数据的,主要负责定时与readWriteCacheMap进⾏数据同步,默认同步时间为 30 秒⼀次。 第⼆层缓存是readWriteCacheMap,readWriteCacheMap采⽤Guava来实现缓存。缓存过期时间默认为180秒,当服务下线、过期、注册、状态变更等操作都会清除此缓存中的数据。 Client获取服务实例数据时,会先从⼀级缓存中获取,如果⼀级缓存中不存在,再从⼆级缓存中获取, 如果⼆级缓存也不存在,会触发缓存的加载,从存储层拉取数据到缓存中,然后再返回给 Client。Eureka 之所以设计⼆级缓存机制,也是为了提⾼ Eureka Server 的响应速度,缺点是缓存会导致 Client 获取不到最新的服务实例信息,然后导致⽆法快速发现新的服务和已下线的服务。 了解了服务端的实现后,想要解决这个问题就变得很简单了,我们可以缩短只读缓存的更新时间 (eureka.server.response-cache-update-interval-ms)让服务发现变得更加及时,或者直接将只读缓 存关闭(eureka.server.use-read-only-response-cache=false),多级缓存也导致C层⾯(数据⼀致 性)很薄弱。 Eureka Server 中会有定时任务去检测失效的服务,将服务实例信息从注册表中移除,也可以将这个失 效检测的时间缩短,这样服务下线后就能够及时从注册表中清除。
  2. 客户端缓存: 客户端缓存主要分为两块内容,⼀块是 Eureka Client 缓存,⼀块是 Ribbon 缓存。 Eureka Client 缓存:EurekaClient负责跟EurekaServer进⾏交互,在EurekaClient中的com.netflflix.discovery.DiscoveryClient.initScheduledTasks() ⽅法中,初始化了⼀个 CacheRefreshThread 定时任务专⻔⽤来拉取 Eureka Server 的实例信息到本地。所以我们需要缩短这个定时拉取服务信息的时间间隔(eureka.client.registryFetchIntervalSeconds) 来快速发现新的服务。 Ribbon 缓存 Ribbon会EurekaClient中获取服务信息,ServerListUpdater是Ribbon中负责服务实例 更新的组件,默认的实现是PollingServerListUpdater,通过线程定时去更新实例信息。定时刷新的时间间隔默认是30秒,当服务停⽌或者上线后,这边最快也需要30秒才能将实例信息更新成最新的。我们 可以将这个时间调短⼀点,⽐如 3 秒。 刷新间隔的参数是通过 getRefreshIntervalMs ⽅法来获取的,⽅法中的逻辑也是从Ribbon 的配置中进 ⾏取值的。 将这些服务端缓存和客户端缓存的时间全部缩短后,跟默认的配置时间相⽐,快了很多。我们通过调整 参数的⽅式来尽量加快服务发现的速度,但是还是不能完全解决报错的问题,间隔时间设置为3秒,也还是会有间隔。所以我们⼀般都会开启重试功能,当路由的服务出现问题时,可以重试到另⼀个服务来 保证这次请求的成功。
小论文/大论文必备 | YOLO多模态目标检测、绘制曲线对比图 | 引入多种绘制曲线对比图,包括mAP0.5,mAP0.5:0.95,Loss损失变化的曲线对比 本文通过可视化方法分析YOLO多模态模型的训练过程,对比不同实验的检测精度和损失函数变化。主要内容包括:1)绘制mAP@0.5和mAP@0.5:0.95曲线评估检测精度;2)分析边界框回归损失(box_loss)、分类损失(cls_loss)和分布式损失(dfl_loss)的收敛趋势;3)提供完整的Python代码实现,支持多实验对比,可生成高清曲线图。该方法能直观展示模型训练效果,便于优化策略评估和参数调优。 阅读详情

相关推荐

【鸿蒙实战开发】HarmonyOS申请调试签名证书过程

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

HarmonyOS_002的博客 3561

spring-cloud-eureka

概要1.启动原理1.1启动日志 先跑一下microservice-discovery-eureka的EurekaApplication。可以看到启动日志: [Thread-22] o.s.c.n.e.server.EurekaServerBootstrap : Setting the eureka configuration.. [ main] c.n.e.EurekaDiscov

北蔡的代码世界 1251

如何把DeepSeek这个奈斯工具接入到Pycharm里

DeepSeek接入到Pycharm中,提高编程效率,以及帮助解决遇到的问题。

学无止境,何以言满 2623

SpringCloud服务发现组件Eureka源码解析(启动流程)

本文从源码视角简述Eureka Server的启动流程。 一.入口 eureka-server包结构如下: 可以看到,一共包含5个目录,其中/eureka存放配置信息,/static/eureka与/templates/eureka存放静态文件,/org目录存放Java相关代码,/META-INF存放应用的主要信息。 打开/META-INF中的spring.factories,内容如下: org...

qq_15898739的博客 740

Ansys口腔正畸有限元分析:从几何处理到接触设定的全流程解析

本文详细解析了Ansys在口腔正畸有限元分析中的全流程应用,从几何处理到接触设定,帮助医生精准预测牙齿移动趋势。通过有限元建模技术,可同时观察牙齿-牙周膜-牙槽骨的力学响应,显著提升矫治效果预测准确率。文章还分享了实用技巧和参数设置建议,适用于正畸医生和工程师。

weixin_30897233的博客 348

华为防火墙USG6000系列多ISP上网(通过WEB界面设置)

华为防火墙多ISP上网,内网分区上网。内网多网段不同上网方式。

daxihe880的博客 1万+

Running the evict task with compensationTime

代码】RunningtheevicttaskwithcompensationTime。

qq_36521848的博客 1154

20、自定义用户窗体:从创建到使用

本文详细介绍了在Excel VBA中创建和使用自定义用户窗体的完整过程,涵盖从窗体设计、控件添加与属性设置,到事件处理程序编写及宏调用的各个环节。通过实际示例讲解了如何实现文本大小写转换和员工信息录入等功能,并深入解析了常用控件的属性与事件机制。同时提供了控件命名规范、快捷键使用、布局技巧等实用建议,帮助读者构建高效、交互性强的VBA用户界面。

h0i1j2k3l的博客 131

eureka页面定制和修改eureka前台显示的environment

eureka作为springcloud的一个组件,扮演着很重要的角色,客户想定制一番,然后上面的参数也想改一下 第一部分:定制页面 找到启动类里面的eurekaserver的启动类,点击进入,查看如下对应版本的eureka的源码信息,找到对应的ftl就可以修改了 第二部分:修改environment和datacenter 在application添加environment ...

SmileTimLi的博客 5169

人形机器人开源资源大盘点(四):硬件开源平台

开源人形机器人平台推动技术民主化 本期聚焦四大开源人形机器人平台:斯坦福的ToddlerBot以6000美元低成本实现30自由度运动,支持仿真到现实的零样本迁移;法国Reachy2被HuggingFace收购,融合开源AI与硬件,适配医疗、工业等场景;国内OriginMan基于ROS2全开源,集成大模型能力,支持深度改造;OAN框架则为经典NAO机器人注入开源活力,扩展交互功能。这些平台通过不同路径降低研发门槛,推动人形机器人技术走向开放共享的未来。

Studying…… 370

YOLOv8目标出入计数部署:讲解C#使用ONNX检测模型进行目标出入计数 可运行完整实例

代码、步骤、讲解完整,可一键运行,效果如下:车辆出入计数人员出入计数。

Limiiiing的博客 79

SpringCloud注册中心Eureka源码分析(四)

SpringCloud注册中心Eureka源码分析 服务获取与服务续约 继续查看DiscoveryClient的initScheduledTasks函数,不难发现在其中还有两个定时任务,分别是“服务获取”和“服务续约”: if (this.clientConfig.shouldFetchRegistry()) { renewalIntervalInSecs = this.c...

AnnotationZZ的博客 850

【软考高级架构】论文范文12——论生成式AI在软件开发中的应用与挑战

随着生成式AI技术的跨越式发展,以大语言模型为代表的生成式AI正在深刻改变软件开发的传统模式。在代码生成、测试构造、文档编写等环节,生成式AI展现出显著的效率提升潜力,但其应用也面临着代码质量不可控、安全漏洞植入、版权合规等严峻挑战。本文以笔者主导的某大型金融企业智能研发辅助平台建设项目为案例,围绕生成式AI在软件开发中的应用实践与应对挑战的策略展开论述。笔者担任系统架构师,负责平台架构设计、模型选型、质量保障机制及安全防线构建。

qq837993702的博客 275

SpringCloud注册中心Eureka源码分析(一)

首先,对于服务注册中心、服务提供者、服务消费者这三个主要元素来说,后两者在整个运行机制中是大部分通信行为的主动发起者,而注册中心主要是处理请求的接收者。所以,我们可以从Eureka的客户端作为入口看看它们是如何完成这些主动通信行为的。

AnnotationZZ的博客 772

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

SpringCloud注册中心Eureka源码分析(三)

SpringCloud注册中心Eureka源码分析 服务注册 在理解了多个注册中心信息的加载后,我们再回头看看DiscoveryClient类是如何实现“服务注册”行为的,通过查看它的构造类,可以找到它调用了下面这个函数: private void initScheduledTasks() { int renewalIntervalInSecs; int expBa...

AnnotationZZ的博客 619

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

SpringCloud注册中心Eureka源码分析(二)

SpringCloud注册中心Eureka源码分析 serviceUrls 在获取了Region和Zone的信息之后,才开始真正加载Eureka Server 的具体地址。他根据传入的参数按一定算法确定加载位于哪一个Zone配置的serviceUrls。 int myZoneOffset = getZoneOffset(instanceZone, preferSameZone, availZone...

AnnotationZZ的博客 678

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Spring Cloud 源码分析之 Eureka 篇:注册中心启动类上的注解 EnableEurekaServer

表示,EurekaServerMarkerConfiguration这个类也会被作为bean做实例化;如上所示,简单到只有个一内部类EurekaServerMarkerConfiguration.Marker,我的猜测是有的bean会通过注解ConditionalOnBean作为自己是否实例化的条件,而条件对应的bean就是EurekaServerMarkerConfiguration.Marker;,今天的源码分析都是围绕这个类开展的;...

m0_57042151的博客 368

Spring Cloud源码分析之Eureka篇第二章:注册中心启动类上的注解EnableEurekaServer

本章是《Spring Cloud源码分析》系列文章的第二篇,我们从注册中心Eureka开始这段历程; Spring Cloud源码下载 今天的源码分析主要是spring-cloud-netflix这个工程的源码,版本为1.4.0.RELEASE,请参照第一篇《Spring Cloud源码分析之Eureka篇第一章:准备工作》中的方法在GiuHub下载; 启动类上的注解 第一篇《Spring Cl...

灵越启航 206

微服务:SpringCloud 注册中心(Eureka)+ 源码分析est

一、没有注册中心的模拟微服务 这里有四个项目,其中三个是服务提供者,这三个服务提供者通过@RestController暴露rest接口,供消费者去调用。消费者可以用java代码编写get或者post请求,我们通常用的是RestTmplate,这个spring为我们提供好的类,特别好用!正常情况下,我们就可以很顺利的调用这些服务了。这个是没有问题的! 小伙伴们可能要问,这能调用。我们直接搞就行了,为啥还用注册中心呢?多此一举? 我们来考虑这样几个问题:(1)在我们调用提供者的接口的时候,是不是要将他

Zystem 531

Spring Cloud 学习 之 Spring Cloud Eureka(源码分析)

Spring Cloud 学习 之 Spring Cloud Eureka(源码分析) Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 文章目录客户端(以之前搭建的[客户端](https://blog.youkuaiyun.com/qq_41907991/article/details/89611301)demo为例):Region,Zone,...

做一个认真的程序员 1110

Spring Cloud 源码分析之 Eureka 篇第二章:注册中心启动类上的注解 EnableEurekaServer

​ 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos ​

java_beautiful的博客 390

SpringCloud 微服务注册中心 Eureka - Client

前段时间业务工作太忙,很久没有学习技术了~ 碰巧前几天工作中遇到一个问题需要研究注册中心和OpenFeign源码才能解决,由于OpenFeign源码我已经比较熟悉了,所以今天来分享一下向Server注册实例的原理。只有懂原理才能进行扩展,对项目进行不断优化。也许很多人会说Eureka已经过时了,我又何尝不知道相对来说Nacos更好用,而且Eureka已经不更新了,但是没有办法呀,公司用的就是Eureka。不过大体思想都是相同的,只是Nacos在Eureka的基础上做了一些优化让服务注册中心更加完善。读完。

Huangjiazhen711的博客 913

SpringCloud服务注册与发现:Eureka架构与实现原理

SpringCloud Eureka作为微服务架构中的核心组件,通过服务注册与发现机制解决了服务通信的难题。本文从Eureka的基础架构入手,详细介绍了其注册机制、高可用设计、自我保护机制和服务发现功能,并通过源码分析深入理解了Eureka的实现原理。与其他注册中心相比,Eureka在可用性方面表现出色,特别适合对服务可用性要求较高的分布式系统。在实际应用中,开发者需要根据业务需求和系统特性选择合适的注册中心。随着微服务架构的普及,熟练掌握Eureka等服务注册与发现组件已成为Java开发者的必备技能。

程序媛学姐的博客 1992

2.SpringCloud学习(二)——Spring Cloud Eureka 服务注册中心

1.简介 1.1 概述 Service Discovery is one of the key tenets of a microservice-based architecture. Trying to hand-configure each client or some form of convention can be difficult to do and can be brittle. Eureka is the Netflix Service Discovery Server and Clie

Soulballad的博客 443

【SpringCloud】学习笔记-p1(服务拆分&远程调用&Eureka注册中心&Ribbon负载均衡)

SpringCloudRibbon的底层采用了一个拦截器,拦截了RestTemplate发出的请求,对地址做了修改。用一幅图来总结一下:基本流程如下:拦截我们的请求http://userservice/user/1会从请求url中获取服务名称,也就是user-service根据user-service到eureka拉取服务列表eureka返回列表,localhost:8081、localhost:8082IRule利用内置负载均衡规则,从列表中选择一个,例如localhost:8081。

2301_82242130的博客 1116

SpringCloud全家桶从入门到放弃

SpringCloud一代全家桶从入门到放弃

猿上生活 5283
上一篇: 分布式一致性算法与开源分布式应用具体实现
下一篇: springboot2.x +rabbitmq使用和源码分析一(自动装配)
F_Hello_World
博客等级 码龄11年 38粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值