Eureka 源码解析 —— 应用实例注册发现(七)之增量获取
摘要: 原创出处 http://www.iocoder.cn/Eureka/instance-registry-fetch-delta/ 「芋道源码」欢迎转载,保留摘要,谢谢!
本文主要基于 Eureka 1.8.X 版本
- 1. 概述
- 2. 应用集合一致性哈希码
- 2.1 计算公式
- 2.2 合理性
- 3. Eureka-Client 发起增量获取
- 3.1 合并应用集合
- 4. Eureka-Server 接收全量获取
- 3.1 接收全量获取请求
- 3.2 最近租约变更记录队列
- 3.3 缓存读取
- 666. 彩蛋
1. 概述
本文主要分享 Eureka-Client 向 Eureka-Server 获取增量注册信息的过程。
前置阅读:《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》
FROM 《深度剖析服务发现组件Netflix Eureka》
Eureka-Client 获取注册信息,分成全量获取和增量获取。默认配置下,Eureka-Client 启动时,首先执行一次全量获取进行本地缓存注册信息,而后每 30 秒增量获取刷新本地缓存( 非“正常”情况下会是全量获取 )。
本文重点在于增量获取。
推荐 Spring Cloud 书籍:
- 请支持正版。下载盗版,等于主动编写低级 BUG 。
- 程序猿DD —— 《Spring Cloud微服务实战》
- 周立 —— 《Spring Cloud与Docker微服务架构实战》
- 两书齐买,京东包邮。
2. 应用集合一致性哈希码
Applications.appsHashCode
,应用集合一致性哈希码。
增量获取注册的应用集合( Applications ) 时,Eureka-Client 会获取到:
- Eureka-Server 近期变化( 注册、下线 )的应用集合
- Eureka-Server 应用集合一致性哈希码
Eureka-Client 将变化的应用集合和本地缓存的应用集合进行合并后进行计算本地的应用集合一致性哈希码。若两个哈希码相等,意味着增量获取成功;若不相等,意味着增量获取失败,Eureka-Client 重新和 Eureka-Server 全量获取应用集合。
Eureka 比较应用集合一致性哈希码,和日常我们通过哈希码比较两个对象是否相等类似。
2.1 计算公式
appsHashCode = ${status}_${count}_
使用每个应用实例状态(
status
) + 数量(count
)拼接出一致性哈希码。若数量为 0 ,该应用实例状态不进行拼接。状态以字符串大小排序。举个例子,8 个 UP ,0 个 DOWN ,则
appsHashCode = UP_8_
。8 个 UP ,2 个 DOWN ,则appsHashCode = DOWN_2_UP_8_
。实现代码如下:
// Applications.java public String getReconcileHashCode() {// 计数集合 key:应用实例状态TreeMap<String, AtomicInteger> instanceCountMap = new TreeMap<String, AtomicInteger>();populateInstanceCountMap(instanceCountMap);// 计算 hashcodereturn getReconcileHashCode(instanceCountMap); }
调用
#populateInstanceCountMap()
方法,计算每个应用实例状态的数量。实现代码如下:// Applications.java public void populateInstanceCountMap(Map<String, AtomicInteger> instanceCountMap) {for (Application app : this.getRegisteredApplications()) {for (InstanceInfo info : app.getInstancesAsIsFromEureka()) {// 计数AtomicInteger instanceCount = instanceCountMap.computeIfAbsent(info.getStatus().name(),k -> new AtomicInteger(0));instanceCount.incrementAndGet();}} }public List<Application> getRegisteredApplications() {return new ArrayList<Application>(this.applications); }// Applications.java public List<InstanceInfo> getInstancesAsIsFromEureka() {synchronized (instances) {return new ArrayList<InstanceInfo>(this.instances);} }
- 计数那块代码,使用 Integer 即可,无需使用 AtomicInteger 。
调用
#getReconcileHashCode()
方法,计算hashcode
。实现代码如下:public static String getReconcileHashCode(Map<String, AtomicInteger> instanceCountMap) {StringBuilder reconcileHashCode = new StringBuilder(75);for (Map.Entry<String, AtomicInteger> mapEntry : instanceCountMap.entrySet()) {reconcileHashCode.append(mapEntry.getKey()).append(STATUS_DELIMITER) // status.append(mapEntry.getValue().get()).append(STATUS_DELIMITER); // count}return reconcileHashCode.toString(); }
2.2 合理性
本小节,建议你理解完全文后,再回到此处
本小节,建议你理解完全文后,再回到此处
本小节,建议你理解完全文后,再回到此处
笔者刚看完应用集合一致性哈希算法的计算公式,处于一脸懵逼的状态。这么精简的方式真的能够校验出数据的一致性么?不晓得有多少读者跟笔者有一样的疑惑。下面我们来论证该算法的合理性( 一本正经的胡说八道 )。
一致性哈希值通过状态 + 数量来计算,那么是不是可能状态总数是一样多,实际分布在不同的应用?那么我们列举模型如下:
UP | |
---|---|
应用A | m |
应用B | n |
如果此时应用A 下线了 c 个原应用实例,应用B 注册了 c 个信应用实例,那么处于 UP 状态的数量仍然是 m + n 个。
- 正常情况下,Eureka-Client 从 Eureka-Server 获取到完整的增量变化并合并,此时应用情况如下表格所示,两者是一致的,一致性哈希算法合理。
UP (server) | UP (client) | |
---|---|---|
应用A | m - c | m - c |
应用B | n + c | n + c |
- 异常情况下【1】,变更记录队列全部过期。那 Eureka-Client 从 Eureka-Server 获取到空的增量变化并合并,此时应用情况如下表格所示,两者应用是不相同的, 一致性哈希值却是相等的,一致性哈希算法不合理。
UP (server) | UP (client) | |
---|---|---|
应用A | m - c | m |
应用B | n + c | n |
- 异常情况下【2】,变更记录队列部分过期,例如应用A 和 应用B 都剩余 w 条变更记录。那 Eureka-Client 从 Eureka-Server 获取到部分的增量变化并合并,两者应用是不相同的,此时应用情况如下表格所示,一致性哈希值却是相等的,一致性哈希算法不合理。
UP (server) | UP (client) | |
---|---|---|
应用A | m - c | m - w |
应用B | n + c | n + w |
What ? 从异常情况【1】【2】可以看到,一致性哈希算法竟然是不合理的,那么我们手动来做一次最精简的实验。实验如下:
- 模拟场景:异常情况【1】,m = n = c = 1 。简单粗暴。
- 特别配置
eureka.retentionTimeInMSInDeltaQueue = 1
,变更记录队列每条记录存活时长 1 ms。用以实现 Eureka-Client 请求不到完整的增量变化。eureka.deltaRetentionTimerIntervalInMs = 1
,变更记录队列每条记录过期定时任务执行频率 1 ms。用以实现 Eureka-Client 请求不到完整的增量变化。eureka.shouldUseReadOnlyResponseCache = false
,禁用响应缓存的只读缓存。用以避免等待缓存刷新。eureka.waitTimeInMsWhenSyncEmpty = 1
,
- 实验过程
- 00:00 启动 Eureka-Server
- 00:30 启动应用A ,向 Eureka-Server 注册
- 01:00 启动 Eureka-Client ,向 Eureka-Server 获取注册信息,等待获取到应用A
- 01:30 关闭应用A 。立即启动应用B ,向 Eureka-Server 注册
- 等待 5 分钟,Eureka-Client 无法获取到应用B
- 此时应用情况如下表格所示,两者应用是不相同的,一致性哈希值却是相等的,一致性哈希算法不合理。
UP (server) | UP (client) | |
---|---|---|
应用A | 0 | 1 |
应用B | 1 | 0 |
🙂结论🙂
当然排除掉特别极端的场景,Eureka-Client 从 Eureka-Server 因为网络异常导致一直同步不到增量变化,又恰好应用关闭和开启满足状态统计数量。另外,变更记录队列记录过期时长为 300 秒,增量获取频率为 30 秒,获取的次数有 10 次左右。所以,应用集合一致性哈希码在绝大多数场景是合理的。笔者的YY,解决这个极小场景有如下方式:
- 第一种,修改计算公式
appsHashCode = MD5(${app_name}_${instance_id}_${status}_${count}_)
,增加对应用名和应用实例编号敏感。 - 第二种,每 N 分钟进行一次全量获取注册信息。
ps :笔者怀着忐忑的心写完了这个小节,如果有不合理的地方,又或者有不同观点的胖友,欢迎一起探讨。谢谢。
TODO[0027][反思]:应用集合一致性哈希算法。
3. Eureka-Client 发起增量获取
在 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「2.4 发起获取注册信息」 里,调用 DiscoveryClient#getAndUpdateDelta(...)
方法,增量获取注册信息,并刷新本地缓存,实现代码如下:
1: private void getAndUpdateDelta(Applications applications) throws Throwable {2: long currentUpdateGeneration = fetchRegistryGeneration.get();3: 4: // 增量获取注册信息5: Applications delta = null;6: EurekaHttpResponse<Applications> httpResponse = eurekaTransport.queryClient.getDelta(remoteRegionsRef.get());7: if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {8: delta = httpResponse.getEntity();9: } 10: 11: if (delta == null) { 12: // 增量获取为空,全量获取 13: logger.warn("The server does not allow the delta revision to be applied because it is not safe. " 14: + "Hence got the full registry."); 15: getAndStoreFullRegistry(); 16: } else if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)) { 17: logger.debug("Got delta update with apps hashcode {}", delta.getAppsHashCode()); 18: String reconcileHashCode = ""; 19: if (fetchRegistryUpdateLock.tryLock()) { 20: try { 21: // 将变化的应用集合和本地缓存的应用集合进行合并 22: updateDelta(delta); 23: // 计算本地的应用集合一致性哈希码 24: reconcileHashCode = getReconcileHashCode(applications); 25: } finally { 26: fetchRegistryUpdateLock.unlock(); 27: } 28: } else { 29: logger.warn("Cannot acquire update lock, aborting getAndUpdateDelta"); 30: } 31: // There is a diff in number of instances for some reason 32: if (!reconcileHashCode.equals(delta.getAppsHashCode()) // 一致性哈希值不相等 33: || clientConfig.shouldLogDeltaDiff()) { // 34: reconcileAndLogDifference(delta, reconcileHashCode); // this makes a remoteCall 35: } 36: } else { 37: logger.warn("Not updating application delta as another thread is updating it already"); 38: logger.debug("Ignoring delta update with apps hashcode {}, as another thread is updating it already", delta.getAppsHashCode()); 39: } 40: } |
第 4 至 9 行 :请求增量获取注册信息,实现代码如下:
// AbstractJerseyEurekaHttpClient.java @Override public EurekaHttpResponse<Applications> getDelta(String... regions) {return getApplicationsInternal("apps/delta", regions); }
- 调用
AbstractJerseyEurekaHttpClient#getApplicationsInternal(...)
方法,GET 请求 Eureka-Server 的apps/detla
接口,参数为regions
,返回格式为 JSON ,实现增量获取注册信息。
- 调用
第 11 至 15 行 :增量获取失败,调用
#getAndStoreFullRegistry()
方法,全量获取注册信息,并设置到本地缓存。该方法在 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「2.4.1 全量获取注册信息,并设置到本地缓存」 有详细解析。第 16 至 35 行 :处理增量获取的结果。
- 第 16 行 :TODO[0025] :并发更新的情况???
- 第 19 行 :TODO[0025] :并发更新的情况???
- 第 21 行 :调用
#updateDelta(...)
方法,将变化的应用集合和本地缓存的应用集合进行合并。 - 第 31 至 35 行 :一致性哈希值不相等,调用
#reconcileAndLogDifference()
方法,全量获取注册信息,并设置到本地缓存,和#getAndStoreFullRegistry()
基本类似。- 第 33 行 :配置
eureka.printDeltaFullDiff
,是否打印增量和全量差异。默认值 :false
。从目前代码实现上来看,暂时没有生效。注意 :开启该参数会导致每次增量获取后又发起全量获取,不要开启。
- 第 33 行 :配置
3.1 合并应用集合
调用 #updateDelta(...)
方法,将变化的应用集合和本地缓存的应用集合进行合并。实现代码如下:
1: private void updateDelta(Applications delta) {2: int deltaCount = 0;3: for (Application app : delta.getRegisteredApplications()) { // 循环增量(变化)应用集合4: for (InstanceInfo instance : app.getInstances()) {5: Applications applications = getApplications();6: // TODO[0009]:RemoteRegionRegistry7: String instanceRegion = instanceRegionChecker.getInstanceRegion(instance);8: if (!instanceRegionChecker.isLocalRegion(instanceRegion)) {9: Applications remoteApps = remoteRegionVsApps.get(instanceRegion); 10: if (null == remoteApps) { 11: remoteApps = new Applications(); 12: remoteRegionVsApps.put(instanceRegion, remoteApps); 13: } 14: applications = remoteApps; 15: } 16: 17: ++deltaCount; 18: if (ActionType.ADDED.equals(instance.getActionType())) { // 添加 19: Application existingApp = applications.getRegisteredApplications(instance.getAppName()); 20: if (existingApp == null) { 21: applications.addApplication(app); 22: } 23: logger.debug("Added instance {} to the existing apps in region {}", instance.getId(), instanceRegion); 24: applications.getRegisteredApplications(instance.getAppName()).addInstance(instance); 25: } else if (ActionType.MODIFIED.equals(instance.getActionType())) { // 修改 26: Application existingApp = applications.getRegisteredApplications(instance.getAppName()); 27: if (existingApp == null) { 28: applications.addApplication(app); 29: } 30: logger.debug("Modified instance {} to the existing apps ", instance.getId()); 31: 32: applications.getRegisteredApplications(instance.getAppName()).addInstance(instance); 33: } else if (ActionType.DELETED.equals(instance.getActionType())) { // 删除 34: Application existingApp = applications.getRegisteredApplications(instance.getAppName()); 35: if (existingApp == null) { 36: applications.addApplication(app); 37: } 38: logger.debug("Deleted instance {} to the existing apps ", instance.getId()); 39: applications.getRegisteredApplications(instance.getAppName()).removeInstance(instance); 40: } 41: } 42: } 43: logger.debug("The total number of instances fetched by the delta processor : {}", deltaCount); 44: 45: getApplications().setVersion(delta.getVersion()); 46: // 过滤、打乱应用集合 47: getApplications().shuffleInstances(clientConfig.shouldFilterOnlyUpInstances()); 48: 49: // TODO[0009]:RemoteRegionRegistry 50: for (Applications applications : remoteRegionVsApps.values()) { 51: applications.setVersion(delta.getVersion()); 52: applications.shuffleInstances(clientConfig.shouldFilterOnlyUpInstances()); 53: } 54: } |
第 6 至 15 行 :TODO[0009]:RemoteRegionRegistry
第 18 至 24 行 :添加( ADDED )应用实例时,调用
Application#addInstance(...)
方法,实现代码如下:// Application.java public void addInstance(InstanceInfo i) {// 添加到 应用实例映射instancesMap.put(i.getId(), i);synchronized (instances) {// 移除原有实例instances.remove(i);// 添加新实例instances.add(i);// 设置 isDirty ,目前只用于 `#toString()` 方法打印,无业务逻辑isDirty = true;} }// InstanceInfo.java @Override public int hashCode() { // 只使用 ID 计算 hashcodeString id = getId();return (id == null) ? 31 : (id.hashCode() + 31); }@Override public boolean equals(Object obj) { // 只对比 IDif (this == obj) {return true;}if (obj == null) {return false;}if (getClass() != obj.getClass()) {return false;}InstanceInfo other = (InstanceInfo) obj;String id = getId();if (id == null) {if (other.getId() != null) {return false;}} else if (!id.equals(other.getId())) {return false;}return true; }
第 25 至 32 行 :修改( MODIFIED )应用实例时,同样调用
Application#addInstance(...)
方法。第 33 至 40 行 :删除( DELETED )应用实例时,调用
Application#removeInstance(...)
方法,实现代码如下:public void removeInstance(InstanceInfo i) {removeInstance(i, true); }private void removeInstance(InstanceInfo i, boolean markAsDirty) {// 移除 应用实例映射instancesMap.remove(i.getId());synchronized (instances) {// 移除 应用实例instances.remove(i);if (markAsDirty) {// 设置 isDirty ,目前只用于 `#toString()` 方法打印,无业务逻辑isDirty = true;}} }
第 47 行 :调用
Applications#shuffleInstances(...)
方法,根据配置eureka.shouldFilterOnlyUpInstances = true
( 默认值 :true
) 过滤只保留状态为开启( UP )的应用实例,并随机打乱应用实例顺序。打乱后,实现调用应用服务的随机性。代码比较易懂,点击链接查看方法实现。第 49 至 53 行 :TODO[0009]:RemoteRegionRegistry
4. Eureka-Server 接收全量获取
3.1 接收全量获取请求
com.netflix.eureka.resources.ApplicationsResource
,处理所有应用的请求操作的 Resource ( Controller )。
接收增量获取请求,映射 ApplicationsResource#getContainers()
方法。
- 和 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「3.1 接收全量获取请求」 类似,就不重复啰嗦啦。
- 点击 链接 查看该方法的带中文注释代码。
3.2 最近租约变更记录队列
AbstractInstanceRegistry.recentlyChangedQueue
,最近租约变更记录队列。实现代码如下:
// AbstractInstanceRegistry.java /** * 最近租约变更记录队列 */ private ConcurrentLinkedQueue<RecentlyChangedItem> recentlyChangedQueue = new ConcurrentLinkedQueue<RecentlyChangedItem>();/** * 最近租约变更记录 */ private static final class RecentlyChangedItem {/*** 最后更新时间戳*/private long lastUpdateTime;/*** 租约*/private Lease<InstanceInfo> leaseInfo;public RecentlyChangedItem(Lease<InstanceInfo> lease) {this.leaseInfo = lease;lastUpdateTime = System.currentTimeMillis();}public long getLastUpdateTime() {return this.lastUpdateTime;}public Lease<InstanceInfo> getLeaseInfo() {return this.leaseInfo;} } |
当应用实例注册、下线、状态变更时,创建最近租约变更记录( RecentlyChangedItem ) 到队列。
后台任务定时顺序扫描队列,当
lastUpdateTime
超过一定时长后进行移除。实现代码如下:// AbstractInstanceRegistry.java this.deltaRetentionTimer.schedule(getDeltaRetentionTask(),serverConfig.getDeltaRetentionTimerIntervalInMs(),serverConfig.getDeltaRetentionTimerIntervalInMs());private TimerTask getDeltaRetentionTask() {return new TimerTask() {@Overridepublic void run() {Iterator<RecentlyChangedItem> it = recentlyChangedQueue.iterator();while (it.hasNext()) {if (it.next().getLastUpdateTime() < System.currentTimeMillis() - serverConfig.getRetentionTimeInMSInDeltaQueue()) {it.remove();} else {break;}}}}; }
- 配置
eureka.deltaRetentionTimerIntervalInMs
, 移除队列里过期的租约变更记录的定时任务执行频率,单位:毫秒。默认值 :30 * 1000 毫秒。 - 配置
eureka.retentionTimeInMSInDeltaQueue
,租约变更记录过期时长,单位:毫秒。默认值 : 3 * 60 * 1000 毫秒。
- 配置
3.3 缓存读取
在 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「3.3 缓存读取」 里,在 #generatePayload()
方法里,调用 AbstractInstanceRegistry#getApplicationDeltas(...)
方法,获取近期变化的应用集合,实现代码如下:
// AbstractInstanceRegistry.java1: public Applications getApplicationDeltas() {2: // 添加 增量获取次数 到 监控3: GET_ALL_CACHE_MISS_DELTA.increment();4: // 初始化 变化的应用集合5: Applications apps = new Applications();6: apps.setVersion(responseCache.getVersionDelta().get());7: Map<String, Application> applicationInstancesMap = new HashMap<String, Application>();8: try {9: // 获取写锁10: write.lock();11: // 获取 最近租约变更记录队列12: Iterator<RecentlyChangedItem> iter = this.recentlyChangedQueue.iterator();13: logger.debug("The number of elements in the delta queue is :" + this.recentlyChangedQueue.size());14: // 拼装 变化的应用集合15: while (iter.hasNext()) {16: Lease<InstanceInfo> lease = iter.next().getLeaseInfo();17: InstanceInfo instanceInfo = lease.getHolder();18: Object[] args = {instanceInfo.getId(), instanceInfo.getStatus().name(), instanceInfo.getActionType().name()};19: logger.debug("The instance id %s is found with status %s and actiontype %s", args);20: Application app = applicationInstancesMap.get(instanceInfo.getAppName());21: if (app == null) {22: app = new Application(instanceInfo.getAppName());23: applicationInstancesMap.put(instanceInfo.getAppName(), app);24: apps.addApplication(app);25: }26: app.addInstance(decorateInstanceInfo(lease));27: }28: 29: // TODO[0009]:RemoteRegionRegistry30: boolean disableTransparentFallback = serverConfig.disableTransparentFallbackToOtherRegion();31: if (!disableTransparentFallback) {32: Applications allAppsInLocalRegion = getApplications(false);33: 34: for (RemoteRegionRegistry remoteRegistry : this.regionNameVSRemoteRegistry.values()) {35: Applications applications = remoteRegistry.getApplicationDeltas();36: for (Application application : applications.getRegisteredApplications()) {37: Application appInLocalRegistry =38: allAppsInLocalRegion.getRegisteredApplications(application.getName());39: if (appInLocalRegistry == null) {40: apps.addApplication(application);41: }42: }43: }44: }45: 46: // 获取全量应用集合,通过它计算一致性哈希值47: Applications allApps = getApplications(!disableTransparentFallback);48: apps.setAppsHashCode(allApps.getReconcileHashCode());49: return apps;50: } finally {51: write.unlock();52: }53: } |
- 第 2 至 3 行 :添加增量获取次数到监控。配合 Netflix Servo 实现监控信息采集。
- 第 4 行 :初始化变化( 增量 )的应用集合(
apps
)。 - 第 9 行 :获取写锁。在 《Eureka源码解析 —— 应用实例注册发现 (九)之岁月是把萌萌的读写锁》 详细解析。
- 第 11 至 13 行 :获取最近租约变更记录队列(
最近租约变更记录队列
)。 - 第 14 至 27 行 :拼装变化的应用集合(
apps
)。 - 第 29 至 44 行 :TODO[0009]:RemoteRegionRegistry
- 第 46 至 48 行 :调用
#getApplications(...)
方法,获取全量应用集合(allApps
),在 《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》「3.3.1 获得注册的应用集合」 有详细解析。后通过allApps
计算一致性哈希值。通过这个全量应用集合的哈希值,Eureka-Client 获取到增量应用集合并合并后,就可以比对啦。 - 第 51 行 :释放写锁。
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- js常用方法
截取字符串...
2024/4/15 15:09:30 - 面试流程以及技巧介绍
面试流程大纲...
2024/4/15 15:09:28 - Python--类属性,实例属性,类方法,静态方法
类属性&实例属性 类属性 类属性属于所有对象共有的,也就是所有对象都会使用同一个类属性,类属性定义在类的内部。 类属性可以直接通过类名调用,修改类属性则所有对象使用时就都会改变。 class Student:name = chiruno#类属性height = 1.56obj1 = Student()# 实例对象 ob…...
2024/4/17 14:47:35 - C++的volatile关键字理解
C++的volatile关键字 1)volatile语义是易变的,该修饰的变量可能会突然地、出乎意料地被改变; 2)禁止编译器优化; 如使得被修饰变量需要每次从内存中读取。因为编译器优化过程,认为这个变量没有被更改的可能性的时候,就会优化成每次从CPU寄存器中读取,导致中断函数的改变…...
2024/4/15 15:09:26 - Java面向对象:new关键字,堆和栈;
1.new关键字分析Cat one = new Cat();实例化对象的过程可以分为两部分:(1)声明对象:Cat one(2)实例化对象 new Cat()声明对象:Cat one是在内存的栈空间中开辟了一块区域,取名叫做one;但在这个时候还不是一个有效的对象,此时one还是个空的,里面什么东西都没有;还不能…...
2024/4/15 17:58:59 - Java 虚方法实例
变量实际引用的是子类对象,而子类对象中覆盖(也可以说成是重写)了父类的方法,这时父类对象调用的是子类中的方法,这时候就成为虚方法调用 下面一个小栗子 class TestVirtualInvoke{static void doStuff(Shape s ){ s.draw();} public static void main( ){ Circle c = new…...
2024/4/15 17:58:58 - leetcode_114_二叉树展开
题目:给定一个二叉树,原地将它展开为一个单链表。例如,给定二叉树1/ \2 5/ \ \ 3 4 6 将其展开为:1\2\3\4\5\6来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list 著作权归领扣网络所有。商业转载请联系官方授权,…...
2024/4/21 7:00:57 - OAuth2(二)——实现
目录OAuth客户端向授权服务器注册 OAuth 客户端使用授权码许可类型获取令牌发送授权请求OAuth2的4种授权模式授权码模式(authorization code)隐式授权模式/简化模式(implicit)优点:缺点:适用场景密码模式(resource owner password credentials)优点:缺点:应用场景:客…...
2024/4/15 17:58:56 - 01Java基础语法-30. 方法的重载(Overload)
方法重载概念 方法重载指同一个类中定义的多个方法之间的关系,满足下列条件的多个方法相互构成重载多个方法在同一个类中 多个方法具有相同的方法名 多个方法的参数不相同,类型不同或者数量不同注意:重载仅对应方法的定义,与方法的调用无关,调用方式参照标准格式重载仅针对…...
2024/4/15 9:59:22 - 2.2线性表的顺序表示与实现(严蔚敏)
线性表的动态分配顺序存储结构 // 函数结果运行状态 #define OK 1 #define ERROR 0 #define OVERFLOW -2#define LIST_INIT_SIZE 100 //线性表存储空间的初始分配量 #define LISTINCREMENT 10 //线性表存储空间的分配增量typedef float ElemType; typedef int Status;typedef s…...
2024/4/15 17:58:55 - 第二周总结ACM
小周总结 这周是对上周所学的两个算法加以巩固的一周,上周我学习了两个算法分别是(最小生成树和并查集),这两个算法的简单使用还是没有问题的,并查集提升中包含了一个种类并查集,对于它我并没有很好地掌握,下周之前争取拿下。 这周在做题中时常碰到别的算法与该算法结合…...
2024/4/15 17:58:53 - java 项目开发工具分享,一键生成java web基础代码
java 项目开发工具分享,一键生成java web基础代码 链接:https://pan.baidu.com/s/1OdjR9zfdVhJYbLc2LP8ERA 提取码:xyih...
2024/4/15 17:58:53 - 【OpenCV】图像边界填充、阈值操作、平滑操作
文章目录一、边界填充1.1 为什么要边界填充1.2 边界填充cv2.copyMakeBorder()1.3 边界填充cv2.borderInterpolate()二、阈值化2.1 固定阈值cv2.threshold()2.2 自适应阈值cv2.adaptiveThreshold()二、平滑/模糊2.1 均值滤波2.1.1 简单均值滤波cv.blur()2.1.2 方框型滤波cv.…...
2024/4/15 4:26:24 - NOIP2010普及组初赛难点整理
选择题设 XXXXXX、YYY、ZZZ 分别代表三进制下的一位数字,若等式 XY+ZX=XYXXY+ZX=XYXXY+ZX=XYX在三进制下成立,那么同样在三进制下,等式 XYZX=()XY\times ZX=()XYZX=()也成立。 A. XYZXYZXYZ B. YXZYXZYXZ C. ZXYZXYZXY D. XZYXZYXZY 【解析】由XY+ZX=XYXXY+ZX=XYXXY+ZX=XYX可…...
2024/4/15 4:45:43 - Java专题 lambda表达式以及Stream的总结及使用 +二者综合案例
直接看代码和注释 基本写的比较详细了,还有自己的理解也有//lambda 表达式//针对于接口只有一个方法的做法//是匿名内部类的一种简写方法//语法格式 // 方法的参数列表->{方法体} // ()->{方法体}:没有返回值,方法中没有参数 // (x)->{方法体}:无返回值,如果是一…...
2024/4/15 17:58:49 - Tomcat 源码构建
下载源码准备⼯作 解压 tar.gz 压缩包,得到⽬录 apache-tomcat-8.5.50-src 进⼊ apache-tomcat-8.5.50-src ⽬录,创建⼀个pom.xml⽂件,⽂件内容如下 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/…...
2024/4/17 5:28:19 - 【matplotlib】解决子图排列+子图嵌套的问题
子图嵌套的问题大家通常都会用fig.add_axes在一个fig对象上进行操作。但如果我们有多个子图,但只想在一个子图上再嵌套一个子图呢?我查了很多地方都没有找到解决方法。于是求助于matplotlib gallery,果然有参考的例子,只需要调用inset_axes的操作就可以在ax对象上操作了。 下…...
2024/4/15 15:09:39 - 基于CQT和PCP的和弦识别算法
基于CQT和PCP的和弦识别算法原理声学基础音乐理论基础音高十二平均律音程与和弦时频转换PCP特征向量过程源代码结语 原理 声学基础 声音是物体振动时产生的波动现象,通过介质传达,最终由人或动物的听觉器官感知 声音的特征特性可以通过音量、音调、音色三个基本要素来实现 音…...
2024/4/21 7:11:54 - 一个快速构造GAN的教程:如何用pytorch构造DCGAN
在本教程中,我们将在PyTorch中构建一个简单的DCGAN,并在手写数据集上对它进行训练。我们将讨论PyTorch DataLoader,以及如何使用它将图像数据提供给PyTorch神经网络进行训练。PyTorch是本教程的重点,所以我假设您熟悉GAN的工作方式。 要求python版本为3.7或更高。 PyTo…...
2024/4/23 18:35:21 - German Collegiate Programming Contest 2019
Problem H: Jazz Enthusiast 时间限制: 1 Sec 内存限制: 128 MB 提交: 475 解决: 175 [提交] [命题人:外部导入] 题目描述 Kai is listening to his favourite jazz playlist. He likes to turn on crossfading between songs, so during the last seconds of a song, it wil…...
2024/4/17 5:28:19
最新文章
- 网络通信安全
一、网络通信安全基础 TCP/IP协议简介 TCP/IP体系结构、以太网、Internet地址、端口 TCP/IP协议简介如下:(from文心一言) TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议࿰…...
2024/4/28 18:00:18 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/3/20 10:50:27 - Web CSS笔记3
一、边框弧度 使用它你就可以制作盒子边框圆角 border-radius:1个值四个圆角值相同2个值 第一个值为左上角与右下角,第二个值为右上角与左下角3个值第一个值为左上角, 第二个值为右上角和左下角,第三个值为右下角4个值 左上角,右…...
2024/4/26 1:47:48 - MATLAB绘制堆叠填充图--巧用句柄
MATLAB绘制堆叠填充图–巧用句柄 目录 MATLAB绘制堆叠填充图--巧用句柄1. 主要原理讲解1.1 主要函数1.2 句柄原理 2. 绘图示例2.1 准备数据2.2 绘制堆叠填充图-使用句柄控制图形属性2.3 设置填充颜色和样式2.4 添加标题和标签2.5 绘图效果 3. 结语 堆叠填充图是一种常见的数据可…...
2024/4/25 6:57:41 - 416. 分割等和子集问题(动态规划)
题目 题解 class Solution:def canPartition(self, nums: List[int]) -> bool:# badcaseif not nums:return True# 不能被2整除if sum(nums) % 2 ! 0:return False# 状态定义:dp[i][j]表示当背包容量为j,用前i个物品是否正好可以将背包填满ÿ…...
2024/4/28 4:04:40 - 【Java】ExcelWriter自适应宽度工具类(支持中文)
工具类 import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet;/*** Excel工具类** author xiaoming* date 2023/11/17 10:40*/ public class ExcelUti…...
2024/4/28 12:01:04 - Spring cloud负载均衡@LoadBalanced LoadBalancerClient
LoadBalance vs Ribbon 由于Spring cloud2020之后移除了Ribbon,直接使用Spring Cloud LoadBalancer作为客户端负载均衡组件,我们讨论Spring负载均衡以Spring Cloud2020之后版本为主,学习Spring Cloud LoadBalance,暂不讨论Ribbon…...
2024/4/28 16:34:55 - TSINGSEE青犀AI智能分析+视频监控工业园区周界安全防范方案
一、背景需求分析 在工业产业园、化工园或生产制造园区中,周界防范意义重大,对园区的安全起到重要的作用。常规的安防方式是采用人员巡查,人力投入成本大而且效率低。周界一旦被破坏或入侵,会影响园区人员和资产安全,…...
2024/4/27 12:24:46 - VB.net WebBrowser网页元素抓取分析方法
在用WebBrowser编程实现网页操作自动化时,常要分析网页Html,例如网页在加载数据时,常会显示“系统处理中,请稍候..”,我们需要在数据加载完成后才能继续下一步操作,如何抓取这个信息的网页html元素变化&…...
2024/4/28 12:01:03 - 【Objective-C】Objective-C汇总
方法定义 参考:https://www.yiibai.com/objective_c/objective_c_functions.html Objective-C编程语言中方法定义的一般形式如下 - (return_type) method_name:( argumentType1 )argumentName1 joiningArgument2:( argumentType2 )argumentName2 ... joiningArgu…...
2024/4/28 12:01:03 - 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】
👨💻博客主页:花无缺 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5713-洛谷团队系统【入门2分支结构】🌏题目描述🌏输入格…...
2024/4/28 12:01:03 - 【ES6.0】- 扩展运算符(...)
【ES6.0】- 扩展运算符... 文章目录 【ES6.0】- 扩展运算符...一、概述二、拷贝数组对象三、合并操作四、参数传递五、数组去重六、字符串转字符数组七、NodeList转数组八、解构变量九、打印日志十、总结 一、概述 **扩展运算符(...)**允许一个表达式在期望多个参数࿰…...
2024/4/28 16:07:14 - 摩根看好的前智能硬件头部品牌双11交易数据极度异常!——是模式创新还是饮鸩止渴?
文 | 螳螂观察 作者 | 李燃 双11狂欢已落下帷幕,各大品牌纷纷晒出优异的成绩单,摩根士丹利投资的智能硬件头部品牌凯迪仕也不例外。然而有爆料称,在自媒体平台发布霸榜各大榜单喜讯的凯迪仕智能锁,多个平台数据都表现出极度异常…...
2024/4/27 21:08:20 - Go语言常用命令详解(二)
文章目录 前言常用命令go bug示例参数说明 go doc示例参数说明 go env示例 go fix示例 go fmt示例 go generate示例 总结写在最后 前言 接着上一篇继续介绍Go语言的常用命令 常用命令 以下是一些常用的Go命令,这些命令可以帮助您在Go开发中进行编译、测试、运行和…...
2024/4/28 9:00:42 - 用欧拉路径判断图同构推出reverse合法性:1116T4
http://cplusoj.com/d/senior/p/SS231116D 假设我们要把 a a a 变成 b b b,我们在 a i a_i ai 和 a i 1 a_{i1} ai1 之间连边, b b b 同理,则 a a a 能变成 b b b 的充要条件是两图 A , B A,B A,B 同构。 必要性显然࿰…...
2024/4/27 18:40:35 - 【NGINX--1】基础知识
1、在 Debian/Ubuntu 上安装 NGINX 在 Debian 或 Ubuntu 机器上安装 NGINX 开源版。 更新已配置源的软件包信息,并安装一些有助于配置官方 NGINX 软件包仓库的软件包: apt-get update apt install -y curl gnupg2 ca-certificates lsb-release debian-…...
2024/4/28 4:14:21 - Hive默认分割符、存储格式与数据压缩
目录 1、Hive默认分割符2、Hive存储格式3、Hive数据压缩 1、Hive默认分割符 Hive创建表时指定的行受限(ROW FORMAT)配置标准HQL为: ... ROW FORMAT DELIMITED FIELDS TERMINATED BY \u0001 COLLECTION ITEMS TERMINATED BY , MAP KEYS TERMI…...
2024/4/27 13:52:15 - 【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法
文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中,传感器和控制器产生大量周…...
2024/4/27 13:38:13 - --max-old-space-size=8192报错
vue项目运行时,如果经常运行慢,崩溃停止服务,报如下错误 FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory 因为在 Node 中,通过JavaScript使用内存时只能使用部分内存(64位系统&…...
2024/4/28 12:00:58 - 基于深度学习的恶意软件检测
恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞,例如可以被劫持的合法软件(例如浏览器或 Web 应用程序插件)中的错误。 恶意软件渗透可能会造成灾难性的后果,包括数据被盗、勒索或网…...
2024/4/28 12:00:58 - JS原型对象prototype
让我简单的为大家介绍一下原型对象prototype吧! 使用原型实现方法共享 1.构造函数通过原型分配的函数是所有对象所 共享的。 2.JavaScript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象,所以我们也称为原型对象…...
2024/4/27 22:51:49 - C++中只能有一个实例的单例类
C中只能有一个实例的单例类 前面讨论的 President 类很不错,但存在一个缺陷:无法禁止通过实例化多个对象来创建多名总统: President One, Two, Three; 由于复制构造函数是私有的,其中每个对象都是不可复制的,但您的目…...
2024/4/28 7:31:46 - python django 小程序图书借阅源码
开发工具: PyCharm,mysql5.7,微信开发者工具 技术说明: python django html 小程序 功能介绍: 用户端: 登录注册(含授权登录) 首页显示搜索图书,轮播图࿰…...
2024/4/28 8:32:05 - 电子学会C/C++编程等级考试2022年03月(一级)真题解析
C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…...
2024/4/27 20:28:35 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...
2022/11/19 21:17:16 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在iPhone上关闭“请勿打扰”
Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...
2022/11/19 21:16:57