当前位置: 首页 > news >正文

Java微服务无损发布生产案例

目录

1. 背景

2. 排查

3. 方案

3.1. 外部脚本控制(运维层面)

3.1.1. 优化前的sh脚本逻辑

3.1.2. 优化后的sh脚本逻辑

3.1.3. 测试结果

3.2. 应用内优雅停机(代码层面)

3.2.1. 优雅停机实现类

3.2.2. 测试结果

4. 验证

5. 上线

6. 参考资料


1. 背景

项目为Spring Cloud微服务,注册中心使用nacos。在上线发布过程中发现存在feign调用失败的情况,即发布过程没有做到无损。上线发布采用灰度发布策略,即先发布一台机器,这台机器成功发布后再接着发布其他机器,理论上应该无损。

2. 排查

在上线发布微服务a第xx台机器过程中,a应该处于下线状态,不应该再请求到这台机器,但是从日志看有流量进来并调用a失败了:

3. 方案

借助AI及经验排查项目代码和停机sh脚本后发现代码层面及运维脚本层面都没有实现优雅停机,基于尽量少改动代码考虑,决定采用修改停机sh脚本的方案实现优雅停机。

3.1. 外部脚本控制(运维层面)

这是本案例采用的优雅停机方案,代码无侵入,只需完善停机sh脚本逻辑以支持优雅停机。

3.1.1. 优化前的sh脚本逻辑

停止实例时脚本没有先下线nacos注册中心服务实例,存在被调用方服务B刚停止但nacos还未感知到,此时调用方A从nacos获取到这个已停止的服务B的实例,导致调用失败。

3.1.2. 优化后的sh脚本逻辑

采取主动标记nacos实例下线 + 休眠等待 + 停止java进程方案:先标记nacos实例下线(主动下线而不是被动等待nacos自动检测下线),再设置一个休眠时间一是为了等待nacos更新,二是让正在处理的业务请求有时间完成。最后再停止java进程,避免请求丢失,实现微服务优雅停机。

#!/bin/bash
# stop.sh - 优雅停机脚本APP_NAME="provider"
APP_PORT=8082
NACOS_SERVER="127.0.0.1:8848"echo "开始优雅停止 $APP_NAME..."# 1. 获取服务ip
get_nacos_registered_ip() {# 调用Nacos查询接口local response=$(curl -s "http://${NACOS_SERVER}/nacos/v1/ns/instance/list?serviceName=${APP_NAME}")if [ $? -ne 0 ] || [ -z "$response" ]; thenecho "查询Nacos服务失败" >&2return 1fi# 解析JSON获取IP(需要jq工具)if command -v jq >/dev/null 2>&1; thenlocal ip=$(echo "$response" | jq -r '.hosts[0].ip' 2>/dev/null)if [ "$ip" != "null" ] && [ -n "$ip" ]; thenecho $ipreturn 0fifi# 如果没有jq,使用grep/awk解析local ip=$(echo "$response" | grep -o '"ip":"[^"]*' | cut -d'"' -f4 | head -1)if [ -n "$ip" ]; thenecho $ipreturn 0fiecho "无法从Nacos响应中解析IP" >&2return 1
}LOCAL_IP=$(get_nacos_registered_ip)
echo "检测到服务注册IP: $LOCAL_IP"# 2. 从Nacos下线服务
echo "从Nacos注销服务..."
curl -X DELETE "${NACOS_SERVER}/nacos/v1/ns/instance?serviceName=${APP_NAME}&ip=${LOCAL_IP}&port=${APP_PORT}"# 3. 等待流量切换
echo "等待流量切换(15秒)..."
sleep 15# 4. 根据jar名称查找应用进程,比如provider2
echo "查找应用进程..."
# 更精确的进程查找方式
APP_PID=$(ps aux | grep "[j]ava.*provider2" | awk '{print $2}')if [ -n "$APP_PID" ]; thenecho "找到应用进程,PID: $APP_PID"# 5. 发送优雅停止信号echo "发送优雅停止信号..."kill -15 $APP_PID# 6. 等待进程停止,最多30秒for i in {1..30}; doif kill -0 $APP_PID 2>/dev/null; thenecho "等待应用停止...($i/30)"sleep 1elseecho "应用已优雅停止"exit 0fidone# 7. 强制停止echo "优雅停止超时,强制停止"kill -9 $APP_PID
elseecho "应用未运行"
fiecho "停止脚本执行完成"

另外,springboot项目配置文件application.yml配置优雅停机:

server:shutdown: graceful

3.1.3. 测试结果

脚本运行结果:

应用日志:

3.2. 应用内优雅停机(代码层面)

3.2.1. 优雅停机实现类

package com.example.provider;import com.alibaba.cloud.nacos.registry.NacosRegistration;
import com.alibaba.cloud.nacos.registry.NacosServiceRegistry;
import org.apache.catalina.connector.Connector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;@Component
public class TomcatNacosGracefulShutdown implements TomcatConnectorCustomizer,ApplicationListener<ContextClosedEvent> {private static final Logger logger = LoggerFactory.getLogger(TomcatNacosGracefulShutdown.class);private volatile Connector connector;@Lazy@Resourceprivate NacosServiceRegistry nacosServiceRegistry;@Lazy@Resourceprivate NacosRegistration nacosRegistration;@Overridepublic void customize(Connector connector) {this.connector = connector;logger.info("Tomcat连接器自定义配置完成");}@Overridepublic void onApplicationEvent(ContextClosedEvent event) {logger.info("=== 开始Tomcat+Nacos优雅停机 ===");startGracefulShutdown();}public void startGracefulShutdown() {try {// 步骤1: Nacos注销deregisterFromNacos();// 步骤2: 等待注册中心传播waitForRegistryPropagation();// 步骤3: 暂停Tomcat接收新请求pauseTomcat();// 步骤4: 等待活跃请求完成waitForActiveRequests();logger.info("=== Tomcat+Nacos优雅停机完成 ===");} catch (Exception e) {logger.error("优雅停机失败", e);}}private void deregisterFromNacos() {if (nacosServiceRegistry != null && nacosRegistration != null) {try {logger.info("从Nacos注销服务实例...");nacosServiceRegistry.deregister(nacosRegistration);logger.info("Nacos注销成功: {}", nacosRegistration.getServiceId());} catch (Exception e) {logger.error("Nacos注销失败", e);}} else {logger.warn("Nacos注册组件未找到,跳过注销");}}private void waitForRegistryPropagation() {try {long waitTime = 15000;logger.info("等待注册中心传播: {}ms", waitTime);Thread.sleep(waitTime);} catch (InterruptedException e) {Thread.currentThread().interrupt();logger.warn("等待过程被中断");}}private void pauseTomcat() {if (connector != null) {logger.info("暂停Tomcat连接器,停止接收新请求");connector.pause();} else {logger.warn("Tomcat连接器未找到,无法暂停");}}private void waitForActiveRequests() {if (connector == null) {logger.warn("Tomcat连接器未找到,跳过等待");return;}Executor executor = connector.getProtocolHandler().getExecutor();if (executor instanceof ThreadPoolExecutor) {ThreadPoolExecutor threadPool = (ThreadPoolExecutor) executor;logger.info("等待活跃请求完成...");threadPool.shutdown();try {long maxWaitTime = 30000;if (!threadPool.awaitTermination(maxWaitTime, TimeUnit.MILLISECONDS)) {logger.warn("等待请求超时,强制关闭线程池");threadPool.shutdownNow();if (!threadPool.awaitTermination(5000, TimeUnit.MILLISECONDS)) {logger.error("线程池未能正常终止");}} else {logger.info("所有活跃请求处理完成");}} catch (InterruptedException e) {threadPool.shutdownNow();Thread.currentThread().interrupt();logger.warn("等待过程被中断");}} else {logger.warn("无法获取Tomcat线程池,跳过等待");}}}

3.2.2. 测试结果

4. 验证

在测试环境用jemeter做接口测试,观察在执行停机脚本发布期间有无失败用例。

5. 上线

测试环境验证没问题后上线,生产发布时通过可观测平台查看发布服务的请求列表,发现错误数为0,成功率100%,且发布过程中没有日志告警,说明问题已修复。

6. 参考资料

nacos官方open api

nacos官方FAQ

http://www.dtcms.com/a/523818.html

相关文章:

  • Kivy 乒乓游戏教程 基于Minconda或Anconda 运行
  • 摄影的网站设计特点同城发广告的平台有哪些
  • 【Python高级编程】类和实例化
  • 徐州市建设局交易网站网站设计的公司运营接单
  • 虹科亮相2025嵌入式会议 | 解读CAN XL与TSN如何驱动下一代E/E架构创新
  • VxWorks系统下龙芯平台的PCI驱动与硬件配置
  • 【2026计算机毕业设计】基于Django的新闻资讯平台的设计与实现
  • Linux小课堂: 基于 SSH 的安全文件传输与增量同步机制深度解析之从 wget 到 rsync 的全流程实战
  • 使用ffmpeg裁剪视频
  • 凡科建站网站西安全网推广公司
  • 免费网站建设程序下载建站用什么工具
  • 香港科技大学工学院2026/2027年度研究生课程招生宣讲会-重庆大学专场
  • Qualcomm SNPE(Neural Processing SDK)集成到 OpenWRT + QCS6490 的完整配置指南
  • LangGraph 官方教程:聊天机器人之一
  • Git与Gitee使用中的几个问题
  • 关于淘宝店网站建设的可行性报告网站建设的公司
  • Flink DatastreamAPI详解(四)
  • 线性代数直觉(四):找到特征向量
  • iis网站服务器 建立出现问题微信小程序制作费用是多少
  • 亚马逊云代理商:2025 AWS 服务器配置趋势走向怎么样?
  • 建设银行网站修改手机号湖南省和城乡住房建设厅网站
  • 云电脑与云手机的关系
  • 加性高斯白噪声和码间串扰的信道中Ungerboeck和Forney接收机的区别
  • 厨房电子秤芯片方案:SIC8833
  • 2025MathorCup大数据竞赛A题B题选题建议与分析,思路模型
  • 做网站的公司属于什么行业工商银行手机银行app下载
  • FastGateway 容器化部署与安全集成实践:技术架构与生态融合
  • 流媒体网站建设规划 所需设备网站建设方案500字
  • 非视距城市合成孔径雷达中的多径利用——论文阅读
  • 蓝牙 nRF52732 最简操作