现在任务网站源码分享,任务pp源码(任务网最好的是哪个)

大家好!今天让小编来大家介绍下关于现在任务网站源码分享,任务pp源码的问题,以下是酷知号的小编对此问题的归纳整理,让我们一起来看看吧。

现在任务网站源码分享,任务pp源码

各位老铁们好,相信很多人对现在任务网站源码分享都不是特别的了解,因此呢,今天就来为大家分享下关于现在任务网站源码分享以及任务app源码的问题知识,还望可以帮助大家,解决大家的一些困惑,下面一起来看看吧!

0.环境

eureka版本:1.10.11SpringCloud:2020.0.2SpringBoot:2.4.4测试代码:github.com/hsfxuebao/s…

1.前言

本文主要是解析下SpringCloud整合EurekaClient的源码,这块代码比较多,而且都是些简单代码,我们稍微看下就行,这就是介绍下EurekaClient初始化过程,不管你SpringCloud怎样封装,底层还是EurekaClient的内容,初始化过程包括下面:

去EurekaServer拉取全量注册表,创建定时任务,包括定时去EurekaServer上增量拉取注册表信息,定时renew(服务续约)。服务注册

2.SpringCloud整合EurekaClient启动入口

要看SpringCloud怎样整合EurekaClient,就需要找到它们的自动装配配置类在spring-cloud-starter-netflix-eureka-client依赖的pom文件中,在依赖pom文件中有spring-cloud-netflix-eureka-client,在这个里面能够找到spring.factories文件,这个文件是springspi文件。

核心就是EurekaClientAutoConfiguration这个自动装配类:

@Configuration(proxyBeanMethods=false)n@EnableConfigurationPropertiesn@ConditionalOnClass(EurekaClientConfig.class)n@ConditionalOnProperty(value=&34;,matchIfMissing=true)n@ConditionalOnDiscoveryEnabledn@AutoConfigureBefore({CommonsClientAutoConfiguration.class,ServiceRegistryAutoConfiguration.class})n@AutoConfigureAfter(name={&34;,n&34;,n&34;,n&34;})npublicclassEurekaClientAutoConfiguration{n}

2.1封装配置文件的类

2.1.1EurekaClientConfigBean

@Beann@ConditionalOnMissingBean(value=EurekaClientConfig.class,search=SearchStrategy.CURRENT)npublicEurekaClientConfigBeaneurekaClientConfigBean(ConfigurableEnvironmentenv){nreturnnewEurekaClientConfigBean();n}

其读取的是eureka.client前辍的配置信息。这个类已经被@ConfigurationProperties注解了,所以这些配置信息可以被自动封装并注册到容器。

2.1.2EurekaInstanceConfigBean

@Beann@ConditionalOnMissingBean(value=EurekaInstanceConfig.class,search=SearchStrategy.CURRENT)npublicEurekaInstanceConfigBeaneurekaInstanceConfigBean(InetUtilsinetUtils,nManagementMetadataProvidermanagementMetadataProvider){n}

其读取的是eureka.instance的属性值。这个类也已经被@ConfigurationProperties注解了,所以这些配置信息可以被自动封装并注册到容器。

2.2EurekaClient

接下来,看看核心类EurekaClient是怎么注入进去的?在EurekaClientAutoConfiguration文件中,我们发现有两个地方都可以注入EurekaClient,分别为:

@Configuration(proxyBeanMethods=false)n@ConditionalOnMissingRefreshScopenprotectedstaticclassEurekaClientConfiguration{nn@Bean(destroyMethod=&34;)n@ConditionalOnMissingBean(value=EurekaClient.class,search=SearchStrategy.CURRENT)npublicEurekaClienteurekaClient(ApplicationInfoManagermanager,EurekaClientConfigconfig){nreturnnewCloudEurekaClient(manager,config,this.optionalArgs,this.context);n}n}nn//另一个是:n@Configuration(proxyBeanMethods=false)n@ConditionalOnRefreshScopenprotectedstaticclassRefreshableEurekaClientConfiguration{nn@Bean(destroyMethod=&34;)n@ConditionalOnMissingBean(value=EurekaClient.class,search=SearchStrategy.CURRENT)n@org.springframework.cloud.context.config.annotation.RefreshScopen@LazynpublicEurekaClienteurekaClient(ApplicationInfoManagermanager,EurekaClientConfigconfig,nEurekaInstanceConfiginstance,@Autowired(required=false)HealthCheckHandlerhealthCheckHandler){n}nn}

这就需要分析到底哪一个注解生效了?

@ConditionalOnMissingRefreshScope

@Target({ElementType.TYPE,ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Conditional(OnMissingRefreshScopeCondition.class)

@interfaceConditionalOnMissingRefreshScope{

}

privatestaticclassOnMissingRefreshScopeConditionextendsAnyNestedCondition{

OnMissingRefreshScopeCondition(){

super(ConfigurationPhase.REGISTER_BEAN);

}

@ConditionalOnMissingClass(&34;)

staticclassMissingClass{

}

@ConditionalOnMissingBean(RefreshAutoConfiguration.class)

staticclassMissingScope{

}

@ConditionalOnProperty(value=&34;,havingValue=&34;)

staticclassOnPropertyDisabled{

}

}

大家可以看看AnyNestedCondition这个注解,意思就是只要满足任意一个条件就符合。通过分析,我们知道这三个条件都是满足的,所以这个注解不生效,这个类不生效。

@ConditionalOnRefreshScope

@Target({ElementType.TYPE,ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@ConditionalOnClass(RefreshScope.class)

@ConditionalOnBean(RefreshAutoConfiguration.class)

@ConditionalOnProperty(value=&34;,havingValue=&34;,matchIfMissing=true)

@interfaceConditionalOnRefreshScope{

}

通过这个注解EurekaClientAutoConfiguration上的注解@AutoConfigureAfter,我们知道当前类注入是在RefreshAutoConfiguration之后注入到容器中。而RefreshScope就是在RefreshAutoConfiguration之后中注入的。所以我们需要分析这个类就可以了。

@AutoConfigureAfter(name={&34;,

&34;,

&34;,

&34;})

publicclassEurekaClientAutoConfiguration{

}

2.2.1ApplicationInfoManager

@Beann@ConditionalOnMissingBean(value=ApplicationInfoManager.class,search=SearchStrategy.CURRENT)npublicApplicationInfoManagereurekaApplicationInfoManager(nttEurekaInstanceConfigconfig){ntInstanceInfoinstanceInfo=newInstanceInfoFactory().create(config);ntreturnnewApplicationInfoManager(config,instanceInfo);n}

创建ApplicationInfoManager对象,这个对象主要就是管着当前实例信息,也就是instanceInfo,可以看到,在这个方法中先是创建的instanceInfo,然后将instanceInfo作为构造参数传入了ApplicationInfoManager中。

这个实例信息instanceInfo里面维护了你当前实例的ip,端口,appName等信息,注册的时候就是拿这些信息到EurekaServer上注册。

2.2.2EurekaClient

@Bean(destroyMethod=&34;)n@ConditionalOnMissingBean(value=EurekaClient.class,search=SearchStrategy.CURRENT)npublicEurekaClienteurekaClient(ApplicationInfoManagermanager,EurekaClientConfigconfig){ntreturnnewCloudEurekaClient(manager,config,this.optionalArgs,ntttthis.context);n}

创建EurekaClient对象,这个CloudEurekaClient类是SpringCloud搞得,然后继承Eureka原生的DiscoveryClient类。

publicclassCloudEurekaClientextendsDiscoveryClient

我们可以看看它的构造

最重要的是,它调用了父类的DiscoveryClient的构造,下面重点介绍。

2.3小结

总结以上的信息,从EurekaClientAutoConfiguration等方面可罗列出如下几个比较重要的类,如下:

类名

介绍与作用

EurekaClientConfig

封装了EurekaClient与EurekaServer交互时所需要的配置信息,SpringCloud为其提供了默认配置类:EurekaClientConfigBean。

ApplicationInfoManager

作为应用信息管理器,管理服务实例类Instancenfo和服务实例配置信息类EurekaInstanceConfig。

InstanceInfo

封装了将被发送到EurekaServer进行服务注册的服务实例元数据,它在Eureka注册表中代表着一个服务实例,其他服务可通过InstanceInfo来了解该服务实例的相关信息,从而进行相关操作。

EurekaInstanceConfig

封装了EurekaClient自身服务实例的配置信息,主要用于构建InstanceInfo,通常这些信息在配置文件的eureka.instance前缀下进行设置,SpringCloud通过EurekaInstanceBean配置类提供默认配置。

DiscoveryClient

SpringCloud中定义用来做服务发现的客户端接口。

3.DiscoveryClient类的解析

3.1DiscoveryClient作用

DiscoveryClient是EurekaClient的核心类,其作用与下:

注册实例到EurekaServer中发送心跳更新与EurekaServer的续约在服务关闭时取消与EurekaServer的续约,完成服务下限获取在EurekaServer中的服务实例列表

3.2DiscoveryClient的类结构

可以先看下DiscoveryClient的类结构图:

从类结构图上可以看出DiscoveryClient类实现了EurekaCient,EurekaCient又继承了LookupService,这里看看LookupService类:

publicinterfaceLookupService<T>{n//根据服务实例名称获取ApplicationnApplicationgetApplication(StringappName);n//获取当前注册表中所有的服务实例信息nApplicationsgetApplications();n//根据服务实例Id获取服务实例信息nList<InstanceInfo>getInstancesById(Stringid);nnInstanceInfogetNextServerFromEureka(StringvirtualHostname,booleansecure);n}

Application是持有服务实例信息列表,它表示同一个服务的集群信息,这些服务实例乃是挂载在同一个服务名appName之下,而InstanceInfo则是代表着一个服务实例的信息,Application类代码如下:

publicclassApplication{nnprivatestaticRandomshuffleRandom=newRandom();n//服务名nprivateStringname;n//标识服务状态n@XStreamOmitFieldnprivatevolatilebooleanisDirty=false;nn@XStreamImplicitnprivatefinalSet<InstanceInfo>instances;nnprivatefinalAtomicReference<List<InstanceInfo>>shuffledInstances;nnprivatefinalMap<String,InstanceInfo>instancesMap;nn//……..n}

在Application中对InstanceInfo的操作都是同步的,为的是保证其原子性。Applications则是注册表中所有服务实例的集合,其间的操作也都是同步的。EurekaClient继承了LookupService接口,为DiscoveryClient提供一个上层接口,其目的是为了Eureka1.0x到Eureka2.x的升级做过渡。

EurekaCient接口在LookupService的基础上提供了更丰富的方法,譬如:

提供做种方式获取InstanceInfo,例如根据区域、EurekaServer地址获取等。提供本地客户端(区域、可用区)的数据,这部分与AWS相关提供了为客户端注册和获取健康检查处理器的功能

除了相关查询接口外,EurekaClient提供以下的两个方法,需颇多关注:

publicinterfaceEurekaClientextendsLookupService{n//…….n//为EurekaClient注册健康处理器npublicvoidregisterHealthCheck(HealthCheckHandlerhealthCheckHandler);n//监听Client服务实例信息的更新npublicvoidregisterEventListener(EurekaEventListenereventListener);n}

在EurekaServer中一般是通过心跳来识别一个实例的状态,而在EurekaClient中则存在一个定时任务定时通过HealthCheckHandler检测当前Client的状态,当其状态发生变化的时候,将会触发新的注册事件,更新EurekaServer的注册表中的相关实例信息。

3.3DiscoveryClient构造函数

在DiscoveryClient的构造函数中,会有如下操作,如:服注册表信息、服务注册、初始化发送心跳、缓存刷新、注册定时任务等。因此DiscoveryClient的构造函数贯穿了EurekaClient启动阶段的各项任务。

DiscoveryClient(ApplicationInfoManagerapplicationInfoManager,EurekaClientConfigconfig,AbstractDiscoveryClientOptionalArgsargs,nProvider<BackupRegistry>backupRegistryProvider,EndpointRandomizerendpointRandomizer){n//省略相关信息n}

在DiscoveryClient的构造函数中有如下几个参数:ApplicationInfoManager、EurekaClientConfig、AbstractDiscoveryClientOptionalArgs、Provider<BackupRegistry>、EndpointRandomizer。前两个参数前面已做介绍,AbstractDiscoveryClientOptionalArgs用于注入一些可选参数,BackupRegistry则充当备份注册中心的职责,EndpointRandomizer则是作为端点随机器。对DiscoveryClient的构造函数的职责做一个简单概括:

相关配置赋值,如ApplicationInfoManager、EurekaClientConfig等备份注册中心初始化,默认没有实现拉去EurekaServer注册表信息注册前预处理向EurekaServer注册自身初始化定时任务、缓存刷新、按需注册定时任务

后面将会对这些步骤中对重要点进行相关分析。

4.EurekaClient初始化

接下来我们看下DiscoveryClient是怎样初始化的(构造方法中)。代码如下:

@InjectnDiscoveryClient(ApplicationInfoManagerapplicationInfoManager,EurekaClientConfigconfig,AbstractDiscoveryClientOptionalArgsargs,nProvider<BackupRegistry>backupRegistryProvider,EndpointRandomizerendpointRandomizer){n…nn//如果开启拉取注册表的话nif(clientConfig.shouldFetchRegistry()){ntry{n//todo拉取注册表信息nbooleanprimaryFetchRegistryResult=fetchRegistry(false);nif(!primaryFetchRegistryResult){nlogger.info(&34;);n}n…n}n}n…n//如果进行服务注册的话clientConfig.shouldEnforceRegistrationAtInit()默认falsenif(clientConfig.shouldRegisterWithEureka()&&clientConfig.shouldEnforceRegistrationAtInit()){ntry{n//todo进行服务注册nif(!register()){nthrownewIllegalStateException(&34;);n}n}n…n}nn//finally,initthescheduletasks(e.g.clusterresolvers,heartbeat,instanceInforeplicator,fetchn//todo定时任务ninitScheduledTasks();nn…n}

4.1拉取注册表信息

//如果开启拉取注册表的话nif(clientConfig.shouldFetchRegistry()){n//拉取注册表信息nbooleanprimaryFetchRegistryResult=fetchRegistry(false);n}

如果开启拉取注册信息,就会调用fetchRegistry方法去EurekaServer上面拉取注册表信息。

privatebooleanfetchRegistry(booleanforceFullRegistryFetch){nn//Ifthedeltaisdisabledorifitisthefirsttime,getalln//applicationsnApplicationsapplications=getApplications();nif(clientConfig.shouldDisableDelta()//关闭增量,默认falsen||(!Strings.isNullOrEmpty(clientConfig.getRegistryRefreshSingleVipAddress()))n||forceFullRegistryFetchn||(applications==null)n||(applications.getRegisteredApplications().size()==0)n||(applications.getVersion()==-1))//Clientapplicationdoesnothavelatestlibrarysupportingdeltan{n//todo全量拉取注册表信息ngetAndStoreFullRegistry();n}else{n//todo增量更新ngetAndUpdateDelta(applications);n}n//设置hashCodenapplications.setAppsHashCode(applications.getReconcileHashCode());nlogTotalInstances();n}

可以看下最上面的注释,不启用增量或者是第一次,就拉取全量注册表信息。

不启用增量||强制全量||本地注册表是空的,这个时候就会调用getAndStoreFullRegistry方法去EurekaServer拉取全量注册表。否则的话调用getAndUpdateDelta方法获取增量注册表信息。

4.1.1全量拉取注册表信息

接下来我们看下getAndStoreFullRegistry方法,看看是怎样拉取全量注册表的。

//获取所有注册表信息nprivatevoidgetAndStoreFullRegistry()throwsThrowable{nlongcurrentUpdateGeneration=fetchRegistryGeneration.get();nnApplicationsapps=null;n//交给网络传输组件,发起网络请求,获得响应nEurekaHttpResponse<Applications>httpResponse=clientConfig.getRegistryRefreshSingleVipAddress()==nulln//todoapps请求urln?eurekaTransport.queryClient.getApplications(remoteRegionsRef.get())n:eurekaTransport.queryClient.getVip(clientConfig.getRegistryRefreshSingleVipAddress(),remoteRegionsRef.get());nif(httpResponse.getStatusCode()==Status.OK.getStatusCode()){napps=httpResponse.getEntity();n}nnif(apps==null){nlogger.error(&34;);n}elseif(fetchRegistryGeneration.compareAndSet(currentUpdateGeneration,currentUpdateGeneration+1)){n//nlocalRegionApps.set(this.filterAndShuffle(apps));nlogger.debug(&34;,apps.getAppsHashCode());n}else{nlogger.warn(&34;);n}n}

这里其实就是调用网络组件来发起请求,得到响应了,然后拿到所有得实例信息后,将实例信息设置到本地注册表中。我们这里再深入一点,看看eurekaTransport.queryClient.getApplications(remoteRegionsRef.get())是请求得哪个url:

@OverridenpublicEurekaHttpResponse<Applications>getApplications(String…regions){nreturngetApplicationsInternal(&34;,regions);n}nprivateEurekaHttpResponse<Applications>getApplicationsInternal(StringurlPath,String[]regions){nClientResponseresponse=null;nStringregionsParamValue=null;ntry{nWebResourcewebResource=jerseyClient.resource(serviceUrl).path(urlPath);n//拼接regionnif(regions!=null&&regions.length>0){nregionsParamValue=StringUtil.join(regions);nwebResource=webResource.queryParam(&34;,regionsParamValue);n}nBuilderrequestBuilder=webResource.getRequestBuilder();naddExtraHeaders(requestBuilder);n//提交get请求nresponse=requestBuilder.accept(MediaType.APPLICATION_JSON_TYPE).get(ClientResponse.class);nnApplicationsapplications=null;nif(response.getStatus()==Status.OK.getStatusCode()&&response.hasEntity()){napplications=response.getEntity(Applications.class);n}nreturnanEurekaHttpResponse(response.getStatus(),Applications.class)n.headers(headersOf(response))n.entity(applications)n.build();n}n}

拉取全量注册表的请求为:GET请求,path为:apps/

4.1.2增量拉取注册表信息

getAndUpdateDelta(applications);代码如下:

privatevoidgetAndUpdateDelta(Applicationsapplications)throwsThrowable{nlongcurrentUpdateGeneration=fetchRegistryGeneration.get();nnApplicationsdelta=null;n//提交请求nEurekaHttpResponse<Applications>httpResponse=eurekaTransport.queryClient.getDelta(remoteRegionsRef.get());nif(httpResponse.getStatusCode()==Status.OK.getStatusCode()){ndelta=httpResponse.getEntity();n}nnif(delta==null){nngetAndStoreFullRegistry();n}elseif(fetchRegistryGeneration.compareAndSet(currentUpdateGeneration,currentUpdateGeneration+1)){nnStringreconcileHashCode=&34;;nif(fetchRegistryUpdateLock.tryLock()){ntry{n/**n*这里要将从Server获取到的所有变更信息更新到本地缓存。这些变n*更信来自于两类Region:本地Region与远程Region。而本地缓存也n*分为两类:缓存本地Region的applications与缓存所有远程Regionn*的注册信息的map(key为远程Region,value为该远程Region的注册n*表)n*/n//todonupdateDelta(delta);nreconcileHashCode=getReconcileHashCode(applications);n}finally{nfetchRegistryUpdateLock.unlock();n}n}n…n}

增量拉取注册表的请求:GET请求path为:apps/delta

然后,我们重点看一下updateDelta(delta);方法:

privatevoidupdateDelta(Applicationsdelta){nintdeltaCount=0;nfor(Applicationapp:delta.getRegisteredApplications()){nfor(InstanceInfoinstance:app.getInstances()){nApplicationsapplications=getApplications();nStringinstanceRegion=instanceRegionChecker.getInstanceRegion(instance);n//不是本地region,远程regionnif(!instanceRegionChecker.isLocalRegion(instanceRegion)){nApplicationsremoteApps=remoteRegionVsApps.get(instanceRegion);nif(null==remoteApps){nremoteApps=newApplications();nremoteRegionVsApps.put(instanceRegion,remoteApps);n}napplications=remoteApps;n}nn++deltaCount;n//有新增加的实例信息nif(ActionType.ADDED.equals(instance.getActionType())){nApplicationexistingApp=applications.getRegisteredApplications(instance.getAppName());nif(existingApp==null){napplications.addApplication(app);n}nlogger.debug(&34;,instance.getId(),instanceRegion);napplications.getRegisteredApplications(instance.getAppName()).addInstance(instance);n//有修改的n}elseif(ActionType.MODIFIED.equals(instance.getActionType())){nApplicationexistingApp=applications.getRegisteredApplications(instance.getAppName());nif(existingApp==null){napplications.addApplication(app);n}nlogger.debug(&34;,instance.getId());nnapplications.getRegisteredApplications(instance.getAppName()).addInstance(instance);nn//有删除的n}elseif(ActionType.DELETED.equals(instance.getActionType())){nApplicationexistingApp=applications.getRegisteredApplications(instance.getAppName());nif(existingApp!=null){nlogger.debug(&34;,instance.getId());nexistingApp.removeInstance(instance);n/*n*Wefindallinstancelistfromapplication(ThestatusofinstancestatusisnotonlythestatusisUPbutalsootherstatus)n*ifinstancelistisempty,weremovetheapplication.n*/nif(existingApp.getInstancesAsIsFromEureka().isEmpty()){napplications.removeApplication(existingApp);n}n}n}n}n}n…n}

这个方法就是更新客户端本地的注册表信息。

4.2服务注册

//如果进行服务注册的话clientConfig.shouldEnforceRegistrationAtInit()默认falsenif(clientConfig.shouldRegisterWithEureka()&&clientConfig.shouldEnforceRegistrationAtInit()){ntry{n//todo进行服务注册nif(!register()){nthrownewIllegalStateException(&34;);n}n}catch(Throwableth){nlogger.error(&34;,th.getMessage());nthrownewIllegalStateException(th);n}n}

如果在这里进行服务注册的话,需要配置文件中增加下面配置(默认是false):

eureka.client.should-enforce-registration-at-init:true

所以在这里是没有服务注册的,那么服务注册是在哪里呢?在会面分析续约定时任务时完成了服务注册,不过,我们在这里也看一下服务注册的代码:

booleanregister()throwsThrowable{nEurekaHttpResponse<Void>httpResponse;ntry{n//todo进行服务注册nhttpResponse=eurekaTransport.registrationClient.register(instanceInfo);n}n…n}nreturnhttpResponse.getStatusCode()==Status.NO_CONTENT.getStatusCode();n}

接下来看:

@OverridenpublicEurekaHttpResponse<Void>register(InstanceInfoinfo){nStringurlPath=&34;+info.getAppName();nResponseresponse=null;ntry{nBuilderresourceBuilder=jerseyClient.target(serviceUrl).path(urlPath).request();naddExtraProperties(resourceBuilder);naddExtraHeaders(resourceBuilder);nresponse=resourceBuildern.accept(MediaType.APPLICATION_JSON)n.acceptEncoding(&34;)n.post(Entity.json(info));nreturnanEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();n}finally{nif(logger.isDebugEnabled()){nlogger.debug(&34;,serviceUrl,urlPath,info.getId(),nresponse==null?&34;:response.getStatus());n}nif(response!=null){nresponse.close();n}n}n}

服务注册:POST请求,path为:“apps/&34;cacheRefresh&run:

@Overridenpublicvoidrun(){nFuture<?>future=null;ntry{n//使用Future,可以设定子线程的超时时间,这样当前线程就不用无限等待了nfuture=executor.submit(task);nthreadPoolLevelGauge.set((long)executor.getActiveCount());n//阻塞获取任务的执行结果nfuture.get(timeoutMillis,TimeUnit.MILLISECONDS);//blockuntildoneortimeoutn//delay是个很有用的变量,后面会用到,这里记得每次执行任务成功都会将delay重置ndelay.set(timeoutMillis);nthreadPoolLevelGauge.set((long)executor.getActiveCount());nsuccessCounter.increment();n}catch(TimeoutExceptione){nlogger.warn(&34;,e);ntimeoutCounter.increment();nnlongcurrentDelay=delay.get();n//任务线程超时的时候,就把delay变量翻倍,但不会超过外部调用时设定的最大延时时间nlongnewDelay=Math.min(maxDelay,currentDelay*2);n//设置为最新的值,考虑到多线程,所以用了CASndelay.compareAndSet(currentDelay,newDelay);nn}catch(RejectedExecutionExceptione){n//一旦线程池的阻塞队列中放满了待处理任务,触发了拒绝策略,就会将调度器停掉nif(executor.isShutdown()||scheduler.isShutdown()){nlogger.warn(&34;,e);n}else{nlogger.warn(&34;,e);n}nnrejectedCounter.increment();n}catch(Throwablee){n//一旦出现未知的异常,就停掉调度器nif(executor.isShutdown()||scheduler.isShutdown()){nlogger.warn(&39;tacceptthetask&34;tasksupervisorthrewanexception&34;cacheRefresh&run中finally代码,在这里又重新开启了新的定时任务:

finally{n//这里任务要么执行完毕,要么发生异常,都用cancel方法来清理任务;nif(future!=null){nfuture.cancel(true);n}nn//只要调度器没有停止,就再指定等待时间之后在执行一次同样的任务nif(!scheduler.isShutdown()){n//todo下一次时间再次执行这个任务n//这里就是周期性任务的原因:只要没有停止调度器,就再创建一次性任务,执行时间时delay的值,n//假设外部调用时传入的超时时间为30秒(构造方法的入参timeout),最大间隔时间为50秒(构造方法的入参expBackOffBound)n//如果最近一次任务没有超时,那么就在30秒后开始新任务,n//如果最近一次任务超时了,那么就在50秒后开始新任务(异常处理中有个乘以二的操作,乘以二后的60秒超过了最大间隔50秒)nscheduler.schedule(this,delay.get(),TimeUnit.MILLISECONDS);n}n}

这样就实现了每隔30s调用一个拉取注册表的任务。

4.3.2定时服务续约任务

privatevoidinitScheduledTasks(){n…nn//开启注册nif(clientConfig.shouldRegisterWithEureka()){nn//todo服务续约定时任务n//续约间隔时间30snintrenewalIntervalInSecs=instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();n//指定client从server更新注册表的最大时间间隔指数(倍数),默认为10nintexpBackOffBound=clientConfig.getHeartbeatExecutorExponentialBackOffBound();nlogger.info(&34;+&34;,renewalIntervalInSecs);nn//Heartbeattimern//todo续约,心跳定时任务nheartbeatTask=newTimedSupervisorTask(n&34;,nscheduler,nheartbeatExecutor,nrenewalIntervalInSecs,nTimeUnit.SECONDS,nexpBackOffBound,nnewHeartbeatThread()n);n//续约定时任务nscheduler.schedule(nheartbeatTask,nrenewalIntervalInSecs,TimeUnit.SECONDS);

每30s执行一次服务续约。直接看下HeartbeatThread类。

privateclassHeartbeatThreadimplementsRunnable{npublicvoidrun(){nif(renew()){nlastSuccessfulHeartbeatTimestamp=System.currentTimeMillis();n}n}n}

走的是renew方法请求服务续约,成功后会更新lastSuccessfulHeartbeatTimestamp字段。

booleanrenew(){nEurekaHttpResponse<InstanceInfo>httpResponse;ntry{nhttpResponse=eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(),instanceInfo.getId(),instanceInfo,null);nlogger.debug(PREFIX+&34;,appPathIdentifier,httpResponse.getStatusCode());n//如果是没有发现该实例信息的话nif(httpResponse.getStatusCode()==Status.NOT_FOUND.getStatusCode()){nREREGISTER_COUNTER.increment();nlogger.info(PREFIX+&34;,appPathIdentifier,instanceInfo.getAppName());nlongtimestamp=instanceInfo.setIsDirtyWithTime();n//todo进行服务注册,如果我们不在配置文件中指定服务初始化就注册该服务,那么服务的注册实际是在这里注册的nbooleansuccess=register();nif(success){ninstanceInfo.unsetIsDirty(timestamp);n}nreturnsuccess;n}nreturnhttpResponse.getStatusCode()==Status.OK.getStatusCode();n}catch(Throwablee){nlogger.error(PREFIX+&34;,appPathIdentifier,e);nreturnfalse;n}n}

很简单,就是调用eurekaTransport.registrationClient.sendHeartBeat方法发送服务续约的请求,如果你实例信息在EurekaServer中不存在的话,就进行服务注册,我们再稍微看下sendHeartBeat方法,里面请求uri就是StringurlPath=“apps/”+appName+‘/’+id;

服务续约请求:PUT请求,path为:apps/{appName}/{instanceId}

4.3.3定时更新Client信息给Server任务

privatevoidinitScheduledTasks(){n…n//开启注册nif(clientConfig.shouldRegisterWithEureka()){nn…n//todo定时更新Client信息给服务端n//InstanceInforeplicatorninstanceInfoReplicator=newInstanceInfoReplicator(nthis,ninstanceInfo,nclientConfig.getInstanceInfoReplicationIntervalSeconds(),n2);//burstSizennstatusChangeListener=newApplicationInfoManager.StatusChangeListener(){n@OverridenpublicStringgetId(){nreturn&34;;n}nn//监听到StatusChangeEvent事件,调用notify方法n@Overridenpublicvoidnotify(StatusChangeEventstatusChangeEvent){nlogger.info(&34;,statusChangeEvent);n//todo通知执行方法,这个方法就是立即向服务端发起注册请求ninstanceInfoReplicator.onDemandUpdate();n}n};nn//向applicationInfoManager中注册状态变化事件监听器nif(clientConfig.shouldOnDemandUpdateStatusChange()){napplicationInfoManager.registerStatusChangeListener(statusChangeListener);n}nn//todo参数默认40sninstanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());n}n…n}

我们看下这个start启动方法:

publicvoidstart(intinitialDelayMs){nif(started.compareAndSet(false,true)){ninstanceInfo.setIsDirty();//forinitialregisternFuturenext=scheduler.schedule(this,initialDelayMs,TimeUnit.SECONDS);nscheduledPeriodicRef.set(next);n}n}

这里有个非常重要的点,调用了实例信息的setIsDirty方法,后面的注释说是为了初始化服务注册。

创建一个延时任务,默认是40s。看看40s执行啥东西。com.netflix.discovery.InstanceInfoReplicator34;Therewasaproblemwiththeinstanceinforeplicator&34;statusChangeListener&34;Sawlocalstatuschangeevent{}&onDemandUpdate方法,检查服务实例信息和服务状态的变化,可能会引起按需注册任务,代码如下:

publicbooleanonDemandUpdate(){nif(rateLimiter.acquire(burstSize,allowedRatePerMinute)){nif(!scheduler.isShutdown()){n//提交nscheduler.submit(newRunnable(){n@Overridenpublicvoidrun(){nlogger.debug(&34;);nnFuturelatestPeriodic=scheduledPeriodicRef.get();nif(latestPeriodic!=null&&!latestPeriodic.isDone()){nlogger.debug(&34;);n//取消定时任务nlatestPeriodic.cancel(false);n}n//todo执行向Server端重新注册的请求nInstanceInfoReplicator.this.run();n}n});nreturntrue;n}else{nlogger.warn(&34;);nreturnfalse;n}n}else{nlogger.warn(&34;);nreturnfalse;n}n}

InstanceInfoReplicatorrun方法检查服务实例信息和服务状态的变化,并在服务实例信息和服务状态发生变化的情况下向EurekaServer发起重新注册的请求,为了防止重新执行run方法,onDemandUpdate方法还会取消执行上次已经提交且未完成的run方法,执行最新的按需注册任务。

4.4总结

服务注册的时机

Client提交register()请求的情况有三种:

在应用启动时就可以直接进行register(),不过,需要提前在配置文件中配置在renew时,如果server端返回的是NOT_FOUND,则提交register()当Client的配置信息发生了变更,则Client提交register()

Client实例化

EurekaClient实例化的时候有几个重要步骤,分别如下:

全量拉取注册表信息,放入自己本地注册表中。创建定时任务,定时服务续约任务,默认是30s,定时更新客户端注册表信息,默认是30s,定时更新Client信息给Server端,重新服务注册,默认是40s。

参考文章

eureka-0.10.11源码(注释)springcloud-source-study学习github地址Eureka源码解析SpringCloud技术栈系列文章

OK,关于现在任务网站源码分享和任务app源码的内容到此结束了,希望对大家有所帮助。

以上就是小编对于现在任务网站源码分享,任务pp源码问题和相关问题的解答了,现在任务网站源码分享,任务pp源码的问题希望对你有用!

文章来自互联网,只做分享使用。发布者:酷知号,转转请注明出处:https://www.kuzhihao.com/article/352370.html

(0)
上一篇 2023年8月6日 15:35
下一篇 2023年8月6日 15:35

相关推荐