建筑网站上海网站建设吸引客户的

张小明 2026/1/2 16:31:10
建筑网站上海,网站建设吸引客户的,原创婚纱摄影,株洲seo优化官网企业级 应用优雅上线、下线方案 一#xff1a;应⽤上下线过程中 的 流量有损 问题 据统计#xff0c;应⽤的事故大多发⽣在应⽤上下线过程中#xff0c;有时是应⽤本身代码问题导致。 但有时我们也会发现尽管代码本身没有问题#xff0c;但在应⽤上下线发布过程中仍然会出现…企业级 应用优雅上线、下线方案一应⽤上下线过程中 的 流量有损 问题据统计应⽤的事故大多发⽣在应⽤上下线过程中有时是应⽤本身代码问题导致。但有时我们也会发现尽管代码本身没有问题但在应⽤上下线发布过程中仍然会出现短时间的服务调⽤报错⽐如调⽤时出现 Connection refused 和 No instance 等现象。常见的流量有损问题 出现的原因包括但不限于以下⼏种初始化慢应⽤刚启动接收线上流量进⾏资源初始化加载由于流量太⼤初始化过程慢出现⼤量请求响应超时、阻塞、资源耗尽从⽽造成刚启动应⽤宕机。注册太早服务存在异步资源加载问题当服务还未初始化完全就被注册到注册中⼼导致调⽤时资源未加载完毕出现请求响应慢、调⽤超时报错等现象。流量切换策略不合理 在应用上线时如果采用的是直接全量切换流量的方式可能会导致新上线的实例瞬间承受较大的流量压力进而出现性能问题影响流量的正常处理。服务下线慢 服务停止了但是 注册中⼼ meta data 元数据还在服务列表存在延时导致 下线后在⼀段时间内服务消费者还在 调⽤已下线实例造成请求报错。客户端 本地缓存问题 负载均衡器如 Ribbon、Spring Cloud LoadBalancer 等采用的是基于本地缓存的服务实例列表进行流量分发。如果负载均衡器的本地缓存更新不及时它可能仍然会将流量分发到已下线的实例上 造成请求报错。二微服务优雅上线 优雅下线的几个维度服务自身 维度注册中心 维度RPC Client 维度微服务网关 维度下面就以下几个维度 对于微服务优雅发布方案进行分析2.1 服务自身维度下线时能够执行资源释放日志记录数据持久化安全退出等干工作。处理没有完成的请求不再接收新的请求.池化资源的释放数据库连接池HTTP 连接池处理线程的释放请求处理线程的释放记录日志等。2.2 注册中心 维度使用服务注册与发现机制如Eureka、Nacos、Zookeeper等来管理服务的注册和发现。这样可以确保在上线或下线服务时其他服务能够及时感知到变化。2.3 RPC Client(负载均衡) 维度使用负载均衡机制如spring cloud loadbalanceRibbon等来分发流量到多个实例或节点上。通过负载均衡可以确保流量在服务上线或下线期间的平滑过渡避免单个实例或节点过载以及对已下线节点调用的错误问题发生2.4 网关维度内网网关spring cloud gateway就是使用的负载均衡组件因此在负载均衡组件剔除下线节点即可三微服务 自身维度 的 优雅上线微服务 自身维度 的 优雅上线 的主要流程自动应用之后 延迟注册微服务就绪之后 进行服务预热然后才 平稳运行。在Spring Cloud微服务架构中实现‌延迟注册‌和‌服务预热‌可通过以下方案优化服务启动流程3.1 延迟注册控制服务实例的注册时机对于初始化过程需要异步加载资源的复杂应⽤ 在启动过程由于注册通常与应⽤初始化过程同步进⾏从⽽出现应⽤还未完全初始化就已经被注册到注册中⼼供外部消费者调⽤此时直接调⽤由于资源未加载完成可能会导致请求报错。通过设置延迟注册可让应⽤在充分初始化后再注册到注册中⼼对外提供服务。‌核心场景‌服务启动后等待依赖组件如数据库、缓存初始化完成再注册避免接收未就绪的请求。‌配置自动延迟注册通用方案‌# application.yml spring:cloud:service-registry:auto-registration:enabled:false# 禁用自动注册 eureka:client:initial-instance-info-replication-interval-seconds:30#Eureka初次注册延迟秒通过监听应用启动事件‌ 手动触发注册‌ComponentpublicclassDelayedRegistrationimplementsApplicationListenerApplicationReadyEvent{AutowiredprivateServiceRegistryserviceRegistry;OverridepublicvoidonApplicationEvent(ApplicationReadyEventevent){// 确保依赖初始化完成后注册serviceRegistry.register();}}‌Nacos专属延迟注册‌spring:cloud:nacos:discovery:enabled:false# 关闭自动注册 ephemeral:true# 临时实例注册后自动维护心跳通过API手动注册PostConstructpublicvoidinit(){// 延迟5秒注册Executors.newSingleThreadScheduledExecutor().schedule(()-nacosDiscoveryProperties.registerInstance(),5,TimeUnit.SECONDS);}3.2 服务预热逐步接收流量**‌核心场景‌**实例启动后逐渐增加流量分配避免冷启动时高并发导致崩溃。在线上发布场景下很多时候刚启动的冷系统直接处理⼤量请求可能由于系统内部资源初始化不彻底从⽽出现⼤量请求超时、阻塞、报错甚⾄导致刚发布应⽤宕机等线上发布事故出现。服务自热在服务启动阶段主动触发服务中各种功能的初始化提前建立连接、加载数据等使服务在正式接受流量前就处于热状态。比如可以遍历微服务的所有接口主动发送请求触发懒加载的代码逻辑提前加载相关资源。‌基于健康检查的预热‌配置健康端点在预热完成前返回OUT_OF_SERVICE状态ComponentpublicclassWarmupHealthIndicatorimplementsHealthIndicator{privatebooleanisWarmfalse;OverridepublicHealthhealth(){if(!isWarm){// 返回不可用状态注册中心不分配流量returnHealth.outOfService().build();}returnHealth.up().build();}PostConstructpublicvoidwarmup()throwsInterruptedException{// 模拟预热过程如加载缓存Thread.sleep(30000);isWarmtrue;}}动态权重调整LoadBalancer‌⼩流量预热⽅法通过在服务消费端根据各个服务提供者实例的启动时间计算权重结合负载均衡算法逐步 控制刚启动应⽤流量从小到大放行该开始是小流量 随启动时间逐渐递增慢慢递增到正常⽔平。这样⼀个流量小到大放行过程就是帮助刚启动运⾏进⾏预热。详细 QPS 随时间变化曲线如图 所示⼩流量预热⽅法 可以根据时间来实现 比如可以自定义负载均衡规则根据启动时间分配权重BeanpublicReactorLoadBalancerServiceInstanceweightedLoadBalancer(Environmentenvironment,LoadBalancerClientFactoryfactory){returnnewWeightedLoadBalancer(factory.getLazyProvider(name,ServiceInstanceListSupplier.class),name);}publicclassWeightedLoadBalancerextendsRoundRobinLoadBalancer{OverridepublicResponseServiceInstancechoose(Requestrequest){ListServiceInstanceinstancesgetInstances();// 计算实例启动时间新实例低权重instances.sort(Comparator.comparingLong(i-i.getMetadata().get(startupTime)));returnnewDefaultResponse(instances.get(0));}}网关层流量控制在SpringCloudGateway中按实例启动时间过滤请求BeanpublicRouteLocatorroutes(RouteLocatorBuilderbuilder){returnbuilder.routes().route(warmup_route,r-r.path(/service/**).filters(f-f.filter(newWarmupFilter())).uri(lb://target-service)).build();}publicclassWarmupFilterimplementsGatewayFilter{OverridepublicMonoVoidfilter(ServerWebExchangeexchange,GatewayFilterChainchain){ServiceInstanceinstancechooseInstance();LongstartupTimeinstance.getMetadata().get(startupTime);if(System.currentTimeMillis()-startupTime60000){// 启动未满1分钟则返回错误或降级exchange.getResponse().setStatusCode(HttpStatus.TOO_EARLY);returnexchange.getResponse().setComplete();}returnchain.filter(exchange);}}例如开源微服务治理框架 Dubbo 原⽣就提供延迟注册功能。参考Dubbo延迟注册开源 Dubbo 所实现的⼩流量服务预热模型计算如下公式所示模型中应用 QPS 对应的 f(x) 随调用时刻 x 线性变化x 表示调用时刻的时间startTime 是应用开始时间warmupTime 是用户配置的应用预热时长k 是常数一般表示各实例的默认权重。四微服务 自身维度的 优雅下线微服务自身 要实现 优雅下线 还要做 很多 工作不再接收新的请求处理没有完成的请求池化资源的释放数据库连接池HTTP 连接池处理线程的释放请求处理线程的释放日志记录数据持久化安全退出…等什么才是SpringCloud 优雅下线那么SpringCloud 优雅下线该如何实现呢要介绍清楚 SpringCloud 优雅下线实现机制必须首先从JVM的优雅退出的基础知识开始。4.1 SpringBoot应用如何优雅退出除了 微服务的无损下线作为 SpringBoot应用 还有 单体服务优雅停机的需求处理没有完成的请求注意不再接收新的请求池化资源的释放数据库连接池HTTP 连接池处理线程的释放已经被连接的HTTP请求这些前面介绍到 咱们就先用放在 JVM 处理钩子方法里边去了。SpringBoot应用的优雅停机实际上指的是内嵌WEB服务器的优雅停机。 随着版本升级目前Spring Boot 优雅停机机制 更加完善了。4.2 什么是Web 容器优雅停机行为Web 容器优雅停机行为指的是在关闭容器时停止 接收新的请求但是让正在处理的请求完成后再关闭容器。这样可以避免正在处理的请求被中断从而提高系统的可用性和稳定性。一般来说Web 容器的优雅停机行为需要满足以下几个条件(1) 等待正在处理的请求完成不再接受新的请求。(2) 如果等待时间超过了一定阈值容器可以强制关闭。(3) 在容器关闭之前需要给客户端一个响应告知他们当前正在关闭容器不再接受新的请求4.3 优雅停机具体行为在服务器执行关闭Kill pid时会预留一点时间使容器内部业务线程执行完毕增加了优雅停机配置后, 此时容器 也不允许新的请求进入。目前版本的Spring Boot 优雅停机支持Jetty, Reactor Netty, Tomcat和 Undertow 以及反应式和基于 Servlet 的 web 应用程序都支持优雅停机功能。新请求的处理方式跟web服务器有关Reactor Netty、 Tomcat将停止接入请求Undertow的处理方式是返回503。具体行为如下表所示不同的 Web 容器实现优雅停机的方式可能会有所不同但是一般都会提供相关的配置选项或者 API 接口来实现这个功能。另外和SpringBoot内嵌的WEB服务器类似其他的非SpringBoot内嵌WEB服务器也可以进行设置。下面是 Nginx 和 Apache 的优雅停机配置Nginx 可以通过配置文件中的 worker_shutdown_timeout 选项来设置等待时间Apache 可以通过 graceful-stop 命令来实现优雅停机。4.4 优雅停机的使用和配置Tomcat 新版本配置非常简单server.shutdowngraceful 就搞定了注意优雅停机配置需要配合Tomcat 9.0.33含以上版本server:port:6080shutdown:graceful #开启优雅停机 spring:lifecycle:timeout-per-shutdown-phase:20s #设置缓冲时间 默认30s在设置了缓冲参数timeout-per-shutdown-phase 后在规定时间内如果线程无法执行完毕则会被强制停机。下面我们来看下停机时加了优雅停日志和不加的区别//未加优雅停机配置Disconnectedfrom the target VM,address:127.0.0.1:49754,transport:socketProcessfinishedwithexitcode130(interrupted by signal2:SIGINT)加了优雅停机配置后日志可明显发现的 Waiting for active requests to complete, 此时容器将在ShutdownHook执行完毕后停止。4.5 SpringBoot应用的优雅停机如何触发通过源码分析大家也发现了SpringBoot应用的优雅停机是注册了 JVM 优雅退出的钩子方法JVM 优雅退出的钩子方法如何触发的呢常见的触发方式有方式一kill PID方式二shutdown端点方式一kill PID使用方式kill java进程IDkill命令的格式是 kill -Signal pid其中 pid 就是进程的编号signal是发送给进程的信号默认参数下kill 发送 SIGTERM15信号给进程告诉进程你需要被关闭请自行停止运行并退出。kill、kill -9、kill -3的区别kill 会默认传15代表的信号为SIGTERM这是告诉进程你需要被关闭请自行停止运行并退出进程可以清理缓存自行结束也可以拒绝结束。kill -9 代表的信号是SIGKILL表示进程被终止需要立即退出强制杀死该进程这个信号不能被捕获也不能被忽略。kill -3可以打印进程各个线程的堆栈信息kill -3 pid 后文件的保存路径为/proc/${pid}/cwd文件名为antBuilderOutput.log方式二shutdown端点Spring Boot 提供了/shutdown端点可以借助它实现优雅停机。使用方式在想下线应用的application.yml中添加如下配置从而启用并暴露/shutdown端点management:endpoint:shutdown:enabled:trueendpoints:web:exposure:include:shutdown发送 POST 请求到/shutdown端点curl-Xhttp://ip:port/actuator/shutdown该方式本质和方式一是一样的也是借助 Spring Boot 应用的 Shutdown hook 去实现的。shutdown端点的源码分析actuator 都使用了SPI的扩展方式先看下AutoConfiguration可以看到关键点就是ShutdownEndpointConfiguration(proxyBeanMethodsfalse)ConditionalOnAvailableEndpoint(endpointShutdownEndpoint.class)publicclassShutdownEndpointAutoConfiguration{publicShutdownEndpointAutoConfiguration(){}Bean(destroyMethod)ConditionalOnMissingBeanpublicShutdownEndpointshutdownEndpoint(){returnnewShutdownEndpoint();}}ShutdownEndpoint,的核心代码如下Endpoint(idshutdown,enableByDefaultfalse)publicclassShutdownEndpointimplementsApplicationContextAware{WriteOperationpublicMapString,Stringshutdown(){if(this.contextnull){returnNO_CONTEXT_MESSAGE;}else{booleanvar6false;Mapvar1;try{var6true;var1SHUTDOWN_MESSAGE;var6false;}finally{if(var6){ThreadthreadnewThread(this::performShutdown);thread.setContextClassLoader(this.getClass().getClassLoader());thread.start();}}ThreadthreadnewThread(this::performShutdown);thread.setContextClassLoader(this.getClass().getClassLoader());thread.start();returnvar1;}}privatevoidperformShutdown(){try{Thread.sleep(500L);}catch(InterruptedExceptionvar2){Thread.currentThread().interrupt();}this.context.close();//这里才是核心}}在调用了 this.context.close() 其实就是AbstractApplicationContext 的close() 方法 重点是其中的doClose()Overridepublicvoidclose(){synchronized(this.startupShutdownMonitor){doClose();//重点销毁bean 并执行jvm shutdown hook// If we registered a JVM shutdown hook, we dont need it anymore now:// Weve already explicitly closed the context.if(this.shutdownHook!null){try{Runtime.getRuntime().removeShutdownHook(this.shutdownHook);}catch(IllegalStateExceptionex){// ignore - VM is already shutting down}}}}doClose() 方法又回到了前面的 Spring 的核心关闭方法。doClose()在销毁 bean, 关闭容器之前会执行所有实现 Lifecycel 接口 bean 的 stop方法并且会按 Phase 值分组, phase 大的优先执行。SpringBoot应用 就是 封装了 JVM 退出 钩子 实现了 SpringBoot 自己的 退出 钩子 。4.6 JVM的优雅退出JVM的优雅退出机制主要是通过 Hook实现的。jvm 有shutdwonHook机制中文习惯叫优雅退出。VM的优雅退出Hook和linux系统中执行SIGTERM(kill -15 或者 svc -d)时一样会在退出前执行的一些操作比如资源释放。JVM 退出的钩子函数 的应用场景首先看看JVM 退出的钩子函数 的应用场景。我们的java程序运行在JVM上有很多情况可能会突然崩溃掉比如OOM用户强制退出业务其他报错等一系列的问题可能导致我们的JVM 进程挂掉。如果没有优雅停机 此时直接直接关闭(kill -9)那么就会导致当前正在容器内运行的业务直接失败在某些特殊的场景下产生脏数据。JVM 退出的钩子函数是指在 JVM 进程即将退出时自动执行用户指定的代码段。这个功能的应用场景比较广泛例如(1) 资源释放在 JVM 退出时需要释放一些资源比如关闭数据库连接、释放文件句柄等可以使用钩子函数来自动执行这些操作。(2) 日志记录在 JVM 退出时可以记录一些关键信息比如程序运行时间、内存使用情况等以便后续分析问题。(3) 数据持久化在 JVM 退出时可以将一些重要的数据持久化到磁盘上以便下次启动时可以恢复状态。(4) 安全退出在 JVM 退出时可以执行一些清理操作比如删除临时文件、关闭网络连接等以确保程序的安全退出。总之钩子函数可以在 JVM 退出时执行一些自定义的操作以便更好地管理和控制程序的运行。JVM 退出 钩子 的使用在java程序中可以通过添加 退出 钩子 特定接口的函数实现在程序退出时关闭资源、优雅退出的功能。如何做呢退出 钩子 是通过Runtime.addShutDownHook(Thread hook)来 添加。Runtime.addShutdownHook(Thread hook) 是 Java 中的一个方法用于在 JVM 关闭时 注册一个线程来执行清理操作。Runtime.addShutdownHook(Thread hook) 每一次调用就是注册一个线程 这个线程负责 执行 退出 钩子 清理逻辑参考代码如下// 添加hook thread重写其run方法Runtime.getRuntime().addShutdownHook(newThread(){Overridepublicvoidrun(){System.out.println(this is hook demo...);// jvm 退出的钩子逻辑}});Runtime.addShutdownHook(Thread hook) 可以调用多次从而注册多个线程。当 JVM 即将关闭时会按照注册的顺序依次执行这些线程以便进行一些资源释放、日志记录或其他清理操作。这个方法可以在应用程序中用来确保在程序退出前执行一些必要的清理工作例如关闭数据库连接或释放文件句柄等。JVM 退出 钩子 的 触发场景JVM 退出 钩子 在什么情况下会被调用呢上述我们展示了在程序异常情况下会被调用还有没有其他场景呢程序正常退出使用System.exit()终端使用CtrlC触发的中断系统关闭OutOfMemory宕机使用Kill pid杀死进程 注意大家常常喜欢使用kill -9这个是不会被调用的 JVM 退出 钩子 的…等等五Nacos 注册中心 微服务 优雅上线、优雅下线在分布式微服务场景下 优雅发布 需通过 ‌延迟暴露实例‌上线和 ‌主动注销停机缓冲‌下线实现流量无损切换结合参数调优加速状态感知并通过灰度验证确保流程可靠性。从两个常见的注册中心Nacos和Eureka分析5.1 Nacos 优雅发布 的 要求首先看看以 Nacos 做 注册中心 怎么做优雅发布 对于优雅发布要求是 Service Provider 上线注册到 Nacos后服务能够正常地接收和处理请求而 Service Provider 停服后则不会再收到请求。优雅发布有两个要求优雅上线和优雅下线优雅上线Service Provider 发布完成 之前Service Consumer 不应该从服务列表中拉取到这个服务地址优雅下线Service Provider 下线 之后Service Consumer 不会从服务列表中拉取到这个服务地址。解决了这两个问题优雅发布就可以做到了。5.2 Nacos Server 服务注册 与发现机制 的底层原理跟其他的注册中心一样Nacos 作为注册中心的使用Service Provider 启动后注册到 Nacos ServerService Consumer 则从 Nacos Server 拉取服务列表根据一定算法选择一个 Service Provider 来发送请求。Service Consumer 初始化时会从 Nacos Server 获取服务列表并更新本地缓存。后面 Service Consumer 之后会定时默认间隔 1s 拉取服务列表并更新本地缓存。Service Consumer 会向 Nacos Server 订阅服务列表如果 Nacos Server 上的服务列表发生变化会主动通知 Service Consumer。代码如下//NacosNamingService 类publicListInstanceselectInstances(StringserviceName,StringgroupName,ListStringclusters,booleanhealthy,booleansubscribe)throwsNacosException{ServiceInfoserviceInfo;StringclusterStringStringUtils.join(clusters,,);if(subscribe){serviceInfoserviceInfoHolder.getServiceInfo(serviceName,groupName,clusterString);if(nullserviceInfo){serviceInfoclientProxy.subscribe(serviceName,groupName,clusterString);}}else{serviceInfoclientProxy.queryInstancesOfService(serviceName,groupName,clusterString,0,false);}returnselectInstances(serviceInfo,healthy);}在订阅的代码中加入了定时更新服务列表的代码如下//NamingClientProxyDelegate 类publicServiceInfosubscribe(StringserviceName,StringgroupName,Stringclusters)throwsNacosException{NAMING_LOGGER.info([SUBSCRIBE-SERVICE] service:{}, group:{}, clusters:{} ,serviceName,groupName,clusters);StringserviceNameWithGroupNamingUtils.getGroupedName(serviceName,groupName);StringserviceKeyServiceInfo.getKey(serviceNameWithGroup,clusters);serviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName,groupName,clusters);ServiceInforesultserviceInfoHolder.getServiceInfoMap().get(serviceKey);if(nullresult||!isSubscribed(serviceName,groupName,clusters)){resultgrpcClientProxy.subscribe(serviceName,groupName,clusters);}serviceInfoHolder.processServiceInfo(result);returnresult;}Nacos Server 会定时每隔 5s检查 Service Provider 是否健康根据心跳来检查如果 15s 默认可以配置没有收到心跳则会把服务置为不健康并且通知 Service Consumer。代码如下//UnhealthyInstanceChecker 类publicvoiddoCheck(Clientclient,Serviceservice,HealthCheckInstancePublishInfoinstance){if(instance.isHealthy()isUnhealthy(service,instance)){changeHealthyStatus(client,service,instance);}}privatevoidchangeHealthyStatus(Clientclient,Serviceservice,HealthCheckInstancePublishInfoinstance){instance.setHealthy(false);NotifyCenter.publishEvent(newServiceEvent.ServiceChangedEvent(service));NotifyCenter.publishEvent(newClientEvent.ClientChangedEvent(client));NotifyCenter.publishEvent(newHealthStateChangeTraceEvent(System.currentTimeMillis(),service.getNamespace(),service.getGroup(),service.getName(),instance.getIp(),instance.getPort(),false,client_beat));}5.3 Nacos场景下 服务 优雅上线优雅发布如果没有优雅上线那么存在的问题 主要在于 Service Provider 注册到 Nacos 后服务还没有完成初始化请求已经到来。 主要原因是 Service Provider 启动后立刻注册 Naocs而本身提供的接口可能还没有初始化完成。Nacos场景下 服务 的 优雅上线基本上有两种实现方案优雅上线 方案1延迟注册机制‌服务实例启动时先完成本地预热如 JVM 预热、缓存加载待关键资源初始化完成后再向 Nacos 注册服务。例如在 Spring Boot 应用中结合 PostConstruct 或 ApplicationRunner 接口延迟注册优雅上线 方案 2健康检查联动‌利用 Nacos 主动健康检查机制如 HTTP 探针在服务实例内部接口 /actuator/health 返回健康状态后再允许外部流量接入。通过配置 nacos.health-check-url 确保服务就绪后才被标记为可用节点。优雅上线 方案1延迟注册机制在application.yml中关闭自动注册手动控制注册时机spring:cloud:nacos:discovery:auto-register:false# 禁用自动注册使用ApplicationRunner在服务预热完成后触发注册ComponentpublicclassDelayedRegisterimplementsApplicationRunner{AutowiredprivateNacosDiscoveryPropertiesdiscoveryProperties;Overridepublicvoidrun(ApplicationArgumentsargs){// 模拟预热过程warmUpCache();//这个要自己实现initJVM();//这个要自己实现// 手动注册discoveryProperties.registerInstance();}}此方案确保服务完全就绪后才对外暴露 。优雅上线 方案 2健康检查联动在Nacos中配置Spring Boot Actuator的健康检查路径nacos:discovery:health-check-url:${server.servlet.context-path}/actuator/health health-check-enabled:true自定义健康指标确保关键组件初始化完成ComponentpublicclassCustomHealthIndicatorimplementsHealthIndicator{OverridepublicHealthhealth(){returncacheReady()dbConnected()?Health.up().build():Health.down().build();}// cacheReady() dbConnected() 这个要自己实现}Nacos会持续检查该接口仅当返回UP状态时才允许流量接入 。方案对比5.4 Nacos场景下优雅下线优雅停止对于正常下线Nacos Server 收到 Provider 发送的下线请求后会通知订阅的 Server Consumer而且 Consumer 也会每隔 1s 去更新本地服务列表这个过程已经非常接近优雅下线了。而对于异常下线Nacos Server 采用心跳检测机制来更新服务列表。心跳周期是 5sNacos Server 如果 15s 没收到心跳就才会将实例设置为不健康。正常停服正常下线的情况下最优雅的方式是先向 Nacos Server 发送下线通知发送通知一段时间比如 5s后再停服。比如增加一个 API 接口服务下线之前增加 preStopHook 函数调用这个 API 接口来实现下线。API 接口示例代码如下GetMapping(value/nacos/deregisterInstance)publicStringderegisterInstance(){PropertiespropnewProperties();prop.setProperty(serverAddr,localhost);prop.put(PropertyKeyConst.NAMESPACE,test);NacosNamingServiceclientnewNacosNamingService(prop);client.deregisterInstance(springboot-provider,192.168.31.94,8083);returnsuccess;}在使用 Ribbon 的场景也需要考虑 Ribbon 更新本地缓存服务列表的机制手动下线后可以再等待 30s 后关闭服务。服务故障对于服务故障的情况Nacos Server 需要采用心跳来检测服务在线如果 15s 没收到心跳才会将实例设置为不健康在 30s 没收到心跳才会把这个服务从列表中删除。这个时间可以做优化设置spring.cloud.nacos.discovery.metadata.preserved.heart.beat.interval1000#心跳间隔5s-1s spring.cloud.nacos.discovery.metadata.preserved.heart.beat.timeout3000#超时时间15s-3s spring.cloud.nacos.discovery.metadata.preserved.ip.delete.timeout5000#删除时间30s-5s但是Service Provider 故障情况下即使做优化配置也是很难让 Service Consumer 无感知。极端情况下可能 Provider 部分服务已经不能正常提供了但还是会向 Nacos Server 发送心跳这种情况可以采用服务本身的健康检查来通知 Nacos Server 服务下线。六Eureka注册中心 微服务 优雅上线、优雅下线Eureka优雅上线‌优雅上线需通过 ‌延迟暴露实例‌避免未就绪接收流量和 ‌参数调优‌加速客户端感知实现确保服务初始化期间对外屏蔽就绪后快速生效延迟注册与预热机制‌服务启动后优先完成 JVM 预热、线程池初始化等核心操作‌延迟向 Eureka 注册实例‌避免流量过早进入未就绪的实例。可通过 SmartLifecycle 或 ApplicationRunner 接口手动触发注册ComponentpublicclassDelayRegistrationimplementsSmartLifecycle{AutowiredprivateEurekaClienteurekaClient;privatebooleanrunningfalse;Overridepublicvoidstart(){// 模拟预热 10 秒Thread.sleep(10000);eurekaClient.register();// 手动触发注册this.runningtrue;}}健康检查联动配置 Spring Boot Actuator 健康检查端点 /actuator/health仅当服务初始化完成时返回 UP 状态。确保 Eureka 健康检查机制正确标记实例状态避免注册后因未就绪被标记为不可用。Eureka 优雅下线但如果的务发现组件使用的是 Eureka那么默认最长会有 90 秒的延迟其他应用才会感知到该服务下线这意味着该实例下线后的 90 秒内其他服务仍然可能调用到这个已下线的实例。Spring Boot 应用 退出的时候如何在 Eureka 进行实例的主动删除呢可以借助 Spring Boot 应用的 Shutdown hook结合 Eureka 的Client API达到微服务实例优雅下线的目标。Eureka 的两个核心的 Client API 如下执行eurekaAutoServiceRegistration.start()方法时当前服务向 Eureka 注册中心注册服务执行eurekaAutoServiceRegistration.stop()方法时当前服务会向 Eureka 注册中心进行反注册注册中心收到请求后会将此服务从注册列表中删除。参数调优加速上下线感知1、缩短客户端心跳间隔‌调整 Eureka 客户端向服务端发送心跳的频率加速实例健康状态同步eureka:instance:lease-renewal-interval-in-seconds:5# 心跳间隔默认30秒2、优化服务端缓存刷新‌降低 Eureka Server 响应缓存的刷新周期缩短服务列表同步延迟eureka:server:response-cache-update-interval-ms:3000# 服务端缓存刷新间隔默认30秒3、禁用冗余缓存层‌关闭 Ribbon 和 Eureka 的本地缓存减少客户端感知新实例的延迟ribbon:ServerListRefreshInterval:5000#Ribbon缓存刷新间隔默认30秒 eureka:server:use-read-only-response-cache:false# 禁用只读缓存4、缩短剔除失效实例的周期‌调整 Eureka Server 检查失效实例的频率默认 60 秒调整为 10 秒加快下线实例的清理速度eureka:server:eviction-interval-timer-in-ms:10000# 失效实例检查周期默认60秒5、禁用冗余缓存层‌关闭 Eureka Server 的只读缓存减少服务列表同步延迟 eureka:server:use-read-only-response-cache:false缩短 Ribbon 本地缓存刷新周期至 5 秒加速客户端感知实例变化ribbon:ServerListRefreshInterval:5000# 默认30秒七RPC Client(负载均衡) 维度的优雅上线 优雅下线 的方案设计7.1 RPC Client 维度 优雅上线方案延迟注册机制这个与 前面介绍的 服务自身的 优雅上线 有关服务启动后先完成内部资源初始化如缓存预热、连接池建立再向注册中心注册实例。通过spring.cloud.service-registry.auto-registration.enabledfalse关闭自动注册在ApplicationReadyEvent事件后手动触发注册。流量渐进式接入负载均衡器如Spring Cloud LoadBalancer根据实例启动时间动态分配权重新实例初始权重设为10%每分钟递增20%直至满负荷。健康检查拦截启动阶段健康端点返回OUT_OF_SERVICE状态待预热完成后切换为UP避免网关过早路由请求。7.2 优雅下线方案主动注销双重缓存清理接收停机信号后立即调用DiscoveryClient.shutdown()向注册中心注销实例。接收nacos 服务实例的变化信号后 清理RPC Client 如Ribbon)的ServerList缓存通过ServerListUpdater强制刷新服务列表。流量摘除与请求排空负载均衡器标记下线实例为draining状态新请求不再路由至该实例。通过PreDestroy钩子等待现有请求完成最长等待时间可配置。八网关 维度 的 优雅上线、优雅下线在使用Spring Cloud Gateway作为网关Nacos2.x作为注册中心Spring Cloud Loadbalancer 作为负载均衡的时候如何实现 优雅下线关键是在服务下线后Gateway 网关及时动态感知服务上下线否则 服务下线后网关可能短暂报500的错误。问题原因Loadbalancer 有个缓存 CachingRouteLocator 会缓存服务信息这个缓存每35秒刷新一次。服务在上线或者下线之后未能及时刷新这个缓存 所以导致网关会短暂的访问到已经下线的服务。相应的源码如下publicclassCachingRouteLocatorimplementsOrdered,RouteLocator,ApplicationListenerRefreshRoutesEvent,ApplicationEventPublisherAware{privatestaticfinalLoglogLogFactory.getLog(CachingRouteLocator.class);privatestaticfinalStringCACHE_KEYroutes;privatefinalRouteLocatordelegate;privatefinalFluxRouteroutes;privatefinalMapString,ListcachenewConcurrentHashMap();privateApplicationEventPublisherapplicationEventPublisher;publicCachingRouteLocator(RouteLocatordelegate){this.delegatedelegate;routesCacheFlux.lookup(cache,CACHE_KEY,Route.class).onCacheMissResume(this::fetch);}privateFluxRoutefetch(){returnthis.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE);}OverridepublicFluxRoutegetRoutes(){returnthis.routes;}/*Clearsthe routes cache.*returnroutes flux/publicFluxRouterefresh(){this.cache.clear();returnthis.routes;}OverridepublicvoidonApplicationEvent(RefreshRoutesEventevent){try{fetch().collect(Collectors.toList()).subscribe(list-Flux.fromIterable(list).materialize().collect(Collectors.toList()).subscribe(signals-{applicationEventPublisher.publishEvent(newRefreshRoutesResultEvent(this));cache.put(CACHE_KEY,signals);},throwable-handleRefreshError(throwable)));}catch(Throwablee){handleRefreshError(e);}}privatevoidhandleRefreshError(Throwablethrowable){if(log.isErrorEnabled()){log.error(Refresh routes error !!!,throwable);}applicationEventPublisher.publishEvent(newRefreshRoutesResultEvent(this,throwable));}Deprecated/fortesting*/voidhandleRefresh(){refresh();}OverridepublicintgetOrder(){return0;}OverridepublicvoidsetApplicationEventPublisher(ApplicationEventPublisherapplicationEventPublisher){this.applicationEventPublisherapplicationEventPublisher;}}解决方案因为Nacos2.x使用长连接与服务相连所以可以实时感知到有服务下线。并且当某一个服务下线后Nacos会实时通知给其他服务并发布服务变更事件。所以 可以 通过监听Nacos发布的服务变更事件实时清除CacheManager缓存可以使得当需要相关数据时重新加载最新的数据从而 达到实时感知服务上下线的功能ComponentpublicclassMyListenerextendsSubscriberInstancesChangeEvent{ResourceprivateCacheManagerdefaultLoadBalancerCacheManager;PostConstructpublicvoidinit(){NotifyCenter.registerSubscriber(this);}OverridepublicvoidonEvent(InstancesChangeEventinstancesChangeEvent){CachecachingServiceInstanceListSupplierCachedefaultLoadBalancerCacheManager.getCache(CachingServiceInstanceListSupplier.SERVICE_INSTANCE_CACHE_NAME);if(Objects.nonNull(cachingServiceInstanceListSupplierCache)){cachingServiceInstanceListSupplierCache.evict(instancesChangeEvent.getServiceName());}}OverridepublicClass?extendsEventsubscribeType(){returnInstancesChangeEvent.class;}}这里通过继承的方式监听 Nacos 的 InstancesChangeEvent在 onEvent 接收到实例刷新的信息后直接删除对应服务的负载均衡缓存。缓存的名字是定义在 Spring Gateway 的配置文件中的直接引入即可Cache 则是继承自 Spring Cache 接口负载均衡缓存也继承了 Cache 接口有了 Cache 接口就可以直接使用其接口定义的 evict 方法即可而缓存的 key 名就则就是服务名在 InstancesChangeEvent 中通过 getServiceName 就可以得到服务名。CachingRouteLocator 和 CacheManager 的关系在Spring Cloud Gateway的架构中CachingRouteLocator和 CacheManager 分别承担不同的职责但通过事件机制 形成协同关系。功能职责上的关联CachingRouteLocator 主要负责 路由规则 的缓存管理。它的 getRoutes() 方法通过 CacheFlux 将路由信息进行缓存当有对路由的请求时会先从缓存中获取这样可以提高路由信息获取的效率减少频繁地去加载或查询原始路由数据源的开销。同时它还提供了 refresh() 方法来清除缓存以便重新加载最新的路由数据。defaultLoadBalancerCacheManager 是一个缓存管理器这里主要是对服务实例列表的缓存。在 Nacos 的服务治理场景下服务实例的上下线会影响负载均衡的选择所以当监听到服务实例变更事件时需要对这个缓存进行操作以保证后续负载均衡可以基于最新的服务实例信息进行请求分发。功能定位差异‌CachingRouteLocator负责路由规则缓存管理通过ConcurrentHashMap存储路由信息并响应RefreshRoutesEvent 事件更新缓存defaultLoadBalancerCacheManager管理负载均衡层的服务实例缓存通过Spring Cache抽象接口提供缓存操作能力‌数据流协同‌当Nacos服务实例变更时Nacos发布InstancesChangeEvent事件MyListener监听到事件后通过defaultLoadBalancerCacheManager清除对应服务的负载均衡缓存后续请求触发CachingRouteLocator重新从更新的服务实例列表加载路由这种设计实现了两级缓存解耦负载均衡层缓存服务实例路由层缓存路由规则通过事件驱动保证数据一致性九优雅上线 设计一个顶级、大厂的 流量预热方案结合 服务自热、延迟注册、Sentinel预热 、 Ribbon动态权重负载均衡 四大措施结合的 顶级、大厂、流量预热方案服务自热在服务启动阶段主动触发服务中各种功能的初始化提前建立连接、加载数据等使服务在正式接受流量前就处于热状态。比如可以遍历微服务的所有接口主动发送请求触发懒加载的代码逻辑提前加载相关资源。publicclassWarmupService{AutowiredprivateRestTemplaterestTemplate;PostConstructpublicvoidwarmup()throwsException{// 预热数据库连接池DataSourcedsSpringContext.getBean(DataSource.class);Connectionconnds.getConnection();conn.close();// 预热接口String[]apis{/api/v1/users,/api/v1/products};for(Stringapi:apis){restTemplate.getForObject(http://localhost:8080api,String.class);}}}延迟注册禁用服务启动时的自动注册功能通过在配置文件中设置spring.cloud.discovery.client.simple.auto-registrationfalse来实现。spring:cloud:nacos:discovery:auto-registration:enabled:false# 关闭自动注册 sentinel:transport:dashboard:localhost:8080eager:true# 立即初始化 ribbon:NFLoadBalancerRuleClassName:com.example.WeightedRule待服务完成内部资源初始化后手动向注册中心注册实例 实现延迟注册在Spring Boot应用中可监听ApplicationReadyEvent事件在事件回调中执行注册操作。publicclassDelayedRegistrationimplementsApplicationListenerApplicationReadyEvent{AutowiredprivateNacosServiceRegistryregistry;OverridepublicvoidonApplicationEvent(ApplicationReadyEventevent){// 等待30秒确保预热完成ScheduledExecutorServiceexecutorExecutors.newSingleThreadScheduledExecutor();executor.schedule(()-{registry.register();// 手动注册initSentinelRules();// 初始化限流规则},30,TimeUnit.SECONDS);}privatevoidinitSentinelRules(){FlowRulerulenewFlowRule();rule.setResource(protectedResource);rule.setGrade(RuleConstant.FLOW_GRADE_QPS);rule.setCount(1000);// 最终阈值rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);rule.setWarmUpPeriodSec(300);// 5分钟预热FlowRuleManager.loadRules(Collections.singletonList(rule));}}Sentinel预热使用Sentinel的流控规则采用WARM_UP预热模式在服务启动初期对流量进行限制并随着时间的推移逐渐增加允许通过的流量。在Sentinel控制台或通过Java API配置流控规则设置阈值类型为QPS控制行为为预热模式并设置预热时间。// Sentinel预热规则PostConstructpublicvoidinitFlowRules(){FlowRulerulenewFlowRule();rule.setResource(resName);rule.setGrade(RuleConstant.FLOW_GRADE_QPS);rule.setCount(1000);rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);rule.setWarmUpPeriodSec(300);// 5分钟预热期FlowRuleManager.loadRules(Collections.singletonList(rule));}通过冷启动算法实现QPS从阈值count/coldFactor逐步增长到设定值避免瞬间流量压垮系统rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);rule.setWarmUpPeriodSec(300);// 5分钟预热期该配置会使orderApi接口在10分钟内从333 QPS逐步提升到1000 QPS如果 要 匹配所有的接口请使用 通配符匹配 。FlowRulerulenewFlowRule(*);// 通配符匹配所有接口rule.setCount(100);// 设置最大QPSrule.setWarmUpPeriodSec(300);// 设置预热时间rule.setControlBehavior(FlowRule.CTRL_BEHAVIOR_WARM_UP);// 设置为预热模式FlowRuleManager.loadRules(Collections.singletonList(rule));Ribbon动态权重负载均衡Ribbon的负载均衡器采用动态权重算法根据服务实例的启动时间和预热时长计算权重。实例启动时间越长权重越高能分配到的流量也就越大。新启动的服务实例初始权重较低随着运行时间的增加权重逐渐提高最终达到正常水平。通过以上技术点的结合可以在服务上线时实现流量的平滑过渡有效减少因服务未完全准备就绪而导致的流量有损问题。publicclassWeightedRuleextendsZoneAvoidanceRule{privatestaticfinallongWARMUP_DURATION300000;// 5分钟OverridepublicServerchoose(Objectkey){ListServerserversgetLoadBalancer().getAllServers();servers.forEach(server-{NacosInstanceinstance(NacosInstance)server;longuptimeSystem.currentTimeMillis()-Long.parseLong(instance.getMetadata().get(startupTime));doubleweightcalculateWeight(uptime);instance.setWeight(weight);});returnsuper.choose(key);}privatedoublecalculateWeight(longuptime){if(uptimeWARMUP_DURATION)return1.0;return0.10.9*(uptime/(double)WARMUP_DURATION);}}
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

网站开发公司能不能去wordpress是用php语言的

第一章:Open-AutoGLM调用超时问题的根源剖析在使用 Open-AutoGLM 进行大规模语言模型调用时,开发者频繁遭遇请求超时问题。该问题不仅影响服务响应速度,还可能导致任务中断或系统资源浪费。深入分析其成因,有助于构建更稳定的调用…

张小明 2025/12/30 5:37:51 网站建设

网站制作教程 百度文库网站开发营销网站多少钱

当一个简单的 missing groups or modules: nodejs 错误出现在Koji构建日志时,背后隐藏的是一整套分布式构建系统、软件包管理生态与架构兼容性策略的复杂交互。作为Koji框架的资深维护者,我将带你深入源代码层面,揭示这一现象背后的完整技术链…

张小明 2025/12/30 11:45:21 网站建设

深圳做网站de哈尔滨商城网站建设

【算法详解】如何根据“扩展先序遍历”构建二叉树? 在二叉树的算法题中,我们常遇到的问题是:给定二叉树求遍历序列。但反过来,给定一个遍历序列(字符串),如何还原出一棵二叉树? 通常…

张小明 2025/12/28 10:11:23 网站建设

专业的公司网站设计服务建设银行网站不能打开

还在为工作间隙想阅读却担心影响工作专注度而烦恼吗?作为程序员,我们每天都要在IDEA中度过大量时间,有没有一种方法能够让我们在不离开开发环境的情况下享受阅读的乐趣?今天介绍的这款IDEA阅读插件,正是为解决这一痛点…

张小明 2025/12/30 16:13:14 网站建设

网站空间域名一次性收费还是一年一算怎样免费制作网页

计算机毕设java疫情背景下大学生宿舍管理系统2eq859 (配套有源码 程序 mysql数据库 论文) 本套源码可以在文本联xi,先看具体系统功能演示视频领取,可分享源码参考。随着疫情的持续影响,高校的日常管理面临着诸多挑战,尤…

张小明 2025/12/30 12:28:59 网站建设