Springboot + nacos + k8s 优雅停机

需要处理的问题

  1. 至少有一个服务可用
    K8S 配置滚动部署策略
  2. 服务下线后不再被调度
    • 服务关闭时主动下线 nacos
    • 服务关闭时清理应用里的 loadbalance 实例列表缓存
  3. 之前进来的请求可以返回
    延迟下线,最大可能保证功能结束

业务服务配置 nacos 优雅停机

优雅停机配置与最大等待时间

server:
  shutdown: graceful

spring:
  lifecycle:
    timeout-per-shutdown-phase: 40s
  cloud:
    loadbalancer:
      # 负载均衡缓存配置
      cache:
        enabled: true 
        ttl: 10s   

监听器代码

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.core.PriorityOrdered;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;

@Component
@Slf4j
public class NacosShutdownEvent implements ApplicationListener<ContextClosedEvent>, PriorityOrdered {

    @Resource
    private NacosServiceManager nacosServiceManager;

    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;

    @SneakyThrows
    @Override
    public void onApplicationEvent(ContextClosedEvent event) {
        // 获取服务实例
        NamingService namingService = nacosServiceManager.getNamingService();
        String service = nacosDiscoveryProperties.getService();
        List<Instance> instances = namingService.getAllInstances(service);
        String ip = nacosDiscoveryProperties.getIp();
        log.info("nacosDiscoveryPropertiesIP:{}", ip);
        for (Instance instance : instances) {
            String instanceIp = instance.getIp();
            log.info("instanceIp,{}", instanceIp);
            if (instanceIp.equals(nacosDiscoveryProperties.getIp())) {
                namingService.deregisterInstance(service, instance);
                log.info("deregisterInstance service,{},{}", service, ip);
            }
        }
        Thread.sleep(25 * 1000);
        log.info("end stop nacos");
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

网关

在网关配置过期时间

spring:
  cloud:
    loadbalancer:
      # 负载均衡缓存配置
      cache:
        enabled: true 
        ttl: 10s   

测试

使用jemeter 持续请求,观察在部署期间请求不报错
在这里插入图片描述

K8S

terminationGracePeriodSeconds:60s 这里的时间要大于springboot 里的睡眠时间

k8s 有停机前的处理步骤,可以在那里配置下线 + nacos 与睡眠等待,我采用的是springboot 监听器的方案,便于开发与观察。

其它

同样的方法,编写其他中间件,比如 kafka、xxl 的优雅停机逻辑,并且调整顺序,越小优先执行。

可以在关闭前打印当前活跃的 tomcat 容器中的线程数与队列,用于评估等待时间,待完善。

版本:
springboot 2.6.13
nacos 2.2.0

### 集成概述 在构建微服务架构时,Spring Boot与Dubbo和Nacos的集成能够提供强大的服务治理能力。通过这种组合,不仅可以简化开发流程,还能提高系统的可维护性和灵活性[^1]。 ### 使用Nacos作为注册中心和服务发现工具 Nacos作为一个动态服务发现、配置管理和服务中心,非常适合用于微服务体系中的服务注册与发现工作。其主要特点在于支持多种协议的服务发现方式(如DNS和RPC),并且具备良好的跨语言兼容性。 对于希望利用Nacos来完成这一任务的应用程序来说,在pom.xml文件中加入相应的依赖项是必不可少的第一步: ```xml <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-dubbo-nacos-discovery</artifactId> </dependency> ``` 接着需要修改`application.properties`或`yaml`配置文件以指定Nacos服务器的位置以及其他必要的参数设置: ```properties spring.application.name=demo-service-provider spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 dubbo.protocol.name=dubbo dubbo.protocol.port=-1 dubbo.registry.address=nacos://127.0.0.1:8848 ``` 上述配置指定了应用程序名称、Nacos服务器地址以及Dubbo的相关属性。其中特别需要注意的是`dubbo.protocol.port`被设为-1表示由框架自动分配端口号;而`dubbo.registry.address`则表明使用Nacos作为默认注册中心。 ### 利用Dubbo实现远程过程调用(RPC) 为了让两个不同节点间可以通过网络互相通信并执行对方的方法调用,还需要引入另一个重要的组件——Dubbo。该框架提供了高效可靠的RPC机制,允许开发者轻松定义接口并通过简单的注解形式暴露给其他服务消费[^2]。 假设有一个名为`DemoService`的服务接口及其具体实现类,则可以在Provider端按照如下方式进行编码: #### Provider端代码示例 创建一个普通的Java接口作为对外提供的API入口: ```java public interface DemoService { String sayHello(String name); } ``` 随后编写其实现逻辑,并加上适当标注使其成为可供发布的服务实例之一: ```java @Service(version = "1.0.0") // 这里的@Service来自org.apache.dubbo.config.annotation.Service public class DemoServiceImpl implements DemoService { @Override public String sayHello(String name) { return "Hello, " + name; } } ``` 最后一步是在Consumer端声明对该服务的需求即可发起请求获取响应数据: #### Consumer端代码示例 同样地先定义好所需访问的目标接口签名: ```java @Reference(version = "1.0.0", check=false) private DemoService demoService; // 调用方法处... System.out.println(demoService.sayHello("world")); ``` 这里使用的`@Reference`注解来自于`org.apache.dubbo.config.annotation.Reference`包下,用来指示当前成员变量所代表的就是远端某个已发布出来的对象引用。 ### 结合Seata处理分布式事务 当涉及到多个数据库操作或者其他资源协调场景时,确保整个业务流程的一致性就显得尤为重要了。此时可以考虑借助于像Seata这样的分布式事务解决方案来进行全局控制[^3]。 不过这部分内容相对独立一些,因此如果读者感兴趣的话建议单独查阅相关资料深入了解具体的实施细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值